@promakeai/cli 0.0.6 → 0.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.
Files changed (114) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/registry/about-page.json +4 -2
  3. package/dist/registry/about-section.json +2 -2
  4. package/dist/registry/announcement-bar.json +43 -0
  5. package/dist/registry/auth-core.json +43 -0
  6. package/dist/registry/auth.json +1 -1
  7. package/dist/registry/blog-list-page.json +3 -2
  8. package/dist/registry/blog-section.json +1 -1
  9. package/dist/registry/cart-page.json +3 -3
  10. package/dist/registry/case-study-page.json +48 -0
  11. package/dist/registry/checkout-page.json +6 -5
  12. package/dist/registry/coming-soon-page-minimal.json +45 -0
  13. package/dist/registry/coming-soon-page.json +47 -0
  14. package/dist/registry/contact-info-grid.json +1 -1
  15. package/dist/registry/contact-page-centered.json +2 -2
  16. package/dist/registry/contact-page-map-overlay.json +4 -3
  17. package/dist/registry/contact-page-map-split.json +4 -3
  18. package/dist/registry/contact-page-split.json +3 -3
  19. package/dist/registry/contact-page.json +5 -3
  20. package/dist/registry/cookie-consent.json +43 -0
  21. package/dist/registry/cookies-page.json +3 -1
  22. package/dist/registry/cta-section.json +1 -1
  23. package/dist/registry/db.json +1 -1
  24. package/dist/registry/docs/about-page.md +5 -0
  25. package/dist/registry/docs/announcement-bar.md +38 -0
  26. package/dist/registry/docs/auth-core.md +64 -0
  27. package/dist/registry/docs/blog-list-page.md +1 -0
  28. package/dist/registry/docs/case-study-page.md +39 -0
  29. package/dist/registry/docs/checkout-page.md +2 -1
  30. package/dist/registry/docs/coming-soon-page-minimal.md +32 -0
  31. package/dist/registry/docs/coming-soon-page.md +37 -0
  32. package/dist/registry/docs/contact-page-map-overlay.md +2 -2
  33. package/dist/registry/docs/contact-page-map-split.md +2 -2
  34. package/dist/registry/docs/contact-page.md +5 -0
  35. package/dist/registry/docs/cookie-consent.md +37 -0
  36. package/dist/registry/docs/cookies-page.md +5 -0
  37. package/dist/registry/docs/ecommerce-core.md +4 -3
  38. package/dist/registry/docs/forgot-password-page-split.md +45 -0
  39. package/dist/registry/docs/forgot-password-page.md +14 -5
  40. package/dist/registry/docs/header-ecommerce.md +1 -0
  41. package/dist/registry/docs/hero-carousel.md +37 -0
  42. package/dist/registry/docs/landing-page-app.md +43 -0
  43. package/dist/registry/docs/landing-page-saas.md +41 -0
  44. package/dist/registry/docs/login-page-split.md +13 -4
  45. package/dist/registry/docs/login-page.md +17 -4
  46. package/dist/registry/docs/logo-cloud.md +33 -0
  47. package/dist/registry/docs/masonry-grid.md +37 -0
  48. package/dist/registry/docs/my-orders-page.md +44 -0
  49. package/dist/registry/docs/order-confirmation-page.md +41 -0
  50. package/dist/registry/docs/portfolio-page.md +38 -0
  51. package/dist/registry/docs/pricing-page.md +38 -0
  52. package/dist/registry/docs/privacy-page.md +5 -0
  53. package/dist/registry/docs/product-quick-view.md +37 -0
  54. package/dist/registry/docs/reading-progress.md +31 -0
  55. package/dist/registry/docs/register-page-split.md +45 -0
  56. package/dist/registry/docs/register-page.md +14 -7
  57. package/dist/registry/docs/reset-password-page-split.md +45 -0
  58. package/dist/registry/docs/reset-password-page.md +36 -0
  59. package/dist/registry/docs/share-buttons.md +37 -0
  60. package/dist/registry/docs/team-page.md +38 -0
  61. package/dist/registry/docs/terms-page.md +5 -0
  62. package/dist/registry/docs/timeline-section.md +37 -0
  63. package/dist/registry/docs/video-hero.md +41 -0
  64. package/dist/registry/ecommerce-core.json +17 -1
  65. package/dist/registry/empty-page.json +1 -1
  66. package/dist/registry/faq-categorized.json +1 -1
  67. package/dist/registry/faq-simple.json +1 -1
  68. package/dist/registry/favorites-ecommerce-block.json +1 -1
  69. package/dist/registry/feature-section.json +2 -2
  70. package/dist/registry/footer.json +1 -1
  71. package/dist/registry/forgot-password-page-split.json +50 -0
  72. package/dist/registry/forgot-password-page.json +9 -7
  73. package/dist/registry/header-ecommerce.json +3 -2
  74. package/dist/registry/header-mega.json +1 -1
  75. package/dist/registry/hero-carousel.json +45 -0
  76. package/dist/registry/hero-cta.json +2 -2
  77. package/dist/registry/hero-gradient.json +1 -1
  78. package/dist/registry/hero.json +1 -1
  79. package/dist/registry/index.json +22 -2
  80. package/dist/registry/landing-page-app.json +47 -0
  81. package/dist/registry/landing-page-saas.json +47 -0
  82. package/dist/registry/login-page-split.json +11 -7
  83. package/dist/registry/login-page.json +4 -4
  84. package/dist/registry/logo-cloud.json +41 -0
  85. package/dist/registry/masonry-grid.json +43 -0
  86. package/dist/registry/my-orders-page.json +52 -0
  87. package/dist/registry/order-confirmation-page.json +49 -0
  88. package/dist/registry/portfolio-page.json +45 -0
  89. package/dist/registry/pricing-page.json +47 -0
  90. package/dist/registry/pricing-section.json +1 -1
  91. package/dist/registry/privacy-page.json +3 -1
  92. package/dist/registry/product-detail-block.json +1 -1
  93. package/dist/registry/product-quick-view.json +46 -0
  94. package/dist/registry/products-page.json +3 -3
  95. package/dist/registry/reading-progress.json +43 -0
  96. package/dist/registry/register-page-split.json +50 -0
  97. package/dist/registry/register-page.json +9 -7
  98. package/dist/registry/reset-password-page-split.json +50 -0
  99. package/dist/registry/reset-password-page.json +39 -0
  100. package/dist/registry/share-buttons.json +46 -0
  101. package/dist/registry/team-page.json +47 -0
  102. package/dist/registry/terms-page.json +3 -1
  103. package/dist/registry/testimonials-carousel.json +1 -1
  104. package/dist/registry/testimonials-grid.json +1 -1
  105. package/dist/registry/timeline-section.json +43 -0
  106. package/dist/registry/video-hero.json +42 -0
  107. package/package.json +1 -1
  108. package/template/index.html +5 -5
  109. package/template/src/App.tsx +4 -0
  110. package/template/src/components/GoogleAnalytics.tsx +34 -0
  111. package/template/src/components/Layout.tsx +1 -1
  112. package/template/src/components/ScriptInjector.tsx +62 -0
  113. package/template/src/constants/constants.json +8 -2
  114. package/template/src/pages/Index.tsx +5 -1
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ globstar while`,D,V,F,G,W),this.matchOne(D.slice(V),F.slice(G),X))return this.de
51
51
  ${F}`,kT=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),yT=Object.getOwnPropertyDescriptor(Function.prototype.toString,"name"),vT=(D,F,X)=>{let J=X===""?"":`with ${X.trim()}() `,Q=uT.bind(null,J,F.toString());Object.defineProperty(Q,"name",yT);let{writable:q,enumerable:Y,configurable:Z}=kT;Object.defineProperty(D,"toString",{value:Q,writable:q,enumerable:Y,configurable:Z})};function CX(D,F,{ignoreNonConfigurable:X=!1}={}){let{name:J}=D;for(let Q of Reflect.ownKeys(F))PT(D,F,Q,X);return ST(D,F),vT(D,F,J),D}var b7=new WeakMap,tK=(D,F={})=>{if(typeof D!=="function")throw TypeError("Expected a function");let X,J=0,Q=D.displayName||D.name||"<anonymous>",q=function(...Y){if(b7.set(q,++J),J===1)X=D.apply(this,Y),D=void 0;else if(F.throw===!0)throw Error(`Function \`${Q}\` can only be called once`);return X};return CX(q,D),b7.set(q,J),q};tK.callCount=(D)=>{if(!b7.has(D))throw Error(`The given function \`${D.name}\` is not wrapped by the \`onetime\` package`);return b7.get(D)};var eK=tK;var X6=[];X6.push("SIGHUP","SIGINT","SIGTERM");if(process.platform!=="win32")X6.push("SIGALRM","SIGABRT","SIGVTALRM","SIGXCPU","SIGXFSZ","SIGUSR2","SIGTRAP","SIGSYS","SIGQUIT","SIGIOT");if(process.platform==="linux")X6.push("SIGIO","SIGPOLL","SIGPWR","SIGSTKFLT");var m7=(D)=>!!D&&typeof D==="object"&&typeof D.removeListener==="function"&&typeof D.emit==="function"&&typeof D.reallyExit==="function"&&typeof D.listeners==="function"&&typeof D.kill==="function"&&typeof D.pid==="number"&&typeof D.on==="function",IX=Symbol.for("signal-exit emitter"),jX=globalThis,hT=Object.defineProperty.bind(Object);class DB{emitted={afterExit:!1,exit:!1};listeners={afterExit:[],exit:[]};count=0;id=Math.random();constructor(){if(jX[IX])return jX[IX];hT(jX,IX,{value:this,writable:!1,enumerable:!1,configurable:!1})}on(D,F){this.listeners[D].push(F)}removeListener(D,F){let X=this.listeners[D],J=X.indexOf(F);if(J===-1)return;if(J===0&&X.length===1)X.length=0;else X.splice(J,1)}emit(D,F,X){if(this.emitted[D])return!1;this.emitted[D]=!0;let J=!1;for(let Q of this.listeners[D])J=Q(F,X)===!0||J;if(D==="exit")J=this.emit("afterExit",F,X)||J;return J}}class EX{}var gT=(D)=>{return{onExit(F,X){return D.onExit(F,X)},load(){return D.load()},unload(){return D.unload()}}};class FB extends EX{onExit(){return()=>{}}load(){}unload(){}}class XB extends EX{#D=LX.platform==="win32"?"SIGINT":"SIGHUP";#F=new DB;#X;#J;#q;#W={};#H=!1;constructor(D){super();this.#X=D,this.#W={};for(let F of X6)this.#W[F]=()=>{let X=this.#X.listeners(F),{count:J}=this.#F,Q=D;if(typeof Q.__signal_exit_emitter__==="object"&&typeof Q.__signal_exit_emitter__.count==="number")J+=Q.__signal_exit_emitter__.count;if(X.length===J){this.unload();let q=this.#F.emit("exit",null,F),Y=F==="SIGHUP"?this.#D:F;if(!q)D.kill(D.pid,Y)}};this.#q=D.reallyExit,this.#J=D.emit}onExit(D,F){if(!m7(this.#X))return()=>{};if(this.#H===!1)this.load();let X=F?.alwaysLast?"afterExit":"exit";return this.#F.on(X,D),()=>{if(this.#F.removeListener(X,D),this.#F.listeners.exit.length===0&&this.#F.listeners.afterExit.length===0)this.unload()}}load(){if(this.#H)return;this.#H=!0,this.#F.count+=1;for(let D of X6)try{let F=this.#W[D];if(F)this.#X.on(D,F)}catch(F){}this.#X.emit=(D,...F)=>{return this.#Z(D,...F)},this.#X.reallyExit=(D)=>{return this.#B(D)}}unload(){if(!this.#H)return;this.#H=!1,X6.forEach((D)=>{let F=this.#W[D];if(!F)throw Error("Listener not defined for signal: "+D);try{this.#X.removeListener(D,F)}catch(X){}}),this.#X.emit=this.#J,this.#X.reallyExit=this.#q,this.#F.count-=1}#B(D){if(!m7(this.#X))return 0;return this.#X.exitCode=D||0,this.#F.emit("exit",this.#X.exitCode,null),this.#q.call(this.#X,this.#X.exitCode)}#Z(D,...F){let X=this.#J;if(D==="exit"&&m7(this.#X)){if(typeof F[0]==="number")this.#X.exitCode=F[0];let J=X.call(this.#X,D,...F);return this.#F.emit("exit",this.#X.exitCode,null),J}else return X.call(this.#X,D,...F)}}var LX=globalThis.process,{onExit:JB,load:Y20,unload:Z20}=gT(m7(LX)?new XB(LX):new FB);var QB=d7.stderr.isTTY?d7.stderr:d7.stdout.isTTY?d7.stdout:void 0,fT=QB?eK(()=>{JB(()=>{QB.write("\x1B[?25h")},{alwaysLast:!0})}):()=>{},qB=fT;var c7=!1,o6={};o6.show=(D=YB.stderr)=>{if(!D.isTTY)return;c7=!1,D.write("\x1B[?25h")};o6.hide=(D=YB.stderr)=>{if(!D.isTTY)return;qB(),c7=!0,D.write("\x1B[?25l")};o6.toggle=(D,F)=>{if(D!==void 0)c7=D;if(c7)o6.show(F);else o6.hide(F)};var AX=o6;var L8=N2(wX(),1);import U1 from"node:process";function OX(){if(U1.platform!=="win32")return U1.env.TERM!=="linux";return Boolean(U1.env.CI)||Boolean(U1.env.WT_SESSION)||Boolean(U1.env.TERMINUS_SUBLIME)||U1.env.ConEmuTask==="{cmd::Cmder}"||U1.env.TERM_PROGRAM==="Terminus-Sublime"||U1.env.TERM_PROGRAM==="vscode"||U1.env.TERM==="xterm-256color"||U1.env.TERM==="alacritty"||U1.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var mT={info:M.blue("ℹ"),success:M.green("✔"),warning:M.yellow("⚠"),error:M.red("✖")},dT={info:M.blue("i"),success:M.green("√"),warning:M.yellow("‼"),error:M.red("×")},cT=OX()?mT:dT,I8=cT;function RX({onlyFirst:D=!1}={}){return new RegExp("(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))|[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]",D?void 0:"g")}var lT=RX();function j8(D){if(typeof D!=="string")throw TypeError(`Expected a \`string\`, got \`${typeof D}\``);return D.replace(lT,"")}function HB(D){return D===161||D===164||D===167||D===168||D===170||D===173||D===174||D>=176&&D<=180||D>=182&&D<=186||D>=188&&D<=191||D===198||D===208||D===215||D===216||D>=222&&D<=225||D===230||D>=232&&D<=234||D===236||D===237||D===240||D===242||D===243||D>=247&&D<=250||D===252||D===254||D===257||D===273||D===275||D===283||D===294||D===295||D===299||D>=305&&D<=307||D===312||D>=319&&D<=322||D===324||D>=328&&D<=331||D===333||D===338||D===339||D===358||D===359||D===363||D===462||D===464||D===466||D===468||D===470||D===472||D===474||D===476||D===593||D===609||D===708||D===711||D>=713&&D<=715||D===717||D===720||D>=728&&D<=731||D===733||D===735||D>=768&&D<=879||D>=913&&D<=929||D>=931&&D<=937||D>=945&&D<=961||D>=963&&D<=969||D===1025||D>=1040&&D<=1103||D===1105||D===8208||D>=8211&&D<=8214||D===8216||D===8217||D===8220||D===8221||D>=8224&&D<=8226||D>=8228&&D<=8231||D===8240||D===8242||D===8243||D===8245||D===8251||D===8254||D===8308||D===8319||D>=8321&&D<=8324||D===8364||D===8451||D===8453||D===8457||D===8467||D===8470||D===8481||D===8482||D===8486||D===8491||D===8531||D===8532||D>=8539&&D<=8542||D>=8544&&D<=8555||D>=8560&&D<=8569||D===8585||D>=8592&&D<=8601||D===8632||D===8633||D===8658||D===8660||D===8679||D===8704||D===8706||D===8707||D===8711||D===8712||D===8715||D===8719||D===8721||D===8725||D===8730||D>=8733&&D<=8736||D===8739||D===8741||D>=8743&&D<=8748||D===8750||D>=8756&&D<=8759||D===8764||D===8765||D===8776||D===8780||D===8786||D===8800||D===8801||D>=8804&&D<=8807||D===8810||D===8811||D===8814||D===8815||D===8834||D===8835||D===8838||D===8839||D===8853||D===8857||D===8869||D===8895||D===8978||D>=9312&&D<=9449||D>=9451&&D<=9547||D>=9552&&D<=9587||D>=9600&&D<=9615||D>=9618&&D<=9621||D===9632||D===9633||D>=9635&&D<=9641||D===9650||D===9651||D===9654||D===9655||D===9660||D===9661||D===9664||D===9665||D>=9670&&D<=9672||D===9675||D>=9678&&D<=9681||D>=9698&&D<=9701||D===9711||D===9733||D===9734||D===9737||D===9742||D===9743||D===9756||D===9758||D===9792||D===9794||D===9824||D===9825||D>=9827&&D<=9829||D>=9831&&D<=9834||D===9836||D===9837||D===9839||D===9886||D===9887||D===9919||D>=9926&&D<=9933||D>=9935&&D<=9939||D>=9941&&D<=9953||D===9955||D===9960||D===9961||D>=9963&&D<=9969||D===9972||D>=9974&&D<=9977||D===9979||D===9980||D===9982||D===9983||D===10045||D>=10102&&D<=10111||D>=11094&&D<=11097||D>=12872&&D<=12879||D>=57344&&D<=63743||D>=65024&&D<=65039||D===65533||D>=127232&&D<=127242||D>=127248&&D<=127277||D>=127280&&D<=127337||D>=127344&&D<=127373||D===127375||D===127376||D>=127387&&D<=127404||D>=917760&&D<=917999||D>=983040&&D<=1048573||D>=1048576&&D<=1114109}function VB(D){return D===12288||D>=65281&&D<=65376||D>=65504&&D<=65510}function WB(D){return D>=4352&&D<=4447||D===8986||D===8987||D===9001||D===9002||D>=9193&&D<=9196||D===9200||D===9203||D===9725||D===9726||D===9748||D===9749||D>=9776&&D<=9783||D>=9800&&D<=9811||D===9855||D>=9866&&D<=9871||D===9875||D===9889||D===9898||D===9899||D===9917||D===9918||D===9924||D===9925||D===9934||D===9940||D===9962||D===9970||D===9971||D===9973||D===9978||D===9981||D===9989||D===9994||D===9995||D===10024||D===10060||D===10062||D>=10067&&D<=10069||D===10071||D>=10133&&D<=10135||D===10160||D===10175||D===11035||D===11036||D===11088||D===11093||D>=11904&&D<=11929||D>=11931&&D<=12019||D>=12032&&D<=12245||D>=12272&&D<=12287||D>=12289&&D<=12350||D>=12353&&D<=12438||D>=12441&&D<=12543||D>=12549&&D<=12591||D>=12593&&D<=12686||D>=12688&&D<=12773||D>=12783&&D<=12830||D>=12832&&D<=12871||D>=12880&&D<=42124||D>=42128&&D<=42182||D>=43360&&D<=43388||D>=44032&&D<=55203||D>=63744&&D<=64255||D>=65040&&D<=65049||D>=65072&&D<=65106||D>=65108&&D<=65126||D>=65128&&D<=65131||D>=94176&&D<=94180||D>=94192&&D<=94198||D>=94208&&D<=101589||D>=101631&&D<=101662||D>=101760&&D<=101874||D>=110576&&D<=110579||D>=110581&&D<=110587||D===110589||D===110590||D>=110592&&D<=110882||D===110898||D>=110928&&D<=110930||D===110933||D>=110948&&D<=110951||D>=110960&&D<=111355||D>=119552&&D<=119638||D>=119648&&D<=119670||D===126980||D===127183||D===127374||D>=127377&&D<=127386||D>=127488&&D<=127490||D>=127504&&D<=127547||D>=127552&&D<=127560||D===127568||D===127569||D>=127584&&D<=127589||D>=127744&&D<=127776||D>=127789&&D<=127797||D>=127799&&D<=127868||D>=127870&&D<=127891||D>=127904&&D<=127946||D>=127951&&D<=127955||D>=127968&&D<=127984||D===127988||D>=127992&&D<=128062||D===128064||D>=128066&&D<=128252||D>=128255&&D<=128317||D>=128331&&D<=128334||D>=128336&&D<=128359||D===128378||D===128405||D===128406||D===128420||D>=128507&&D<=128591||D>=128640&&D<=128709||D===128716||D>=128720&&D<=128722||D>=128725&&D<=128728||D>=128732&&D<=128735||D===128747||D===128748||D>=128756&&D<=128764||D>=128992&&D<=129003||D===129008||D>=129292&&D<=129338||D>=129340&&D<=129349||D>=129351&&D<=129535||D>=129648&&D<=129660||D>=129664&&D<=129674||D>=129678&&D<=129734||D===129736||D>=129741&&D<=129756||D>=129759&&D<=129770||D>=129775&&D<=129784||D>=131072&&D<=196605||D>=196608&&D<=262141}function pT(D){if(!Number.isSafeInteger(D))throw TypeError(`Expected a code point, got \`${typeof D}\`.`)}function GB(D,{ambiguousAsWide:F=!1}={}){if(pT(D),VB(D)||WB(D)||F&&HB(D))return 2;return 1}var UB=N2(zB(),1),iT=new Intl.Segmenter,oT=/^\p{Default_Ignorable_Code_Point}$/u;function NX(D,F={}){if(typeof D!=="string"||D.length===0)return 0;let{ambiguousIsNarrow:X=!0,countAnsiEscapeCodes:J=!1}=F;if(!J)D=j8(D);if(D.length===0)return 0;let Q=0,q={ambiguousAsWide:!X};for(let{segment:Y}of iT.segment(D)){let Z=Y.codePointAt(0);if(Z<=31||Z>=127&&Z<=159)continue;if(Z>=8203&&Z<=8207||Z===65279)continue;if(Z>=768&&Z<=879||Z>=6832&&Z<=6911||Z>=7616&&Z<=7679||Z>=8400&&Z<=8447||Z>=65056&&Z<=65071)continue;if(Z>=55296&&Z<=57343)continue;if(Z>=65024&&Z<=65039)continue;if(oT.test(Y))continue;if(UB.default().test(Y)){Q+=2;continue}Q+=GB(Z,q)}return Q}function TX({stream:D=process.stdout}={}){return Boolean(D&&D.isTTY&&process.env.TERM!=="dumb"&&!("CI"in process.env))}import MB from"node:process";function _X(){let{env:D}=MB,{TERM:F,TERM_PROGRAM:X}=D;if(MB.platform!=="win32")return F!=="linux";return Boolean(D.WT_SESSION)||Boolean(D.TERMINUS_SUBLIME)||D.ConEmuTask==="{cmd::Cmder}"||X==="Terminus-Sublime"||X==="vscode"||F==="xterm-256color"||F==="alacritty"||F==="rxvt-unicode"||F==="rxvt-unicode-256color"||D.TERMINAL_EMULATOR==="JetBrains-JediTerm"}import y1 from"node:process";var nT=3;class CB{#D=0;start(){if(this.#D++,this.#D===1)this.#F()}stop(){if(this.#D<=0)throw Error("`stop` called more times than `start`");if(this.#D--,this.#D===0)this.#X()}#F(){if(y1.platform==="win32"||!y1.stdin.isTTY)return;y1.stdin.setRawMode(!0),y1.stdin.on("data",this.#J),y1.stdin.resume()}#X(){if(!y1.stdin.isTTY)return;y1.stdin.off("data",this.#J),y1.stdin.pause(),y1.stdin.setRawMode(!1)}#J(D){if(D[0]===nT)y1.emit("SIGINT")}}var rT=new CB,PX=rT;var sT=N2(wX(),1);class IB{#D=0;#F=!1;#X=0;#J=-1;#q=0;#W;#H;#B;#Z;#V;#Y;#G;#K;#z;#$;#C;color;constructor(D){if(typeof D==="string")D={text:D};if(this.#W={color:"cyan",stream:p7.stderr,discardStdin:!0,hideCursor:!0,...D},this.color=this.#W.color,this.spinner=this.#W.spinner,this.#V=this.#W.interval,this.#B=this.#W.stream,this.#Y=typeof this.#W.isEnabled==="boolean"?this.#W.isEnabled:TX({stream:this.#B}),this.#G=typeof this.#W.isSilent==="boolean"?this.#W.isSilent:!1,this.text=this.#W.text,this.prefixText=this.#W.prefixText,this.suffixText=this.#W.suffixText,this.indent=this.#W.indent,p7.env.NODE_ENV==="test")this._stream=this.#B,this._isEnabled=this.#Y,Object.defineProperty(this,"_linesToClear",{get(){return this.#D},set(F){this.#D=F}}),Object.defineProperty(this,"_frameIndex",{get(){return this.#J}}),Object.defineProperty(this,"_lineCount",{get(){return this.#X}})}get indent(){return this.#K}set indent(D=0){if(!(D>=0&&Number.isInteger(D)))throw Error("The `indent` option must be an integer from 0 and up");this.#K=D,this.#j()}get interval(){return this.#V??this.#H.interval??100}get spinner(){return this.#H}set spinner(D){if(this.#J=-1,this.#V=void 0,typeof D==="object"){if(D.frames===void 0)throw Error("The given spinner must have a `frames` property");this.#H=D}else if(!_X())this.#H=L8.default.line;else if(D===void 0)this.#H=L8.default.dots;else if(D!=="default"&&L8.default[D])this.#H=L8.default[D];else throw Error(`There is no built-in spinner named '${D}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`)}get text(){return this.#z}set text(D=""){this.#z=D,this.#j()}get prefixText(){return this.#$}set prefixText(D=""){this.#$=D,this.#j()}get suffixText(){return this.#C}set suffixText(D=""){this.#C=D,this.#j()}get isSpinning(){return this.#Z!==void 0}#U(D=this.#$,F=" "){if(typeof D==="string"&&D!=="")return D+F;if(typeof D==="function")return D()+F;return""}#R(D=this.#C,F=" "){if(typeof D==="string"&&D!=="")return F+D;if(typeof D==="function")return F+D();return""}#j(){let D=this.#B.columns??80,F=this.#U(this.#$,"-"),X=this.#R(this.#C,"-"),J=" ".repeat(this.#K)+F+"--"+this.#z+"--"+X;this.#X=0;for(let Q of j8(J).split(`
52
52
  `))this.#X+=Math.max(1,Math.ceil(NX(Q,{countAnsiEscapeCodes:!0})/D))}get isEnabled(){return this.#Y&&!this.#G}set isEnabled(D){if(typeof D!=="boolean")throw TypeError("The `isEnabled` option must be a boolean");this.#Y=D}get isSilent(){return this.#G}set isSilent(D){if(typeof D!=="boolean")throw TypeError("The `isSilent` option must be a boolean");this.#G=D}frame(){let D=Date.now();if(this.#J===-1||D-this.#q>=this.interval)this.#J=++this.#J%this.#H.frames.length,this.#q=D;let{frames:F}=this.#H,X=F[this.#J];if(this.color)X=M[this.color](X);let J=typeof this.#$==="string"&&this.#$!==""?this.#$+" ":"",Q=typeof this.text==="string"?" "+this.text:"",q=typeof this.#C==="string"&&this.#C!==""?" "+this.#C:"";return J+X+Q+q}clear(){if(!this.#Y||!this.#B.isTTY)return this;this.#B.cursorTo(0);for(let D=0;D<this.#D;D++){if(D>0)this.#B.moveCursor(0,-1);this.#B.clearLine(1)}if(this.#K||this.lastIndent!==this.#K)this.#B.cursorTo(this.#K);return this.lastIndent=this.#K,this.#D=0,this}render(){if(this.#G)return this;return this.clear(),this.#B.write(this.frame()),this.#D=this.#X,this}start(D){if(D)this.text=D;if(this.#G)return this;if(!this.#Y){if(this.text)this.#B.write(`- ${this.text}
53
53
  `);return this}if(this.isSpinning)return this;if(this.#W.hideCursor)AX.hide(this.#B);if(this.#W.discardStdin&&p7.stdin.isTTY)this.#F=!0,PX.start();return this.render(),this.#Z=setInterval(this.render.bind(this),this.interval),this}stop(){if(!this.#Y)return this;if(clearInterval(this.#Z),this.#Z=void 0,this.#J=0,this.clear(),this.#W.hideCursor)AX.show(this.#B);if(this.#W.discardStdin&&p7.stdin.isTTY&&this.#F)PX.stop(),this.#F=!1;return this}succeed(D){return this.stopAndPersist({symbol:I8.success,text:D})}fail(D){return this.stopAndPersist({symbol:I8.error,text:D})}warn(D){return this.stopAndPersist({symbol:I8.warning,text:D})}info(D){return this.stopAndPersist({symbol:I8.info,text:D})}stopAndPersist(D={}){if(this.#G)return this;let F=D.prefixText??this.#$,X=this.#U(F," "),J=D.symbol??" ",Q=D.text??this.text,Y=typeof Q==="string"?(J?" ":"")+Q:"",Z=D.suffixText??this.#C,K=this.#R(Z," "),H=X+J+Y+K+`
54
- `;return this.stop(),this.#B.write(H),this}}function H2(D){return new IB(D)}var y8=N2(h1(),1);import k8 from"path";import{execSync as vx}from"child_process";var _8=N2(h1(),1);import pX from"path";var iX="promake.json";async function W2(D){let F=pX.join(D,iX);if(await _8.default.pathExists(F))return _8.default.readJson(F);return null}async function Q5(D,F){let X=pX.join(D,iX);await _8.default.writeJson(X,F,{spaces:2})}async function oX(D){let F=pX.join(D,iX);return _8.default.pathExists(F)}function q6(){return{modulesPath:"src/modules",envFile:".env",modules:{}}}async function JD(D,F,X){let J=await W2(D)||q6();if(!J.modules)J.modules={};J.modules[F]=X,await Q5(D,J)}async function nX(D,F){let X=await W2(D);if(X?.modules&&F in X.modules)delete X.modules[F],await Q5(D,X)}function QD(D,F,X){if(X==="default")return`${D.modulesPath||"src/modules"}/${F}`;return X}var Y6=N2(h1(),1);import Z6 from"path";import{fileURLToPath as Wx}from"url";var Gx=Wx(import.meta.url),P8=Z6.dirname(Gx),x8={mode:process.env.PROMAKE_MODE||"local",remoteRegistryUrl:"https://raw.githubusercontent.com/anthropics/promake-registry/main",remoteTemplateUrl:"https://raw.githubusercontent.com/anthropics/promake-registry/main/template"},$V=P8.includes("src"),zV=$V?Z6.join(P8,"..","..","dist","registry"):Z6.join(P8,"registry"),UV=$V?Z6.join(P8,"..","..","template"):Z6.join(P8,"..","template");async function C1(D){if(x8.mode==="local"){let J=Z6.join(zV,`${D}.json`);if(await Y6.default.pathExists(J))return Y6.default.readJson(J)}let F=`${x8.remoteRegistryUrl}/registry/${D}.json`,X=await fetch(F);if(!X.ok)throw Error(`Registry item "${D}" not found (status: ${X.status})`);return X.json()}async function MV(){if(x8.mode==="local"){let X=Z6.join(zV,"index.json");if(await Y6.default.pathExists(X))return Y6.default.readJson(X)}let D=`${x8.remoteRegistryUrl}/registry/index.json`,F=await fetch(D);if(!F.ok)throw Error("Failed to fetch registry index");return F.json()}async function CV(D){try{return(await C1(D)).usage||null}catch{return null}}async function $x(){return Y6.default.pathExists(UV)}async function IV(D){if(await $x()){await Y6.default.copy(UV,D);return}if(x8.mode==="remote")throw Error("Remote template download not yet implemented. Please use local mode or ensure template/ exists.");throw Error("Template not found. Ensure cli/template/ exists.")}var q5=N2(h1(),1);import{execSync as zx}from"child_process";import t6 from"path";function Ux(D){if(q5.default.existsSync(t6.join(D,"bun.lockb")))return"bun";if(q5.default.existsSync(t6.join(D,"pnpm-lock.yaml")))return"pnpm";if(q5.default.existsSync(t6.join(D,"yarn.lock")))return"yarn";if(q5.default.existsSync(t6.join(D,"package-lock.json")))return"npm";return"npm"}function Mx(D,F,X=!1){let J=F.join(" ");return{bun:`bun add ${X?"-D ":""}${J}`,npm:`npm install ${X?"--save-dev ":""}${J}`,yarn:`yarn add ${X?"-D ":""}${J}`,pnpm:`pnpm add ${X?"-D ":""}${J}`}[D]}async function rX(D,F,X=!1){if(F.length===0)return;let J=Ux(D),Q=Mx(J,F,X);zx(Q,{cwd:D,stdio:"inherit"})}var Cx=new Set(["react","react-dom","react-router","zustand","@tanstack/react-query","tailwindcss","@tailwindcss/vite","tailwind-merge","clsx","class-variance-authority","@radix-ui/react-accordion","@radix-ui/react-alert-dialog","@radix-ui/react-aspect-ratio","@radix-ui/react-avatar","@radix-ui/react-checkbox","@radix-ui/react-collapsible","@radix-ui/react-context-menu","@radix-ui/react-dialog","@radix-ui/react-dropdown-menu","@radix-ui/react-hover-card","@radix-ui/react-label","@radix-ui/react-menubar","@radix-ui/react-navigation-menu","@radix-ui/react-popover","@radix-ui/react-progress","@radix-ui/react-radio-group","@radix-ui/react-scroll-area","@radix-ui/react-select","@radix-ui/react-separator","@radix-ui/react-slider","@radix-ui/react-slot","@radix-ui/react-switch","@radix-ui/react-tabs","@radix-ui/react-toggle","@radix-ui/react-toggle-group","@radix-ui/react-tooltip","cmdk","embla-carousel-react","input-otp","react-day-picker","react-resizable-panels","recharts","sonner","vaul","react-hook-form","@hookform/resolvers","zod","i18next","react-i18next","i18next-browser-languagedetector","axios","date-fns","lucide-react","motion","next-themes","sql.js","@promakeai/customer-backend-client","@promakeai/inspector"]);async function jV(D){let F=t6.join(D,"package.json"),X=new Set;if(!await q5.default.pathExists(F))return X;let J=await q5.default.readJson(F),Q=J.dependencies||{},q=J.devDependencies||{};for(let Y of Object.keys(Q))X.add(Y);for(let Y of Object.keys(q))X.add(Y);return X}function sX(D,F){return D.filter((X)=>{let J=X.split("@")[0]||X.replace(/^@/,"").split("@")[0],Q=X.startsWith("@")?"@"+J:J;if(Cx.has(Q))return!1;if(F.has(Q))return!1;return!0})}async function LV(D,F){let X=t6.join(D,"package.json"),J=await q5.default.readJson(X);J.name=F,await q5.default.writeJson(X,J,{spaces:2})}var I1=[{name:"ecommerce",description:"E-commerce starter with cart, products, checkout",header:"header-ecommerce",footer:"footer",indexSections:["hero-cta","featured-products","category-section"],modules:["ecommerce-core","product-card","products-page","cart-page","checkout-page","cart-drawer","favorites-ecommerce-block","login-page","register-page","forgot-password-page","about-page","contact-page-map-split"],theme:{preset:"blue",radius:"medium",shadow:"small",maxWidth:"6xl"}},{name:"blog",description:"Blog starter with posts, categories, favorites",header:"header-simple",footer:"footer-minimal",indexSections:["hero","blog-section"],modules:["blog-core","post-card","blog-list-page","about-page","contact-page-split"],theme:{preset:"rose",radius:"medium",shadow:"subtle",font:"nunito"}},{name:"portfolio",description:"Portfolio starter with skills, services",header:"header-minimal",footer:"footer-minimal",indexSections:["hero-profile","feature-section","cta-section"],modules:["skill-card","service-card","about-page","contact-page"],theme:{preset:"slate",radius:"medium",shadow:"subtle",font:"raleway"}},{name:"empty",description:"Minimal setup without header, footer, or sections",modules:[],theme:{preset:"zinc"}}];var qD=["api","auth","db","animations"];import DJ from"path";import gV from"fs/promises";async function aX(D,F=!1){if(await oX(D)&&!F)return!1;let X=q6();return await Q5(D,X),!0}var EV=new E0("init").description("Initialize promake.json in current directory").option("-f, --force","Overwrite existing config").action(async(D)=>{let F=process.cwd();if(await oX(F)&&!D.force){console.log(M.yellow("promake.json already exists.")),console.log(M.dim("Use --force to overwrite."));return}await aX(F,D.force),console.log(M.green("Created promake.json")),console.log(""),console.log("You can now add modules:"),console.log(M.cyan(" npx promake add <module-name>"))});var y0=N2(h1(),1);import S8 from"path";async function wV(D,F,X){let J=await y0.default.pathExists(F);switch(X){case"overwrite":return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:J?"updated":"created",path:F};case"stop":if(J)return{action:"skipped",path:F,reason:"file exists"};return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};case"skip":return{action:"skipped",path:F,reason:"skip behavior"};case"merge":if(F.endsWith(".json"))return Ix(D,F);if(F.endsWith(".css"))return jx(D,F);return{action:"skipped",path:F,reason:"no merge strategy"};case"prompt":if(J)return{action:"skipped",path:F,reason:"file exists"};return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};default:return{action:"skipped",path:F,reason:"unknown behavior"}}}async function Ix(D,F){if(!await y0.default.pathExists(F))return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};try{let J=await y0.default.readJson(F),Q=JSON.parse(D),q=OV(J,Q);return await y0.default.writeJson(F,q,{spaces:2}),{action:"merged",path:F}}catch(J){return{action:"skipped",path:F,reason:"merge failed"}}}async function jx(D,F){if(!await y0.default.pathExists(F))return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};try{let J=await y0.default.readFile(F,"utf-8");if(!J.includes(D.trim()))return await y0.default.writeFile(F,J+`
54
+ `;return this.stop(),this.#B.write(H),this}}function H2(D){return new IB(D)}var y8=N2(h1(),1);import k8 from"path";import{execSync as vx}from"child_process";var _8=N2(h1(),1);import pX from"path";var iX="promake.json";async function W2(D){let F=pX.join(D,iX);if(await _8.default.pathExists(F))return _8.default.readJson(F);return null}async function Q5(D,F){let X=pX.join(D,iX);await _8.default.writeJson(X,F,{spaces:2})}async function oX(D){let F=pX.join(D,iX);return _8.default.pathExists(F)}function q6(){return{modulesPath:"src/modules",envFile:".env",modules:{}}}async function JD(D,F,X){let J=await W2(D)||q6();if(!J.modules)J.modules={};J.modules[F]=X,await Q5(D,J)}async function nX(D,F){let X=await W2(D);if(X?.modules&&F in X.modules)delete X.modules[F],await Q5(D,X)}function QD(D,F,X){if(X==="default")return`${D.modulesPath||"src/modules"}/${F}`;return X}var Y6=N2(h1(),1);import Z6 from"path";import{fileURLToPath as Wx}from"url";var Gx=Wx(import.meta.url),P8=Z6.dirname(Gx),x8={mode:process.env.PROMAKE_MODE||"local",remoteRegistryUrl:"https://raw.githubusercontent.com/anthropics/promake-registry/main",remoteTemplateUrl:"https://raw.githubusercontent.com/anthropics/promake-registry/main/template"},$V=P8.includes("src"),zV=$V?Z6.join(P8,"..","..","dist","registry"):Z6.join(P8,"registry"),UV=$V?Z6.join(P8,"..","..","template"):Z6.join(P8,"..","template");async function C1(D){if(x8.mode==="local"){let J=Z6.join(zV,`${D}.json`);if(await Y6.default.pathExists(J))return Y6.default.readJson(J)}let F=`${x8.remoteRegistryUrl}/registry/${D}.json`,X=await fetch(F);if(!X.ok)throw Error(`Registry item "${D}" not found (status: ${X.status})`);return X.json()}async function MV(){if(x8.mode==="local"){let X=Z6.join(zV,"index.json");if(await Y6.default.pathExists(X))return Y6.default.readJson(X)}let D=`${x8.remoteRegistryUrl}/registry/index.json`,F=await fetch(D);if(!F.ok)throw Error("Failed to fetch registry index");return F.json()}async function CV(D){try{return(await C1(D)).usage||null}catch{return null}}async function $x(){return Y6.default.pathExists(UV)}async function IV(D){if(await $x()){await Y6.default.copy(UV,D,{filter:(F)=>!F.includes("node_modules")});return}if(x8.mode==="remote")throw Error("Remote template download not yet implemented. Please use local mode or ensure template/ exists.");throw Error("Template not found. Ensure cli/template/ exists.")}var q5=N2(h1(),1);import{execSync as zx}from"child_process";import t6 from"path";function Ux(D){if(q5.default.existsSync(t6.join(D,"bun.lockb")))return"bun";if(q5.default.existsSync(t6.join(D,"pnpm-lock.yaml")))return"pnpm";if(q5.default.existsSync(t6.join(D,"yarn.lock")))return"yarn";if(q5.default.existsSync(t6.join(D,"package-lock.json")))return"npm";return"npm"}function Mx(D,F,X=!1){let J=F.join(" ");return{bun:`bun add ${X?"-D ":""}${J}`,npm:`npm install ${X?"--save-dev ":""}${J}`,yarn:`yarn add ${X?"-D ":""}${J}`,pnpm:`pnpm add ${X?"-D ":""}${J}`}[D]}async function rX(D,F,X=!1){if(F.length===0)return;let J=Ux(D),Q=Mx(J,F,X);zx(Q,{cwd:D,stdio:"inherit"})}var Cx=new Set(["react","react-dom","react-router","zustand","@tanstack/react-query","tailwindcss","@tailwindcss/vite","tailwind-merge","clsx","class-variance-authority","@radix-ui/react-accordion","@radix-ui/react-alert-dialog","@radix-ui/react-aspect-ratio","@radix-ui/react-avatar","@radix-ui/react-checkbox","@radix-ui/react-collapsible","@radix-ui/react-context-menu","@radix-ui/react-dialog","@radix-ui/react-dropdown-menu","@radix-ui/react-hover-card","@radix-ui/react-label","@radix-ui/react-menubar","@radix-ui/react-navigation-menu","@radix-ui/react-popover","@radix-ui/react-progress","@radix-ui/react-radio-group","@radix-ui/react-scroll-area","@radix-ui/react-select","@radix-ui/react-separator","@radix-ui/react-slider","@radix-ui/react-slot","@radix-ui/react-switch","@radix-ui/react-tabs","@radix-ui/react-toggle","@radix-ui/react-toggle-group","@radix-ui/react-tooltip","cmdk","embla-carousel-react","input-otp","react-day-picker","react-resizable-panels","recharts","sonner","vaul","react-hook-form","@hookform/resolvers","zod","i18next","react-i18next","i18next-browser-languagedetector","axios","date-fns","lucide-react","motion","next-themes","sql.js","@promakeai/customer-backend-client","@promakeai/inspector"]);async function jV(D){let F=t6.join(D,"package.json"),X=new Set;if(!await q5.default.pathExists(F))return X;let J=await q5.default.readJson(F),Q=J.dependencies||{},q=J.devDependencies||{};for(let Y of Object.keys(Q))X.add(Y);for(let Y of Object.keys(q))X.add(Y);return X}function sX(D,F){return D.filter((X)=>{let J=X.split("@")[0]||X.replace(/^@/,"").split("@")[0],Q=X.startsWith("@")?"@"+J:J;if(Cx.has(Q))return!1;if(F.has(Q))return!1;return!0})}async function LV(D,F){let X=t6.join(D,"package.json"),J=await q5.default.readJson(X);J.name=F,await q5.default.writeJson(X,J,{spaces:2})}var I1=[{name:"ecommerce",description:"E-commerce starter with cart, products, checkout",header:"header-ecommerce",footer:"footer",indexSections:["hero-cta","featured-products","category-section"],modules:["ecommerce-core","product-card","products-page","cart-page","checkout-page","order-confirmation-page","my-orders-page","cart-drawer","favorites-ecommerce-block","login-page-split","register-page-split","forgot-password-page-split","reset-password-page-split","about-page","contact-page-map-split"],theme:{preset:"blue",radius:"medium",shadow:"small",maxWidth:"6xl"}},{name:"blog",description:"Blog starter with posts, categories, favorites",header:"header-simple",footer:"footer-minimal",indexSections:["hero","blog-section"],modules:["blog-core","post-card","blog-list-page","about-page","contact-page-split"],theme:{preset:"rose",radius:"medium",shadow:"subtle",font:"nunito"}},{name:"portfolio",description:"Portfolio starter with skills, services",header:"header-minimal",footer:"footer-minimal",indexSections:["hero-profile","feature-section","cta-section"],modules:["skill-card","service-card","about-page","contact-page"],theme:{preset:"slate",radius:"medium",shadow:"subtle",font:"raleway"}},{name:"empty",description:"Minimal setup without header, footer, or sections",modules:[],theme:{preset:"zinc"}}];var qD=["api","auth","db","animations"];import DJ from"path";import gV from"fs/promises";async function aX(D,F=!1){if(await oX(D)&&!F)return!1;let X=q6();return await Q5(D,X),!0}var EV=new E0("init").description("Initialize promake.json in current directory").option("-f, --force","Overwrite existing config").action(async(D)=>{let F=process.cwd();if(await oX(F)&&!D.force){console.log(M.yellow("promake.json already exists.")),console.log(M.dim("Use --force to overwrite."));return}await aX(F,D.force),console.log(M.green("Created promake.json")),console.log(""),console.log("You can now add modules:"),console.log(M.cyan(" npx promake add <module-name>"))});var y0=N2(h1(),1);import S8 from"path";async function wV(D,F,X){let J=await y0.default.pathExists(F);switch(X){case"overwrite":return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:J?"updated":"created",path:F};case"stop":if(J)return{action:"skipped",path:F,reason:"file exists"};return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};case"skip":return{action:"skipped",path:F,reason:"skip behavior"};case"merge":if(F.endsWith(".json"))return Ix(D,F);if(F.endsWith(".css"))return jx(D,F);return{action:"skipped",path:F,reason:"no merge strategy"};case"prompt":if(J)return{action:"skipped",path:F,reason:"file exists"};return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};default:return{action:"skipped",path:F,reason:"unknown behavior"}}}async function Ix(D,F){if(!await y0.default.pathExists(F))return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};try{let J=await y0.default.readJson(F),Q=JSON.parse(D),q=OV(J,Q);return await y0.default.writeJson(F,q,{spaces:2}),{action:"merged",path:F}}catch(J){return{action:"skipped",path:F,reason:"merge failed"}}}async function jx(D,F){if(!await y0.default.pathExists(F))return await y0.default.ensureDir(S8.dirname(F)),await y0.default.writeFile(F,D,"utf-8"),{action:"created",path:F};try{let J=await y0.default.readFile(F,"utf-8");if(!J.includes(D.trim()))return await y0.default.writeFile(F,J+`
55
55
  `+D,"utf-8"),{action:"merged",path:F};return{action:"skipped",path:F,reason:"content already exists"}}catch(J){return{action:"skipped",path:F,reason:"merge failed"}}}function OV(D,F){let X={...D};for(let J in F)if(Object.prototype.hasOwnProperty.call(F,J)){let Q=F[J],q=X[J];if(!(J in X))X[J]=Q;else if(AV(Q)&&AV(q))X[J]=OV(q,Q)}return X}function AV(D){return Boolean(D&&typeof D==="object"&&!Array.isArray(D))}async function RV(D){await y0.default.remove(D)}async function j1(D){return y0.default.pathExists(D)}async function NV(D){return y0.default.readFile(D,"utf-8")}var tX={"registry:store":"overwrite","registry:hook":"overwrite","registry:service":"overwrite","registry:type":"overwrite","registry:context":"stop","registry:page":"stop","registry:component":"overwrite","registry:block":"overwrite","registry:ui":"skip","registry:lang":"merge","registry:lib":"overwrite","registry:style":"merge","registry:module":"stop","registry:file":"prompt","registry:index":"overwrite"},Lx=["accordion","alert","alert-dialog","aspect-ratio","avatar","badge","breadcrumb","button","calendar","card","carousel","chart","checkbox","collapsible","command","context-menu","dialog","drawer","dropdown-menu","form","hover-card","input","input-otp","label","menubar","navigation-menu","pagination","popover","progress","radio-group","resizable","scroll-area","select","separator","sheet","sidebar","skeleton","slider","sonner","switch","table","tabs","textarea","toast","toggle","toggle-group","tooltip"];function TV(D){return Lx.includes(D)}function _V(D){let F=[],X=[];for(let J of D)if(TV(J))F.push(J);else X.push(J);return{shadcn:F,promake:X}}var YD=N2(h1(),1);import Ex from"path";async function PV(D,F,X){let J=Ex.join(D,F),Q="";if(await YD.default.pathExists(J))Q=await YD.default.readFile(J,"utf-8");let q=[],Y=[];for(let[Z,K]of Object.entries(X))if(!new RegExp(`^${Z}=`,"m").test(Q))Y.push(`${Z}=${K}`),q.push(Z);if(Y.length>0){let Z=Q.trim()?`
56
56
 
57
57
  `:"";Q=Q.trim()+Z+Y.join(`
@@ -280,7 +280,7 @@ constants.json (full)`)),console.log(M.dim(JSON.stringify($.constantsConfig,null
280
280
  `)),TR(),console.log(M.bold.cyan(`════════════════════════════════════════════════════════════════
281
281
  `))});function TR(){console.log(M.bold.yellow(`
282
282
  ━━━ QUICK REFERENCE ━━━
283
- `)),$1("promake create <name>","Create new project"),$1("promake add <modules...>","Add modules"),$1("promake add <page> --module-name <name>","Add page with custom name"),$1("promake remove <modules...>","Remove modules"),$1("promake sync","Sync from promake.json"),$1("promake theme --list","List theme options"),$1("promake theme --preset <name>","Apply color preset"),$1("promake list","List available modules"),$1("promake discover --json","Project info for AI"),$1("promake seo","Sync SEO meta tags"),$1("promake usage","Show this guide"),$1("promake <command> --help","Command-specific help"),console.log()}var PR=new E0("info").description("Show detailed information about a module").argument("<module>","Module name").option("--json","Output as JSON").action(async(D,F)=>{let X=H2(`Fetching ${D}...`).start();try{let J=await C1(D);if(X.stop(),!J)console.error(M.red(`Module not found: ${D}`)),process.exit(1);if(F.json){console.log(JSON.stringify(J,null,2));return}if(console.log(""),console.log(M.bold.cyan(J.name)),console.log(M.dim("─".repeat(J.name.length+4))),J.title)console.log(`${M.gray("Title:")} ${J.title}`);if(console.log(`${M.gray("Type:")} ${J.type.replace("registry:","")}`),J.description)console.log(`${M.gray("Description:")} ${J.description}`);if(J.registryDependencies?.length)console.log(""),console.log(M.gray("Dependencies:")),J.registryDependencies.forEach((Q)=>{let Y=!Q.includes("-")||["button","card","input","badge","dialog","sheet","dropdown-menu","accordion","tabs","select","checkbox","slider","separator","avatar","tooltip","popover","navigation-menu","scroll-area"].includes(Q)?M.dim("(shadcn)"):"";console.log(` ${M.yellow("•")} ${Q} ${Y}`)});if(J.dependencies?.length)console.log(""),console.log(M.gray("NPM Packages:")),J.dependencies.forEach((Q)=>{console.log(` ${M.blue("•")} ${Q}`)});if(J.files?.length)console.log(""),console.log(M.gray("Files:")),J.files.forEach((Q)=>{console.log(` ${M.dim("•")} ${Q.path}`)});if(J.exports){if(console.log(""),console.log(M.gray("Exports:")),J.exports.types?.length)console.log(` ${M.magenta("Types:")} ${J.exports.types.join(", ")}`);if(J.exports.variables?.length)console.log(` ${M.green("Components:")} ${J.exports.variables.join(", ")}`)}if(J.route)console.log(""),console.log(M.gray("Route:")),console.log(` ${M.cyan("Path:")} ${J.route.path}`),console.log(` ${M.cyan("Component:")} ${J.route.componentName}`);if(J.usage)console.log(""),console.log(M.gray("Usage:")),console.log(M.dim("─".repeat(40))),console.log(J.usage);console.log("")}catch(J){if(X.fail("Failed to fetch module info"),J instanceof Error)console.error(M.red(J.message));process.exit(1)}});var xR={name:"@promakeai/cli",version:"0.0.6",type:"module",bin:{promake:"dist/index.js"},files:["dist/index.js","dist/registry","template"],scripts:{dev:"bun run src/index.ts","dev:app":"cd dev && bun run dev","dev:populate":"bun run scripts/populate-dev.ts","dev:fresh":"bun run dev:populate && bun run dev:app","playground:create":"rm -rf playground && bun run dev -- create playground --template empty --pm bun","playground:reset":"bun run playground:create","playground:add":"cd playground && bun run ../src/index.ts add","playground:ecommerce":"rm -rf playground && bun run dev -- create playground --template ecommerce --pm bun",build:"bun run build:cli && bun run build:registry","build:cli":"bun build src/index.ts --outdir dist --target node --minify","build:registry":"bun run scripts/build-registry.ts",typecheck:"tsc --noEmit",prepublishOnly:"bun run build",test:"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage",release:"bun run build && npm publish --access public"},dependencies:{"adm-zip":"^0.5.16",archiver:"^7.0.1",chalk:"^5.3.0",commander:"^12.1.0",culori:"^4.0.2","fs-extra":"^11.2.0",glob:"^11.0.0",ora:"^8.1.1",prompts:"^2.4.2"},devDependencies:{"@types/archiver":"^7.0.0","@types/bun":"^1.1.14","@types/culori":"^4.0.1","@types/fs-extra":"^11.0.4","@types/node":"^22.10.2","@types/prompts":"^2.4.9",typescript:"^5.7.2"}};var f2=new E0;f2.name("promake").description(`Modular React template CLI - Build React apps with pre-built components
283
+ `)),$1("promake create <name>","Create new project"),$1("promake add <modules...>","Add modules"),$1("promake add <page> --module-name <name>","Add page with custom name"),$1("promake remove <modules...>","Remove modules"),$1("promake sync","Sync from promake.json"),$1("promake theme --list","List theme options"),$1("promake theme --preset <name>","Apply color preset"),$1("promake list","List available modules"),$1("promake discover --json","Project info for AI"),$1("promake seo","Sync SEO meta tags"),$1("promake usage","Show this guide"),$1("promake <command> --help","Command-specific help"),console.log()}var PR=new E0("info").description("Show detailed information about a module").argument("<module>","Module name").option("--json","Output as JSON").action(async(D,F)=>{let X=H2(`Fetching ${D}...`).start();try{let J=await C1(D);if(X.stop(),!J)console.error(M.red(`Module not found: ${D}`)),process.exit(1);if(F.json){console.log(JSON.stringify(J,null,2));return}if(console.log(""),console.log(M.bold.cyan(J.name)),console.log(M.dim("─".repeat(J.name.length+4))),J.title)console.log(`${M.gray("Title:")} ${J.title}`);if(console.log(`${M.gray("Type:")} ${J.type.replace("registry:","")}`),J.description)console.log(`${M.gray("Description:")} ${J.description}`);if(J.registryDependencies?.length)console.log(""),console.log(M.gray("Dependencies:")),J.registryDependencies.forEach((Q)=>{let Y=!Q.includes("-")||["button","card","input","badge","dialog","sheet","dropdown-menu","accordion","tabs","select","checkbox","slider","separator","avatar","tooltip","popover","navigation-menu","scroll-area"].includes(Q)?M.dim("(shadcn)"):"";console.log(` ${M.yellow("•")} ${Q} ${Y}`)});if(J.dependencies?.length)console.log(""),console.log(M.gray("NPM Packages:")),J.dependencies.forEach((Q)=>{console.log(` ${M.blue("•")} ${Q}`)});if(J.files?.length)console.log(""),console.log(M.gray("Files:")),J.files.forEach((Q)=>{console.log(` ${M.dim("•")} ${Q.path}`)});if(J.exports){if(console.log(""),console.log(M.gray("Exports:")),J.exports.types?.length)console.log(` ${M.magenta("Types:")} ${J.exports.types.join(", ")}`);if(J.exports.variables?.length)console.log(` ${M.green("Components:")} ${J.exports.variables.join(", ")}`)}if(J.route)console.log(""),console.log(M.gray("Route:")),console.log(` ${M.cyan("Path:")} ${J.route.path}`),console.log(` ${M.cyan("Component:")} ${J.route.componentName}`);if(J.usage)console.log(""),console.log(M.gray("Usage:")),console.log(M.dim("─".repeat(40))),console.log(J.usage);console.log("")}catch(J){if(X.fail("Failed to fetch module info"),J instanceof Error)console.error(M.red(J.message));process.exit(1)}});var xR={name:"@promakeai/cli",version:"0.1.0",type:"module",bin:{promake:"dist/index.js"},files:["dist/index.js","dist/registry","template"],scripts:{dev:"bun run src/index.ts","dev:app":"cd dev && bun run dev","dev:populate":"bun run scripts/populate-dev.ts","dev:fresh":"bun run dev:populate && bun run dev:app","playground:create":"rm -rf playground && bun run dev -- create playground --template empty --pm bun","playground:reset":"bun run playground:create","playground:add":"cd playground && bun run ../src/index.ts add","playground:ecommerce":"rm -rf playground && bun run dev -- create playground --template ecommerce --pm bun",build:"bun run build:cli && bun run build:registry","build:cli":"bun build src/index.ts --outdir dist --target node --minify","build:registry":"bun run scripts/build-registry.ts",typecheck:"tsc --noEmit",prepublishOnly:"bun run build",test:"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage",release:"bun run build && npm publish --access public"},dependencies:{"adm-zip":"^0.5.16",archiver:"^7.0.1",chalk:"^5.3.0",commander:"^12.1.0",culori:"^4.0.2","fs-extra":"^11.2.0",glob:"^11.0.0",ora:"^8.1.1",prompts:"^2.4.2"},devDependencies:{"@types/archiver":"^7.0.0","@types/bun":"^1.1.14","@types/culori":"^4.0.1","@types/fs-extra":"^11.0.4","@types/node":"^22.10.2","@types/prompts":"^2.4.9",typescript:"^5.7.2"}};var f2=new E0;f2.name("promake").description(`Modular React template CLI - Build React apps with pre-built components
284
284
 
285
285
  Quick Start:
286
286
  $ promake create my-app
@@ -3,7 +3,9 @@
3
3
  "type": "registry:page",
4
4
  "title": "About Page",
5
5
  "description": "Company/personal about page with hero section, mission statement, team member cards with photos, company timeline/history, values section with icons, and statistics counters. Responsive layout with alternating content sections. Supports both corporate and personal portfolio styles.",
6
- "registryDependencies": [],
6
+ "registryDependencies": [
7
+ "animations"
8
+ ],
7
9
  "usage": "import { AboutPage } from '@/modules/about-page';\n\n<Route path=\"/about\" element={<AboutPage />} />\n\n• Sections: hero, mission, team, timeline, values, stats\n• Edit content in lang/en.json\n• Wrapped with Layout component",
8
10
  "route": {
9
11
  "path": "/about",
@@ -26,7 +28,7 @@
26
28
  "path": "about-page/lang/en.json",
27
29
  "type": "registry:lang",
28
30
  "target": "$modules$/about-page/lang/en.json",
29
- "content": "{\r\n \"title\": \"About Us\",\r\n \"subtitle\": \"AI will customize this about page subtitle based on your site. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyTitle\": \"Our Story\",\r\n \"storyP1\": \"This is placeholder text for your story. AI will replace this with your actual background and journey. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyP2\": \"Placeholder text for the second paragraph of your story. AI will customize this based on your journey and values. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"missionTitle\": \"Our Mission\",\r\n \"missionDesc\": \"AI will customize this mission description based on your goals. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"valuesTitle\": \"Our Values\",\r\n \"valuesDesc\": \"This values description will be replaced by AI with your actual values. Duis aute irure dolor in reprehenderit.\",\r\n \"teamTitle\": \"Our Team\",\r\n \"teamDesc\": \"Placeholder team description. AI will customize this based on your team structure and culture.\",\r\n \"qualityTitle\": \"Quality First\",\r\n \"qualityDesc\": \"AI will replace this quality description with relevant information about your quality standards.\",\r\n \"customersValue\": \"500+\",\r\n \"customersLabel\": \"Happy Customers\",\r\n \"projectsValue\": \"1000+\",\r\n \"projectsLabel\": \"Projects Completed\",\r\n \"experienceValue\": \"10+\",\r\n \"experienceLabel\": \"Years Experience\",\r\n \"satisfactionValue\": \"99%\",\r\n \"satisfactionLabel\": \"Client Satisfaction\",\r\n \"ctaTitle\": \"Ready to Work Together?\",\r\n \"ctaDesc\": \"AI will customize this CTA description based on your site goals. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"\r\n}\r\n"
31
+ "content": "{\r\n \"title\": \"About Us\",\r\n \"subtitle\": \"Ask Promake to customize this about page subtitle based on your site. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyTitle\": \"Our Story\",\r\n \"storyP1\": \"This is placeholder text for your story. Ask Promake to replace this with your actual background and journey. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyP2\": \"Placeholder text for the second paragraph of your story. Ask Promake to customize this based on your journey and values. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"missionTitle\": \"Our Mission\",\r\n \"missionDesc\": \"Ask Promake to customize this mission description based on your goals. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"valuesTitle\": \"Our Values\",\r\n \"valuesDesc\": \"This values description will be replaced by Promake with your actual values. Duis aute irure dolor in reprehenderit.\",\r\n \"teamTitle\": \"Our Team\",\r\n \"teamDesc\": \"Placeholder team description. Ask Promake to customize this based on your team structure and culture.\",\r\n \"qualityTitle\": \"Quality First\",\r\n \"qualityDesc\": \"Ask Promake to replace this quality description with relevant information about your quality standards.\",\r\n \"customersValue\": \"500+\",\r\n \"customersLabel\": \"Happy Customers\",\r\n \"projectsValue\": \"1000+\",\r\n \"projectsLabel\": \"Projects Completed\",\r\n \"experienceValue\": \"10+\",\r\n \"experienceLabel\": \"Years Experience\",\r\n \"satisfactionValue\": \"99%\",\r\n \"satisfactionLabel\": \"Client Satisfaction\",\r\n \"ctaTitle\": \"Ready to Work Together?\",\r\n \"ctaDesc\": \"Ask Promake to customize this CTA description based on your site goals. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"\r\n}\r\n"
30
32
  },
31
33
  {
32
34
  "path": "about-page/lang/tr.json",
@@ -16,13 +16,13 @@
16
16
  "path": "about-section/about-section.tsx",
17
17
  "type": "registry:component",
18
18
  "target": "$modules$/about-section/about-section.tsx",
19
- "content": "import { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface AboutSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function AboutSection({ className }: AboutSectionProps) {\r\n const { t } = useTranslation(\"about-section\");\r\n\r\n const stats = [\r\n { value: t(\"stat1Value\", \"500+\"), label: t(\"stat1Label\", \"Happy Clients\") },\r\n { value: t(\"stat2Value\", \"1000+\"), label: t(\"stat2Label\", \"Projects Completed\") },\r\n { value: t(\"stat3Value\", \"99%\"), label: t(\"stat3Label\", \"Satisfaction Rate\") },\r\n { value: t(\"stat4Value\", \"15+\"), label: t(\"stat4Label\", \"Years Experience\") },\r\n ];\r\n\r\n const companies = [\"Google\", \"Microsoft\", \"Amazon\", \"Apple\", \"Meta\"];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {/* Header */}\r\n <div className=\"grid md:grid-cols-2 gap-8 mb-12\">\r\n <h2 className=\"text-3xl md:text-4xl lg:text-5xl font-bold\">\r\n {t(\"title\", \"About Our Company\")}\r\n </h2>\r\n <p className=\"text-muted-foreground text-lg\">\r\n {t(\"description\", \"We are a passionate team dedicated to creating innovative solutions that empower businesses to thrive in the digital age. Our mission is to deliver excellence in everything we do.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Images Grid */}\r\n <div className=\"grid lg:grid-cols-3 gap-6 mb-16\">\r\n {/* Main Image */}\r\n <div className=\"lg:col-span-2\">\r\n <div className=\"aspect-[16/10] rounded-2xl bg-muted overflow-hidden\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"mainImageAlt\", \"Our team\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Side Column */}\r\n <div className=\"flex flex-col gap-6\">\r\n {/* Info Card */}\r\n <div className=\"bg-muted rounded-2xl p-6 flex flex-col justify-between flex-1\">\r\n <div>\r\n <h3 className=\"text-lg font-semibold mb-2\">\r\n {t(\"cardTitle\", \"Our Mission\")}\r\n </h3>\r\n <p className=\"text-muted-foreground text-sm mb-4\">\r\n {t(\"cardDescription\", \"Providing businesses with effective tools to improve workflows, boost efficiency, and encourage sustainable growth.\")}\r\n </p>\r\n </div>\r\n <Button variant=\"outline\" asChild className=\"w-fit\">\r\n <Link to=\"/about\">{t(\"cardButton\", \"Learn More\")}</Link>\r\n </Button>\r\n </div>\r\n\r\n {/* Secondary Image */}\r\n <div className=\"aspect-square rounded-2xl bg-muted overflow-hidden flex-1\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"secondaryImageAlt\", \"Our office\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Companies */}\r\n <div className=\"text-center mb-16\">\r\n <p className=\"text-muted-foreground mb-6\">\r\n {t(\"companiesTitle\", \"Trusted by leading companies worldwide\")}\r\n </p>\r\n <div className=\"flex flex-wrap justify-center items-center gap-8 md:gap-12\">\r\n {companies.map((company) => (\r\n <span\r\n key={company}\r\n className=\"text-xl md:text-2xl font-semibold text-muted-foreground/50\"\r\n >\r\n {company}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Stats */}\r\n <div className=\"bg-muted rounded-2xl p-8 md:p-12\">\r\n <div className=\"text-center md:text-left mb-8\">\r\n <h3 className=\"text-2xl md:text-3xl font-bold mb-2\">\r\n {t(\"statsTitle\", \"Our Achievements\")}\r\n </h3>\r\n <p className=\"text-muted-foreground max-w-xl\">\r\n {t(\"statsDescription\", \"Numbers that reflect our commitment to excellence and client satisfaction.\")}\r\n </p>\r\n </div>\r\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-8\">\r\n {stats.map((stat, index) => (\r\n <div key={index} className=\"text-center\">\r\n <div className=\"text-3xl md:text-4xl lg:text-5xl font-bold text-primary mb-2\">\r\n {stat.value}\r\n </div>\r\n <p className=\"text-sm md:text-base text-muted-foreground\">\r\n {stat.label}\r\n </p>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
19
+ "content": "import { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface AboutSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function AboutSection({ className }: AboutSectionProps) {\r\n const { t } = useTranslation(\"about-section\");\r\n\r\n const stats = [\r\n { value: t(\"stat1Value\", \"500+\"), label: t(\"stat1Label\", \"Happy Clients\") },\r\n { value: t(\"stat2Value\", \"1000+\"), label: t(\"stat2Label\", \"Projects Completed\") },\r\n { value: t(\"stat3Value\", \"99%\"), label: t(\"stat3Label\", \"Satisfaction Rate\") },\r\n { value: t(\"stat4Value\", \"15+\"), label: t(\"stat4Label\", \"Years Experience\") },\r\n ];\r\n\r\n const companies = [\"Google\", \"Microsoft\", \"Amazon\", \"Apple\", \"Meta\"];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {/* Header */}\r\n <div className=\"grid md:grid-cols-2 gap-8 mb-12\">\r\n <h2 className=\"text-3xl md:text-4xl lg:text-5xl font-bold\">\r\n {t(\"title\", \"About Our Company\")}\r\n </h2>\r\n <p className=\"text-muted-foreground text-lg\">\r\n {t(\"description\", \"We are a passionate team dedicated to creating innovative solutions that empower businesses to thrive in the digital age. Our mission is to deliver excellence in everything we do.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Images Grid */}\r\n <div className=\"grid lg:grid-cols-3 gap-6 mb-16\">\r\n {/* Main Image */}\r\n <div className=\"lg:col-span-2\">\r\n <div className=\"aspect-[16/10] lg:aspect-auto lg:h-full rounded-2xl bg-muted overflow-hidden\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"mainImageAlt\", \"Our team\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Side Column */}\r\n <div className=\"flex flex-col gap-6\">\r\n {/* Info Card */}\r\n <div className=\"bg-muted rounded-2xl p-6 flex flex-col justify-between flex-1\">\r\n <div>\r\n <h3 className=\"text-lg font-semibold mb-2\">\r\n {t(\"cardTitle\", \"Our Mission\")}\r\n </h3>\r\n <p className=\"text-muted-foreground text-sm mb-4\">\r\n {t(\"cardDescription\", \"Providing businesses with effective tools to improve workflows, boost efficiency, and encourage sustainable growth.\")}\r\n </p>\r\n </div>\r\n <Button variant=\"outline\" asChild className=\"w-fit\">\r\n <Link to=\"/about\">{t(\"cardButton\", \"Learn More\")}</Link>\r\n </Button>\r\n </div>\r\n\r\n {/* Secondary Image */}\r\n <div className=\"aspect-square rounded-2xl bg-muted overflow-hidden flex-1\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"secondaryImageAlt\", \"Our office\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Companies */}\r\n <div className=\"text-center mb-16\">\r\n <p className=\"text-muted-foreground mb-6\">\r\n {t(\"companiesTitle\", \"Trusted by leading companies worldwide\")}\r\n </p>\r\n <div className=\"flex flex-wrap justify-center items-center gap-8 md:gap-12\">\r\n {companies.map((company) => (\r\n <span\r\n key={company}\r\n className=\"text-xl md:text-2xl font-semibold text-muted-foreground/50\"\r\n >\r\n {company}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Stats */}\r\n <div className=\"bg-muted rounded-2xl p-8 md:p-12\">\r\n <div className=\"text-center md:text-left mb-8\">\r\n <h3 className=\"text-2xl md:text-3xl font-bold mb-2\">\r\n {t(\"statsTitle\", \"Our Achievements\")}\r\n </h3>\r\n <p className=\"text-muted-foreground max-w-xl\">\r\n {t(\"statsDescription\", \"Numbers that reflect our commitment to excellence and client satisfaction.\")}\r\n </p>\r\n </div>\r\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-8\">\r\n {stats.map((stat, index) => (\r\n <div key={index} className=\"text-center\">\r\n <div className=\"text-3xl md:text-4xl lg:text-5xl font-bold text-primary mb-2\">\r\n {stat.value}\r\n </div>\r\n <p className=\"text-sm md:text-base text-muted-foreground\">\r\n {stat.label}\r\n </p>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
20
20
  },
21
21
  {
22
22
  "path": "about-section/lang/en.json",
23
23
  "type": "registry:lang",
24
24
  "target": "$modules$/about-section/lang/en.json",
25
- "content": "{\r\n \"title\": \"About Us\",\r\n \"description\": \"AI will customize this description based on your site and audience. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"mainImageAlt\": \"Our team\",\r\n \"secondaryImageAlt\": \"Our office\",\r\n \"cardTitle\": \"Our Mission\",\r\n \"cardDescription\": \"This mission description will be customized by AI based on your goals and values. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"cardButton\": \"Learn More\",\r\n \"companiesTitle\": \"AI will customize this companies title\",\r\n \"statsTitle\": \"Our Achievements\",\r\n \"statsDescription\": \"This stats description will be replaced by AI with relevant information about your milestones.\",\r\n \"stat1Value\": \"500+\",\r\n \"stat1Label\": \"Happy Clients\",\r\n \"stat2Value\": \"1000+\",\r\n \"stat2Label\": \"Projects Completed\",\r\n \"stat3Value\": \"99%\",\r\n \"stat3Label\": \"Satisfaction Rate\",\r\n \"stat4Value\": \"15+\",\r\n \"stat4Label\": \"Years Experience\"\r\n}\r\n"
25
+ "content": "{\r\n \"title\": \"About Us\",\r\n \"description\": \"Ask Promake to customize this description based on your site and audience. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"mainImageAlt\": \"Our team\",\r\n \"secondaryImageAlt\": \"Our office\",\r\n \"cardTitle\": \"Our Mission\",\r\n \"cardDescription\": \"This mission description will be customized by Promake based on your goals and values. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"cardButton\": \"Learn More\",\r\n \"companiesTitle\": \"Ask Promake to customize this companies title\",\r\n \"statsTitle\": \"Our Achievements\",\r\n \"statsDescription\": \"This stats description will be replaced by Promake with relevant information about your milestones.\",\r\n \"stat1Value\": \"500+\",\r\n \"stat1Label\": \"Happy Clients\",\r\n \"stat2Value\": \"1000+\",\r\n \"stat2Label\": \"Projects Completed\",\r\n \"stat3Value\": \"99%\",\r\n \"stat3Label\": \"Satisfaction Rate\",\r\n \"stat4Value\": \"15+\",\r\n \"stat4Label\": \"Years Experience\"\r\n}\r\n"
26
26
  },
27
27
  {
28
28
  "path": "about-section/lang/tr.json",
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "announcement-bar",
3
+ "type": "registry:component",
4
+ "title": "Announcement Bar",
5
+ "description": "Top notification bar with multiple style variants (default, primary, warning, success, gradient), dismissible with localStorage persistence, customizable icon, and link support. Slide animation.",
6
+ "dependencies": [
7
+ "motion"
8
+ ],
9
+ "registryDependencies": [],
10
+ "usage": "import { AnnouncementBar } from '@/modules/announcement-bar';\n\n<AnnouncementBar\n message=\"New features available!\"\n linkText=\"Learn more\"\n linkUrl=\"/blog/new-features\"\n variant=\"gradient\"\n icon=\"sparkles\"\n/>\n\n• Variants: default, primary, warning, success, gradient\n• Icons: sparkles, megaphone, gift, zap, none\n• Dismissible with storage key",
11
+ "files": [
12
+ {
13
+ "path": "announcement-bar/index.ts",
14
+ "type": "registry:index",
15
+ "target": "$modules$/announcement-bar/index.ts",
16
+ "content": "export { AnnouncementBar } from \"./announcement-bar\";\r\n"
17
+ },
18
+ {
19
+ "path": "announcement-bar/announcement-bar.tsx",
20
+ "type": "registry:component",
21
+ "target": "$modules$/announcement-bar/announcement-bar.tsx",
22
+ "content": "import { useState, useEffect } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { X, ArrowRight, Sparkles, Megaphone, Gift, Zap } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { motion, AnimatePresence } from \"motion/react\";\r\n\r\ntype BarVariant = \"default\" | \"primary\" | \"warning\" | \"success\" | \"gradient\";\r\n\r\ninterface AnnouncementBarProps {\r\n message?: string;\r\n linkText?: string;\r\n linkUrl?: string;\r\n variant?: BarVariant;\r\n icon?: \"sparkles\" | \"megaphone\" | \"gift\" | \"zap\" | \"none\";\r\n dismissible?: boolean;\r\n storageKey?: string;\r\n className?: string;\r\n}\r\n\r\nconst icons = {\r\n sparkles: Sparkles,\r\n megaphone: Megaphone,\r\n gift: Gift,\r\n zap: Zap,\r\n none: null,\r\n};\r\n\r\nconst variantStyles: Record<BarVariant, string> = {\r\n default: \"bg-muted text-muted-foreground\",\r\n primary: \"bg-primary text-primary-foreground\",\r\n warning: \"bg-yellow-500 text-yellow-950\",\r\n success: \"bg-green-500 text-white\",\r\n gradient: \"bg-gradient-to-r from-primary via-purple-500 to-pink-500 text-white\",\r\n};\r\n\r\nexport function AnnouncementBar({\r\n message,\r\n linkText,\r\n linkUrl = \"#\",\r\n variant = \"primary\",\r\n icon = \"sparkles\",\r\n dismissible = true,\r\n storageKey = \"announcement-bar-dismissed\",\r\n className,\r\n}: AnnouncementBarProps) {\r\n const { t } = useTranslation(\"announcement-bar\");\r\n const [isVisible, setIsVisible] = useState(false);\r\n\r\n const displayMessage = message || t(\"message\", \"Exciting news! Check out our latest features.\");\r\n const displayLinkText = linkText || t(\"linkText\", \"Learn more\");\r\n\r\n useEffect(() => {\r\n if (dismissible) {\r\n const dismissed = localStorage.getItem(storageKey);\r\n if (!dismissed) {\r\n setIsVisible(true);\r\n }\r\n } else {\r\n setIsVisible(true);\r\n }\r\n }, [dismissible, storageKey]);\r\n\r\n const handleDismiss = () => {\r\n if (dismissible) {\r\n localStorage.setItem(storageKey, \"true\");\r\n }\r\n setIsVisible(false);\r\n };\r\n\r\n const IconComponent = icons[icon];\r\n\r\n return (\r\n <AnimatePresence>\r\n {isVisible && (\r\n <motion.div\r\n initial={{ height: 0, opacity: 0 }}\r\n animate={{ height: \"auto\", opacity: 1 }}\r\n exit={{ height: 0, opacity: 0 }}\r\n transition={{ duration: 0.3 }}\r\n className={cn(\r\n \"relative overflow-hidden\",\r\n variantStyles[variant],\r\n className\r\n )}\r\n >\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"flex items-center justify-center gap-2 py-2.5 text-sm font-medium\">\r\n {IconComponent && (\r\n <IconComponent className=\"h-4 w-4 flex-shrink-0\" />\r\n )}\r\n <span className=\"text-center\">{displayMessage}</span>\r\n {linkUrl && (\r\n <Link\r\n to={linkUrl}\r\n className=\"inline-flex items-center gap-1 font-semibold hover:underline underline-offset-2\"\r\n >\r\n {displayLinkText}\r\n <ArrowRight className=\"h-3 w-3\" />\r\n </Link>\r\n )}\r\n {dismissible && (\r\n <button\r\n onClick={handleDismiss}\r\n className=\"absolute right-4 p-1 rounded hover:bg-black/10 transition-colors\"\r\n aria-label={t(\"dismiss\", \"Dismiss\")}\r\n >\r\n <X className=\"h-4 w-4\" />\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n );\r\n}\r\n"
23
+ },
24
+ {
25
+ "path": "announcement-bar/lang/en.json",
26
+ "type": "registry:lang",
27
+ "target": "$modules$/announcement-bar/lang/en.json",
28
+ "content": "{\r\n \"message\": \"Ask Promake to customize this announcement message based on your news or promotion.\",\r\n \"linkText\": \"Learn more\",\r\n \"dismiss\": \"Dismiss\"\r\n}\r\n"
29
+ },
30
+ {
31
+ "path": "announcement-bar/lang/tr.json",
32
+ "type": "registry:lang",
33
+ "target": "$modules$/announcement-bar/lang/tr.json",
34
+ "content": "{\r\n \"message\": \"Heyecan verici haber! En son özelliklerimize göz atın.\",\r\n \"linkText\": \"Daha fazla\",\r\n \"dismiss\": \"Kapat\"\r\n}\r\n"
35
+ }
36
+ ],
37
+ "exports": {
38
+ "types": [],
39
+ "variables": [
40
+ "AnnouncementBar"
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "auth-core",
3
+ "type": "registry:module",
4
+ "title": "Auth Core",
5
+ "description": "Core authentication system with Zustand store, JWT token management with auto-refresh. No pages included - use separate page modules (login-page, register-page, etc.) for UI.",
6
+ "dependencies": [
7
+ "zustand"
8
+ ],
9
+ "registryDependencies": [
10
+ "api"
11
+ ],
12
+ "usage": "import { useAuth, useAuthStore } from '@/modules/auth-core';\n\n// Use the hook for auth operations\nconst {\n user,\n tokens,\n isAuthenticated,\n login,\n register,\n confirmEmail,\n forgotPassword,\n resetPassword,\n logout,\n api,\n} = useAuth();\n\n// Auth operations\nawait login(username, password);\nawait register(username, email, password);\nawait confirmEmail(username, code);\nawait forgotPassword(username);\nawait resetPassword(username, code, newPassword);\nlogout();\n\n// Direct store access for state management\nconst setAuth = useAuthStore((state) => state.setAuth);\nconst clearAuth = useAuthStore((state) => state.clearAuth);\n\n• Installed at: src/modules/auth-core/\n• Provides: useAuth hook, useAuthStore\n• Auto token refresh before expiry\n• 401 interceptor with automatic retry\n• Uses customerClient from api module internally",
13
+ "files": [
14
+ {
15
+ "path": "auth-core/index.ts",
16
+ "type": "registry:index",
17
+ "target": "$modules$/auth-core/index.ts",
18
+ "content": "// Store\r\nexport { useAuthStore } from \"./auth-store\";\r\nexport type { User, AuthTokens } from \"./auth-store\";\r\n\r\n// Hook\r\nexport { useAuth } from \"./use-auth\";\r\n"
19
+ },
20
+ {
21
+ "path": "auth-core/auth-store.ts",
22
+ "type": "registry:store",
23
+ "target": "$modules$/auth-core/auth-store.ts",
24
+ "content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\n\r\nexport interface User {\r\n username: string;\r\n email?: string;\r\n}\r\n\r\nexport interface AuthTokens {\r\n accessToken: string;\r\n refreshToken?: string;\r\n idToken?: string;\r\n encryptionKey?: string;\r\n expiresAt?: number; // Unix timestamp in milliseconds\r\n}\r\n\r\ninterface AuthState {\r\n user: User | null;\r\n tokens: AuthTokens | null;\r\n isAuthenticated: boolean;\r\n setAuth: (user: User, tokens: AuthTokens) => void;\r\n updateTokens: (tokens: AuthTokens) => void;\r\n clearAuth: () => void;\r\n isTokenExpired: () => boolean;\r\n getTimeUntilExpiry: () => number | null;\r\n}\r\n\r\nexport const useAuthStore = create<AuthState>()(\r\n persist(\r\n (set, get) => ({\r\n user: null,\r\n tokens: null,\r\n isAuthenticated: false,\r\n\r\n setAuth: (user, tokens) => set({ user, tokens, isAuthenticated: true }),\r\n\r\n updateTokens: (tokens) => set({ tokens }),\r\n\r\n clearAuth: () =>\r\n set({ user: null, tokens: null, isAuthenticated: false }),\r\n\r\n isTokenExpired: () => {\r\n const { tokens } = get();\r\n if (!tokens?.expiresAt) return false;\r\n // Consider token expired 30 seconds before actual expiry for safety margin\r\n return Date.now() >= tokens.expiresAt - 30000;\r\n },\r\n\r\n getTimeUntilExpiry: () => {\r\n const { tokens } = get();\r\n if (!tokens?.expiresAt) return null;\r\n return tokens.expiresAt - Date.now();\r\n },\r\n }),\r\n { name: \"auth-storage\" },\r\n ),\r\n);\r\n"
25
+ },
26
+ {
27
+ "path": "auth-core/use-auth.ts",
28
+ "type": "registry:hook",
29
+ "target": "$modules$/auth-core/use-auth.ts",
30
+ "content": "import { useCallback, useEffect, useRef } from \"react\";\r\nimport {\r\n useAuthStore,\r\n type User,\r\n type AuthTokens,\r\n} from \"@/modules/auth-core/auth-store\";\r\nimport { customerClient } from \"@/modules/api/customer-client\";\r\n\r\n// Refresh token 1 minute before expiry\r\nconst REFRESH_BUFFER_MS = 60 * 1000;\r\n\r\nexport function useAuth() {\r\n const {\r\n user,\r\n tokens,\r\n isAuthenticated,\r\n setAuth,\r\n updateTokens,\r\n clearAuth,\r\n isTokenExpired,\r\n getTimeUntilExpiry,\r\n } = useAuthStore();\r\n\r\n const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const isRefreshingRef = useRef(false);\r\n\r\n // Refresh token using the refresh token\r\n const refreshAccessToken = useCallback(async (): Promise<boolean> => {\r\n const currentTokens = useAuthStore.getState().tokens;\r\n\r\n // Don't refresh if no refresh token exists\r\n if (!currentTokens?.refreshToken || isRefreshingRef.current) {\r\n console.log(\"⚠️ No refresh token available, skipping refresh\");\r\n return false;\r\n }\r\n\r\n isRefreshingRef.current = true;\r\n\r\n try {\r\n // Make a refresh request using the axios instance directly\r\n const response = await customerClient.axios.post<{\r\n accessToken: string;\r\n refreshToken?: string;\r\n expiresIn?: number;\r\n }>(\"/auth/refresh\", {\r\n refreshToken: currentTokens.refreshToken,\r\n });\r\n\r\n const { accessToken, refreshToken, expiresIn } = response.data;\r\n\r\n // Validate response has required data\r\n if (!accessToken) {\r\n console.error(\"❌ Refresh response missing accessToken\");\r\n return false;\r\n }\r\n\r\n const newTokens: AuthTokens = {\r\n accessToken,\r\n refreshToken: refreshToken || currentTokens.refreshToken,\r\n idToken: currentTokens.idToken, // Preserve existing idToken\r\n encryptionKey: currentTokens.encryptionKey, // Preserve existing encryptionKey\r\n expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : undefined,\r\n };\r\n\r\n customerClient.setToken(accessToken);\r\n updateTokens(newTokens);\r\n\r\n console.log(\"✅ Token refreshed successfully\");\r\n return true;\r\n } catch (error) {\r\n console.error(\"❌ Token refresh failed:\", error);\r\n // DON'T clear auth on refresh failure - just return false\r\n // User can still use their existing token until it expires\r\n return false;\r\n } finally {\r\n isRefreshingRef.current = false;\r\n }\r\n }, [updateTokens]);\r\n\r\n // Schedule automatic token refresh\r\n const scheduleTokenRefresh = useCallback(() => {\r\n // Clear any existing timeout\r\n if (refreshTimeoutRef.current) {\r\n clearTimeout(refreshTimeoutRef.current);\r\n refreshTimeoutRef.current = null;\r\n }\r\n\r\n const timeUntilExpiry = getTimeUntilExpiry();\r\n\r\n // Only schedule if we have an expiry time and a refresh token\r\n if (timeUntilExpiry === null || !tokens?.refreshToken) {\r\n return;\r\n }\r\n\r\n // Calculate when to refresh (REFRESH_BUFFER_MS before expiry)\r\n const refreshIn = Math.max(timeUntilExpiry - REFRESH_BUFFER_MS, 0);\r\n\r\n // Don't schedule if expiry is too far in the future (> 24 hours)\r\n if (refreshIn > 24 * 60 * 60 * 1000) {\r\n return;\r\n }\r\n\r\n refreshTimeoutRef.current = setTimeout(async () => {\r\n const success = await refreshAccessToken();\r\n if (success) {\r\n // Reschedule for the new token\r\n scheduleTokenRefresh();\r\n }\r\n }, refreshIn);\r\n }, [getTimeUntilExpiry, tokens?.refreshToken, refreshAccessToken]);\r\n\r\n // Sync token with API client and set up refresh on mount and token changes\r\n useEffect(() => {\r\n if (tokens?.accessToken) {\r\n console.log(\"🔑 Setting token in API client\");\r\n customerClient.setToken(tokens.accessToken);\r\n\r\n // Only try to refresh if we have a refresh token AND token is expired\r\n if (isTokenExpired() && tokens.refreshToken) {\r\n console.log(\"⏰ Token expired, attempting refresh...\");\r\n refreshAccessToken().then((success) => {\r\n if (success) {\r\n scheduleTokenRefresh();\r\n } else {\r\n console.log(\"⚠️ Refresh failed, but keeping existing token\");\r\n }\r\n });\r\n } else if (tokens.refreshToken) {\r\n // Only schedule refresh if we have a refresh token\r\n scheduleTokenRefresh();\r\n }\r\n } else if (tokens && Object.keys(tokens).length === 0) {\r\n // tokens is empty object {} - this shouldn't happen, log it\r\n console.warn(\"⚠️ Tokens object is empty, this may indicate a bug\");\r\n } else {\r\n customerClient.setToken(null);\r\n }\r\n\r\n // Cleanup timeout on unmount\r\n return () => {\r\n if (refreshTimeoutRef.current) {\r\n clearTimeout(refreshTimeoutRef.current);\r\n }\r\n };\r\n }, [\r\n tokens?.accessToken,\r\n tokens?.refreshToken,\r\n isTokenExpired,\r\n refreshAccessToken,\r\n scheduleTokenRefresh,\r\n ]);\r\n\r\n // Set up axios interceptor for 401 responses (token expired during request)\r\n useEffect(() => {\r\n const interceptorId = customerClient.axios.interceptors.response.use(\r\n (response) => response,\r\n async (error) => {\r\n const originalRequest = error.config;\r\n\r\n // Skip refresh for auth endpoints to prevent infinite loops\r\n const isAuthEndpoint = originalRequest?.url?.includes(\"/auth/\");\r\n\r\n // If we get a 401 and haven't retried yet, try to refresh\r\n if (\r\n error.response?.status === 401 &&\r\n !originalRequest._retry &&\r\n tokens?.refreshToken &&\r\n !isAuthEndpoint\r\n ) {\r\n originalRequest._retry = true;\r\n\r\n const success = await refreshAccessToken();\r\n if (success) {\r\n // Retry the original request with new token\r\n const newTokens = useAuthStore.getState().tokens;\r\n if (newTokens?.accessToken) {\r\n originalRequest.headers.Authorization = `Bearer ${newTokens.accessToken}`;\r\n return customerClient.axios(originalRequest);\r\n }\r\n }\r\n }\r\n\r\n return Promise.reject(error);\r\n },\r\n );\r\n\r\n return () => {\r\n customerClient.axios.interceptors.response.eject(interceptorId);\r\n };\r\n }, [tokens?.refreshToken, refreshAccessToken]);\r\n\r\n const login = useCallback(async (username: string, password: string) => {\r\n const response = await customerClient.auth.login({ username, password });\r\n\r\n console.log(\"🔐 Login response:\", response);\r\n console.log(\"🔐 accessToken:\", response.accessToken);\r\n console.log(\"🔐 refreshToken:\", response.refreshToken);\r\n console.log(\"🔐 encryptionKey:\", response.encryptionKey);\r\n\r\n const newTokens: AuthTokens = {\r\n accessToken: response.accessToken,\r\n refreshToken: response.refreshToken,\r\n idToken: response.idToken,\r\n encryptionKey: response.encryptionKey,\r\n expiresAt: response.expiresIn\r\n ? Date.now() + response.expiresIn * 1000\r\n : undefined,\r\n };\r\n\r\n console.log(\"🔐 newTokens object:\", newTokens);\r\n\r\n const newUser: User = {\r\n username,\r\n email: (response as any).email || (response as any).user?.email,\r\n };\r\n\r\n customerClient.setToken(newTokens.accessToken);\r\n setAuth(newUser, newTokens);\r\n\r\n console.log(\r\n \"🔐 Auth set complete, checking store:\",\r\n useAuthStore.getState().tokens,\r\n );\r\n }, []);\r\n\r\n const register = useCallback(\r\n async (username: string, email: string, password: string) => {\r\n await customerClient.auth.register({ username, email, password });\r\n },\r\n [],\r\n );\r\n\r\n const confirmEmail = useCallback(async (username: string, code: string) => {\r\n await customerClient.auth.confirm({ username, code });\r\n }, []);\r\n\r\n const forgotPassword = useCallback(async (username: string) => {\r\n await customerClient.auth.forgotPassword({ username });\r\n }, []);\r\n\r\n const resetPassword = useCallback(\r\n async (username: string, code: string, newPassword: string) => {\r\n await customerClient.auth.resetPassword({ username, code, newPassword });\r\n },\r\n [],\r\n );\r\n\r\n const logout = useCallback(() => {\r\n // Clear any scheduled refresh\r\n if (refreshTimeoutRef.current) {\r\n clearTimeout(refreshTimeoutRef.current);\r\n refreshTimeoutRef.current = null;\r\n }\r\n\r\n customerClient.setToken(null);\r\n clearAuth();\r\n }, [clearAuth]);\r\n\r\n return {\r\n user,\r\n token: tokens?.accessToken ?? null,\r\n tokens,\r\n isAuthenticated,\r\n api: customerClient,\r\n login,\r\n register,\r\n confirmEmail,\r\n forgotPassword,\r\n resetPassword,\r\n logout,\r\n refreshAccessToken,\r\n };\r\n}\r\n"
31
+ }
32
+ ],
33
+ "exports": {
34
+ "types": [
35
+ "AuthTokens",
36
+ "User"
37
+ ],
38
+ "variables": [
39
+ "useAuth",
40
+ "useAuthStore"
41
+ ]
42
+ }
43
+ }
@@ -14,7 +14,7 @@
14
14
  "path": "auth/index.ts",
15
15
  "type": "registry:index",
16
16
  "target": "$modules$/auth/index.ts",
17
- "content": "// Store\r\nexport { useAuthStore } from \"./auth-store\";\r\nexport type { User, AuthTokens } from \"./auth-store\";\r\n\r\n// Hook\r\nexport { useAuth } from \"./use-auth\";\r\n\r\n// Components\r\nexport { AuthHeaderMenu } from \"./auth-header-menu\";\r\nexport { LoginPage } from \"./login-page\";\r\nexport { RegisterPage } from \"./register-page\";\r\nexport { ForgotPasswordPage } from \"./forgot-password-page\";\r\n"
17
+ "content": "// Store\r\nexport { useAuthStore } from \"./auth-store\";\r\nexport type { User, AuthTokens } from \"./auth-store\";\r\n\r\n// Hook\r\nexport { useAuth } from \"./use-auth\";\r\n\r\n// Components\r\nexport { AuthHeaderMenu } from \"./auth-header-menu\";\r\nexport { default as LoginPage } from \"./login-page\";\r\nexport { default as RegisterPage } from \"./register-page\";\r\nexport { default as ForgotPasswordPage } from \"./forgot-password-page\";\r\n"
18
18
  },
19
19
  {
20
20
  "path": "auth/auth-store.ts",
@@ -5,7 +5,8 @@
5
5
  "description": "Blog listing page with category filter tabs, search functionality, and responsive post grid. Uses PostCard component for display with grid/list view toggle. Features loading states, empty state handling, pagination, and featured post highlight at top. Includes sidebar with popular posts, categories, and newsletter signup.",
6
6
  "registryDependencies": [
7
7
  "blog-core",
8
- "post-card"
8
+ "post-card",
9
+ "animations"
9
10
  ],
10
11
  "usage": "import { BlogListPage } from '@/modules/blog-list-page';\n\n<Route path=\"/blog\" element={<BlogListPage />} />\n\n• Uses usePosts() from blog-core (Zustand)\n• Features: category tabs, search, grid/list view\n• Sidebar: popular posts, categories, newsletter",
11
12
  "route": {
@@ -23,7 +24,7 @@
23
24
  "path": "blog-list-page/blog-list-page.tsx",
24
25
  "type": "registry:page",
25
26
  "target": "$modules$/blog-list-page/blog-list-page.tsx",
26
- "content": "import { useState, useEffect } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { Search, Filter } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { FadeIn } from \"@/modules/animations\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { usePosts, useBlogCategories } from \"@/modules/blog-core\";\n\nexport function BlogListPage() {\n const { t } = useTranslation(\"blog-list-page\");\n usePageTitle({ title: t(\"title\") });\n\n const [searchParams, setSearchParams] = useSearchParams();\n const [searchTerm, setSearchTerm] = useState(\n searchParams.get(\"search\") || \"\"\n );\n const [selectedCategories, setSelectedCategories] = useState<string[]>(\n searchParams.get(\"categories\")?.split(\",\").filter(Boolean) || []\n );\n const [selectedTags, setSelectedTags] = useState<string[]>(\n searchParams.get(\"tags\")?.split(\",\").filter(Boolean) || []\n );\n const [sortBy, setSortBy] = useState(searchParams.get(\"sort\") || \"newest\");\n const [viewMode, _setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n\n const { posts, loading, error } = usePosts();\n const { categories } = useBlogCategories();\n\n const filteredPosts = posts.filter((post) => {\n if (searchTerm) {\n const searchLower = searchTerm.toLowerCase();\n if (\n !post.title.toLowerCase().includes(searchLower) &&\n !post.excerpt.toLowerCase().includes(searchLower) &&\n !post.content.toLowerCase().includes(searchLower)\n ) {\n return false;\n }\n }\n\n if (selectedCategories.length > 0) {\n const hasMatchingCategory = selectedCategories.some(\n (categorySlug) =>\n post.category === categorySlug ||\n post.categories?.some((cat) => cat.slug === categorySlug)\n );\n if (!hasMatchingCategory) return false;\n }\n\n if (selectedTags.length > 0) {\n const hasMatchingTag = selectedTags.some((tag) =>\n post.tags.includes(tag)\n );\n if (!hasMatchingTag) return false;\n }\n\n return true;\n });\n\n const sortedPosts = [...filteredPosts].sort((a, b) => {\n switch (sortBy) {\n case \"oldest\":\n return (\n new Date(a.published_at).getTime() -\n new Date(b.published_at).getTime()\n );\n case \"popular\":\n return (b.view_count || 0) - (a.view_count || 0);\n case \"reading-time\":\n return (a.read_time || 0) - (b.read_time || 0);\n case \"newest\":\n default:\n return (\n new Date(b.published_at).getTime() -\n new Date(a.published_at).getTime()\n );\n }\n });\n\n useEffect(() => {\n const params = new URLSearchParams();\n if (searchTerm) params.set(\"search\", searchTerm);\n if (selectedCategories.length)\n params.set(\"categories\", selectedCategories.join(\",\"));\n if (selectedTags.length) params.set(\"tags\", selectedTags.join(\",\"));\n if (sortBy !== \"newest\") params.set(\"sort\", sortBy);\n\n setSearchParams(params);\n }, [searchTerm, selectedCategories, selectedTags, sortBy, setSearchParams]);\n\n const handleCategoryChange = (categorySlug: string, checked: boolean) => {\n if (checked) {\n setSelectedCategories([...selectedCategories, categorySlug]);\n } else {\n setSelectedCategories(\n selectedCategories.filter((c) => c !== categorySlug)\n );\n }\n };\n\n const handleTagChange = (tag: string, checked: boolean) => {\n if (checked) {\n setSelectedTags([...selectedTags, tag]);\n } else {\n setSelectedTags(selectedTags.filter((t) => t !== tag));\n }\n };\n\n const allTags = Array.from(new Set(posts.flatMap((post) => post.tags)));\n\n const clearFilters = () => {\n setSearchTerm(\"\");\n setSelectedCategories([]);\n setSelectedTags([]);\n setSortBy(\"newest\");\n };\n\n const FilterSection = () => (\n <div className=\"space-y-6\">\n <div>\n <h3 className=\"font-semibold mb-3 flex items-center gap-2\">\n <Search className=\"h-4 w-4\" />\n {t(\"search\")}\n </h3>\n <Input\n placeholder={t(\"searchPlaceholder\")}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"categories\")}</h3>\n <div className=\"space-y-2\">\n {categories.map((category) => (\n <div key={category.slug} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`category-${category.slug}`}\n checked={selectedCategories.includes(category.slug)}\n onCheckedChange={(checked) =>\n handleCategoryChange(category.slug, checked as boolean)\n }\n />\n <label\n htmlFor={`category-${category.slug}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {category.name}\n </label>\n </div>\n ))}\n </div>\n </div>\n\n {allTags.length > 0 && (\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"tags\")}</h3>\n <div className=\"space-y-2 max-h-48 overflow-y-auto\">\n {allTags.slice(0, 20).map((tag) => (\n <div key={tag} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`tag-${tag}`}\n checked={selectedTags.includes(tag)}\n onCheckedChange={(checked) =>\n handleTagChange(tag, checked as boolean)\n }\n />\n <label\n htmlFor={`tag-${tag}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {tag}\n </label>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {(searchTerm ||\n selectedCategories.length > 0 ||\n selectedTags.length > 0) && (\n <Button variant=\"outline\" onClick={clearFilters} className=\"w-full\">\n {t(\"clearFilters\")}\n </Button>\n )}\n </div>\n );\n\n if (loading) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"animate-pulse space-y-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-48 bg-muted rounded-lg\"></div>\n ))}\n </div>\n </div>\n </Layout>\n );\n }\n\n if (error) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8 text-center\">\n <p className=\"text-destructive\">{t(\"error\")}</p>\n </div>\n </Layout>\n );\n }\n\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <FadeIn className=\"flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">{t(\"title\")}</h1>\n <p className=\"text-muted-foreground\">{t(\"subtitle\")}</p>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <Select value={sortBy} onValueChange={setSortBy}>\n <SelectTrigger className=\"w-[180px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"newest\">{t(\"sortNewest\")}</SelectItem>\n <SelectItem value=\"oldest\">{t(\"sortOldest\")}</SelectItem>\n <SelectItem value=\"popular\">{t(\"sortPopular\")}</SelectItem>\n <SelectItem value=\"reading-time\">\n {t(\"sortReadingTime\")}\n </SelectItem>\n </SelectContent>\n </Select>\n\n <Sheet>\n <SheetTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"lg:hidden\">\n <Filter className=\"h-4 w-4 mr-2\" />\n {t(\"filters\")}\n </Button>\n </SheetTrigger>\n <SheetContent>\n <SheetHeader>\n <SheetTitle>{t(\"filters\")}</SheetTitle>\n <SheetDescription>{t(\"filterDescription\")}</SheetDescription>\n </SheetHeader>\n <div className=\"mt-6\">\n <FilterSection />\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </FadeIn>\n\n <div className=\"flex flex-col lg:flex-row gap-8\">\n <div className=\"hidden lg:block w-64 flex-shrink-0\">\n <div className=\"sticky top-4\">\n <FilterSection />\n </div>\n </div>\n\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between mb-6\">\n <p className=\"text-sm text-muted-foreground\">\n {t(\"showing\")} {sortedPosts.length} {t(\"of\")} {posts.length}{\" \"}\n {t(\"posts\")}\n {searchTerm && (\n <span className=\"ml-1\">\n {t(\"for\")} \"<strong>{searchTerm}</strong>\"\n </span>\n )}\n </p>\n </div>\n\n {sortedPosts.length > 0 ? (\n <div\n className={`grid gap-6 ${\n viewMode === \"grid\"\n ? \"grid-cols-1 md:grid-cols-2 xl:grid-cols-3\"\n : \"grid-cols-1\"\n }`}\n >\n {sortedPosts.map((post) => (\n <PostCard key={post.id} post={post} layout={viewMode} />\n ))}\n </div>\n ) : (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground mb-4\">\n {t(\"noPostsFound\")}\n </p>\n <Button onClick={clearFilters} variant=\"outline\">\n {t(\"clearFilters\")}\n </Button>\n </div>\n )}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
27
+ "content": "import { useState, useEffect } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { Search, Filter } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { FadeIn } from \"@/modules/animations\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { usePosts, useBlogCategories } from \"@/modules/blog-core\";\n\nexport function BlogListPage() {\n const { t } = useTranslation(\"blog-list-page\");\n usePageTitle({ title: t(\"title\") });\n\n const [searchParams, setSearchParams] = useSearchParams();\n const [searchTerm, setSearchTerm] = useState(\n searchParams.get(\"search\") || \"\"\n );\n const [selectedCategories, setSelectedCategories] = useState<string[]>(\n searchParams.get(\"categories\")?.split(\",\").filter(Boolean) || []\n );\n const [selectedTags, setSelectedTags] = useState<string[]>(\n searchParams.get(\"tags\")?.split(\",\").filter(Boolean) || []\n );\n const [sortBy, setSortBy] = useState(searchParams.get(\"sort\") || \"newest\");\n const [viewMode, _setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n\n const { posts, loading, error } = usePosts();\n const { categories } = useBlogCategories();\n\n const filteredPosts = posts.filter((post) => {\n if (searchTerm) {\n const searchLower = searchTerm.toLowerCase();\n if (\n !post.title.toLowerCase().includes(searchLower) &&\n !post.excerpt.toLowerCase().includes(searchLower) &&\n !post.content.toLowerCase().includes(searchLower)\n ) {\n return false;\n }\n }\n\n if (selectedCategories.length > 0) {\n const hasMatchingCategory = selectedCategories.some(\n (categorySlug) =>\n post.category === categorySlug ||\n post.categories?.some((cat) => cat.slug === categorySlug)\n );\n if (!hasMatchingCategory) return false;\n }\n\n if (selectedTags.length > 0) {\n const hasMatchingTag = selectedTags.some((tag) =>\n post.tags.includes(tag)\n );\n if (!hasMatchingTag) return false;\n }\n\n return true;\n });\n\n const sortedPosts = [...filteredPosts].sort((a, b) => {\n switch (sortBy) {\n case \"oldest\":\n return (\n new Date(a.published_at).getTime() -\n new Date(b.published_at).getTime()\n );\n case \"popular\":\n return (b.view_count || 0) - (a.view_count || 0);\n case \"reading-time\":\n return (a.read_time || 0) - (b.read_time || 0);\n case \"newest\":\n default:\n return (\n new Date(b.published_at).getTime() -\n new Date(a.published_at).getTime()\n );\n }\n });\n\n useEffect(() => {\n const params = new URLSearchParams();\n if (searchTerm) params.set(\"search\", searchTerm);\n if (selectedCategories.length)\n params.set(\"categories\", selectedCategories.join(\",\"));\n if (selectedTags.length) params.set(\"tags\", selectedTags.join(\",\"));\n if (sortBy !== \"newest\") params.set(\"sort\", sortBy);\n\n setSearchParams(params);\n }, [searchTerm, selectedCategories, selectedTags, sortBy, setSearchParams]);\n\n const handleCategoryChange = (categorySlug: string, checked: boolean) => {\n if (checked) {\n setSelectedCategories([...selectedCategories, categorySlug]);\n } else {\n setSelectedCategories(\n selectedCategories.filter((c) => c !== categorySlug)\n );\n }\n };\n\n const handleTagChange = (tag: string, checked: boolean) => {\n if (checked) {\n setSelectedTags([...selectedTags, tag]);\n } else {\n setSelectedTags(selectedTags.filter((t) => t !== tag));\n }\n };\n\n const allTags = Array.from(new Set(posts.flatMap((post) => post.tags)));\n\n const clearFilters = () => {\n setSearchTerm(\"\");\n setSelectedCategories([]);\n setSelectedTags([]);\n setSortBy(\"newest\");\n };\n\n const FilterSection = () => (\n <div className=\"space-y-6\">\n <div>\n <h3 className=\"font-semibold mb-3 flex items-center gap-2\">\n <Search className=\"h-4 w-4\" />\n {t(\"search\")}\n </h3>\n <Input\n placeholder={t(\"searchPlaceholder\")}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"categories\")}</h3>\n <div className=\"space-y-2\">\n {categories.map((category) => (\n <div key={category.slug} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`category-${category.slug}`}\n checked={selectedCategories.includes(category.slug)}\n onCheckedChange={(checked) =>\n handleCategoryChange(category.slug, checked as boolean)\n }\n />\n <label\n htmlFor={`category-${category.slug}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {category.name}\n </label>\n </div>\n ))}\n </div>\n </div>\n\n {allTags.length > 0 && (\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"tags\")}</h3>\n <div className=\"space-y-2 max-h-48 overflow-y-auto\">\n {allTags.slice(0, 20).map((tag) => (\n <div key={tag} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`tag-${tag}`}\n checked={selectedTags.includes(tag)}\n onCheckedChange={(checked) =>\n handleTagChange(tag, checked as boolean)\n }\n />\n <label\n htmlFor={`tag-${tag}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {tag}\n </label>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {(searchTerm ||\n selectedCategories.length > 0 ||\n selectedTags.length > 0) && (\n <Button variant=\"outline\" onClick={clearFilters} className=\"w-full\">\n {t(\"clearFilters\")}\n </Button>\n )}\n </div>\n );\n\n if (loading) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"animate-pulse space-y-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-48 bg-muted rounded-lg\"></div>\n ))}\n </div>\n </div>\n </Layout>\n );\n }\n\n if (error) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8 text-center\">\n <p className=\"text-destructive\">{t(\"error\")}</p>\n </div>\n </Layout>\n );\n }\n\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <FadeIn className=\"flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">{t(\"title\")}</h1>\n <p className=\"text-muted-foreground\">{t(\"subtitle\")}</p>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <Select value={sortBy} onValueChange={setSortBy}>\n <SelectTrigger className=\"w-[180px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"newest\">{t(\"sortNewest\")}</SelectItem>\n <SelectItem value=\"oldest\">{t(\"sortOldest\")}</SelectItem>\n <SelectItem value=\"popular\">{t(\"sortPopular\")}</SelectItem>\n <SelectItem value=\"reading-time\">\n {t(\"sortReadingTime\")}\n </SelectItem>\n </SelectContent>\n </Select>\n\n <Sheet>\n <SheetTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"lg:hidden\">\n <Filter className=\"h-4 w-4 mr-2\" />\n {t(\"filters\")}\n </Button>\n </SheetTrigger>\n <SheetContent>\n <SheetHeader>\n <SheetTitle>{t(\"filters\")}</SheetTitle>\n <SheetDescription>{t(\"filterDescription\")}</SheetDescription>\n </SheetHeader>\n <div className=\"mt-6\">\n <FilterSection />\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </FadeIn>\n\n <div className=\"flex flex-col lg:flex-row gap-8\">\n <div className=\"hidden lg:block w-64 flex-shrink-0\">\n <div className=\"sticky top-4\">\n <FilterSection />\n </div>\n </div>\n\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between mb-6\">\n <p className=\"text-sm text-muted-foreground\">\n {t(\"showing\")} {sortedPosts.length} {t(\"of\")} {posts.length}{\" \"}\n {t(\"posts\")}\n {searchTerm && (\n <span className=\"ml-1\">\n {t(\"for\")} \"<strong>{searchTerm}</strong>\"\n </span>\n )}\n </p>\n </div>\n\n {sortedPosts.length > 0 ? (\n <div\n className={`grid gap-6 ${\n viewMode === \"grid\"\n ? \"grid-cols-1 md:grid-cols-2 xl:grid-cols-3\"\n : \"grid-cols-1\"\n }`}\n >\n {sortedPosts.map((post) => (\n <PostCard key={post.id} post={post} layout={viewMode} />\n ))}\n </div>\n ) : (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground mb-4\">\n {t(\"noPostsFound\")}\n </p>\n <Button onClick={clearFilters} variant=\"outline\">\n {t(\"clearFilters\")}\n </Button>\n </div>\n )}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default BlogListPage;\n"
27
28
  },
28
29
  {
29
30
  "path": "blog-list-page/lang/en.json",
@@ -25,7 +25,7 @@
25
25
  "path": "blog-section/lang/en.json",
26
26
  "type": "registry:lang",
27
27
  "target": "$modules$/blog-section/lang/en.json",
28
- "content": "{\r\n \"tagline\": \"Latest Updates\",\r\n \"title\": \"From Our Blog\",\r\n \"subtitle\": \"AI will customize this blog section subtitle based on your content strategy. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"viewAll\": \"View all articles\",\r\n \"readMore\": \"Read more\",\r\n \"post1Title\": \"AI will replace this blog post title\",\r\n \"post1Summary\": \"This is placeholder blog post summary text. AI will customize this based on your actual blog content. Lorem ipsum dolor sit amet.\",\r\n \"post1Category\": \"Tutorial\",\r\n \"post1Author\": \"Sarah Chen\",\r\n \"post1Date\": \"Jan 15, 2024\",\r\n \"post2Title\": \"Replace this with your second blog post title\",\r\n \"post2Summary\": \"Placeholder summary text. AI will generate appropriate blog post summaries based on your content. Sed do eiusmod tempor.\",\r\n \"post2Category\": \"Best Practices\",\r\n \"post2Author\": \"Michael Park\",\r\n \"post2Date\": \"Jan 10, 2024\",\r\n \"post3Title\": \"This blog post title will be customized by AI\",\r\n \"post3Summary\": \"AI will replace this summary with relevant content based on your blog posts. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"post3Category\": \"Design\",\r\n \"post3Author\": \"Emily Davis\",\r\n \"post3Date\": \"Jan 5, 2024\"\r\n}\r\n"
28
+ "content": "{\r\n \"tagline\": \"Latest Updates\",\r\n \"title\": \"From Our Blog\",\r\n \"subtitle\": \"Ask Promake to customize this blog section subtitle based on your content strategy. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"viewAll\": \"View all articles\",\r\n \"readMore\": \"Read more\",\r\n \"post1Title\": \"Ask Promake to replace this blog post title\",\r\n \"post1Summary\": \"This is placeholder blog post summary text. Ask Promake to customize this based on your actual blog content. Lorem ipsum dolor sit amet.\",\r\n \"post1Category\": \"Tutorial\",\r\n \"post1Author\": \"Sarah Chen\",\r\n \"post1Date\": \"Jan 15, 2024\",\r\n \"post2Title\": \"Replace this with your second blog post title\",\r\n \"post2Summary\": \"Placeholder summary text. Ask Promake to generate appropriate blog post summaries based on your content. Sed do eiusmod tempor.\",\r\n \"post2Category\": \"Best Practices\",\r\n \"post2Author\": \"Michael Park\",\r\n \"post2Date\": \"Jan 10, 2024\",\r\n \"post3Title\": \"This blog post title will be customized by Promake\",\r\n \"post3Summary\": \"Ask Promake to replace this summary with relevant content based on your blog posts. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"post3Category\": \"Design\",\r\n \"post3Author\": \"Emily Davis\",\r\n \"post3Date\": \"Jan 5, 2024\"\r\n}\r\n"
29
29
  },
30
30
  {
31
31
  "path": "blog-section/lang/tr.json",
@@ -23,19 +23,19 @@
23
23
  "path": "cart-page/cart-page.tsx",
24
24
  "type": "registry:page",
25
25
  "target": "$modules$/cart-page/cart-page.tsx",
26
- "content": "import { Link } from \"react-router\";\nimport { Trash2, Plus, Minus, ArrowLeft, ShoppingBag } from \"lucide-react\";\nimport { Layout } from \"@/components/Layout\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Input } from \"@/components/ui/input\";\nimport { useTranslation } from \"react-i18next\";\nimport { useCart, formatPrice } from \"@/modules/ecommerce-core\";\nimport constants from \"@/constants/constants.json\";\nimport { FadeIn } from \"@/modules/animations\";\n\nexport function CartPage() {\n const { t } = useTranslation(\"cart-page\");\n const { state, removeItem, updateQuantity } = useCart();\n const { items, total } = state;\n\n const currency = constants.site.currency || \"USD\";\n const shipping = 0;\n const tax = 0;\n const freeShippingThreshold = 100;\n\n const getProductPrice = (product: { price: number; sale_price?: number; on_sale?: boolean }) => {\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\n };\n\n const handleQuantityChange = (productId: number | string, newQuantity: number) => {\n if (newQuantity <= 0) {\n removeItem(productId);\n } else {\n updateQuantity(productId, newQuantity);\n }\n };\n\n const handleQuantityInputChange = (productId: number | string, value: string) => {\n const quantity = parseInt(value) || 1;\n handleQuantityChange(productId, quantity);\n };\n\n const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);\n const finalTotal = total + shipping + tax;\n\n if (items.length === 0) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"max-w-2xl mx-auto text-center py-16\">\n <div className=\"mb-8\">\n <ShoppingBag className=\"h-24 w-24 mx-auto text-muted-foreground mb-4\" />\n <h1 className=\"text-3xl font-bold mb-4\">\n {t(\"empty\", \"Your Cart is Empty\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\"emptyDescription\", \"Looks like you haven't added any items to your cart yet.\")}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t(\"continueShopping\", \"Continue Shopping\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n </Layout>\n );\n }\n\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <FadeIn className=\"flex items-center gap-4 mb-8\">\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <Link to=\"/products\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Link>\n </Button>\n <div>\n <h1 className=\"text-3xl font-bold\">{t(\"title\", \"Shopping Cart\")}</h1>\n <p className=\"text-muted-foreground\">\n {itemCount} {t(\"itemsInCart\", \"items in your cart\")}\n </p>\n </div>\n </FadeIn>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-8\">\n <div className=\"lg:col-span-2 space-y-4\">\n {items.map((item) => (\n <Card key={item.id}>\n <CardContent className=\"p-6\">\n <div className=\"flex gap-4\">\n <div className=\"w-24 h-24 flex-shrink-0\">\n <img\n src={item.product.images[0] || \"/images/placeholder.png\"}\n alt={item.product.name}\n className=\"w-full h-full object-cover rounded-lg\"\n />\n </div>\n\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold\">{item.product.name}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {item.product.category_name ||\n item.product.categories?.[0]?.name ||\n item.product.category}\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeItem(item.product.id)}\n className=\"text-destructive hover:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n onClick={() => handleQuantityChange(item.product.id, item.quantity - 1)}\n >\n <Minus className=\"h-3 w-3\" />\n </Button>\n <Input\n type=\"number\"\n value={item.quantity}\n onChange={(e) => handleQuantityInputChange(item.product.id, e.target.value)}\n className=\"w-16 text-center\"\n min=\"1\"\n />\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n onClick={() => handleQuantityChange(item.product.id, item.quantity + 1)}\n >\n <Plus className=\"h-3 w-3\" />\n </Button>\n </div>\n\n <div className=\"text-right\">\n <p className=\"font-semibold\">\n {formatPrice(getProductPrice(item.product) * item.quantity, currency)}\n </p>\n {item.quantity > 1 && (\n <p className=\"text-sm text-muted-foreground\">\n {formatPrice(getProductPrice(item.product), currency)} {t(\"each\", \"each\")}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>{t(\"orderSummary\", \"Order Summary\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex justify-between\">\n <span>\n {t(\"subtotal\", \"Subtotal\")} ({itemCount} {t(\"items\", \"items\")})\n </span>\n <span>{formatPrice(total, currency)}</span>\n </div>\n\n <div className=\"flex justify-between\">\n <span>{t(\"shipping\", \"Shipping\")}</span>\n <span>\n {shipping === 0 ? t(\"free\", \"Free\") : formatPrice(shipping, currency)}\n </span>\n </div>\n\n <div className=\"flex justify-between\">\n <span>{t(\"tax\", \"Tax\")}</span>\n <span>{formatPrice(tax, currency)}</span>\n </div>\n\n <Separator />\n\n <div className=\"flex justify-between text-lg font-semibold\">\n <span>{t(\"total\", \"Total\")}</span>\n <span>{formatPrice(finalTotal, currency)}</span>\n </div>\n\n {shipping > 0 && freeShippingThreshold && freeShippingThreshold > total && (\n <div className=\"text-sm text-muted-foreground bg-muted/50 p-3 rounded-lg\">\n {t(\"freeShippingMessage\", \"Add {{amount}} more for free shipping!\").replace(\n \"{{amount}}\",\n formatPrice(freeShippingThreshold - total, currency)\n )}\n </div>\n )}\n\n <Button asChild className=\"w-full\" size=\"lg\">\n <Link to=\"/checkout\">{t(\"proceedToCheckout\", \"Proceed to Checkout\")}</Link>\n </Button>\n\n <Button variant=\"outline\" asChild className=\"w-full\">\n <Link to=\"/products\">{t(\"continueShopping\", \"Continue Shopping\")}</Link>\n </Button>\n </CardContent>\n </Card>\n\n <Card>\n <CardContent className=\"p-4\">\n <div className=\"text-center space-y-2\">\n <div className=\"text-sm text-muted-foreground\">\n {t(\"secureCheckout\", \"Secure Checkout\")}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\"secureCheckoutDescription\", \"Your payment information is encrypted and secure\")}\n </p>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
26
+ "content": "import { Link } from \"react-router\";\nimport { Trash2, Plus, Minus, ArrowLeft, ShoppingBag } from \"lucide-react\";\nimport { Layout } from \"@/components/Layout\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Input } from \"@/components/ui/input\";\nimport { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useCart, formatPrice } from \"@/modules/ecommerce-core\";\nimport constants from \"@/constants/constants.json\";\nimport { FadeIn } from \"@/modules/animations\";\n\nexport function CartPage() {\n const { t } = useTranslation(\"cart-page\");\n usePageTitle({ title: t(\"pageTitle\", \"Shopping Cart\") });\n const { state, removeItem, updateQuantity } = useCart();\n const { items, total } = state;\n\n const currency = constants.site.currency || \"USD\";\n const shipping = 0;\n const tax = 0;\n const freeShippingThreshold = 100;\n\n const getProductPrice = (product: { price: number; sale_price?: number; on_sale?: boolean }) => {\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\n };\n\n const handleQuantityChange = (productId: number | string, newQuantity: number) => {\n if (newQuantity <= 0) {\n removeItem(productId);\n } else {\n updateQuantity(productId, newQuantity);\n }\n };\n\n const handleQuantityInputChange = (productId: number | string, value: string) => {\n const quantity = parseInt(value) || 1;\n handleQuantityChange(productId, quantity);\n };\n\n const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);\n const finalTotal = total + shipping + tax;\n\n if (items.length === 0) {\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"max-w-2xl mx-auto text-center py-16\">\n <div className=\"mb-8\">\n <ShoppingBag className=\"h-24 w-24 mx-auto text-muted-foreground mb-4\" />\n <h1 className=\"text-3xl font-bold mb-4\">\n {t(\"empty\", \"Your Cart is Empty\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\"emptyDescription\", \"Looks like you haven't added any items to your cart yet.\")}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t(\"continueShopping\", \"Continue Shopping\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n </Layout>\n );\n }\n\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <FadeIn className=\"flex items-center gap-4 mb-8\">\n <Button variant=\"ghost\" size=\"icon\" asChild>\n <Link to=\"/products\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Link>\n </Button>\n <div>\n <h1 className=\"text-3xl font-bold\">{t(\"title\", \"Shopping Cart\")}</h1>\n <p className=\"text-muted-foreground\">\n {itemCount} {t(\"itemsInCart\", \"items in your cart\")}\n </p>\n </div>\n </FadeIn>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-8\">\n <div className=\"lg:col-span-2 space-y-4\">\n {items.map((item) => (\n <Card key={item.id}>\n <CardContent className=\"p-6\">\n <div className=\"flex gap-4\">\n <div className=\"w-24 h-24 flex-shrink-0\">\n <img\n src={item.product.images[0] || \"/images/placeholder.png\"}\n alt={item.product.name}\n className=\"w-full h-full object-cover rounded-lg\"\n />\n </div>\n\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold\">{item.product.name}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {item.product.category_name ||\n item.product.categories?.[0]?.name ||\n item.product.category}\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeItem(item.product.id)}\n className=\"text-destructive hover:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n onClick={() => handleQuantityChange(item.product.id, item.quantity - 1)}\n >\n <Minus className=\"h-3 w-3\" />\n </Button>\n <Input\n type=\"number\"\n value={item.quantity}\n onChange={(e) => handleQuantityInputChange(item.product.id, e.target.value)}\n className=\"w-16 text-center\"\n min=\"1\"\n />\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n onClick={() => handleQuantityChange(item.product.id, item.quantity + 1)}\n >\n <Plus className=\"h-3 w-3\" />\n </Button>\n </div>\n\n <div className=\"text-right\">\n <p className=\"font-semibold\">\n {formatPrice(getProductPrice(item.product) * item.quantity, currency)}\n </p>\n {item.quantity > 1 && (\n <p className=\"text-sm text-muted-foreground\">\n {formatPrice(getProductPrice(item.product), currency)} {t(\"each\", \"each\")}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>{t(\"orderSummary\", \"Order Summary\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex justify-between\">\n <span>\n {t(\"subtotal\", \"Subtotal\")} ({itemCount} {t(\"items\", \"items\")})\n </span>\n <span>{formatPrice(total, currency)}</span>\n </div>\n\n <div className=\"flex justify-between\">\n <span>{t(\"shipping\", \"Shipping\")}</span>\n <span>\n {shipping === 0 ? t(\"free\", \"Free\") : formatPrice(shipping, currency)}\n </span>\n </div>\n\n <div className=\"flex justify-between\">\n <span>{t(\"tax\", \"Tax\")}</span>\n <span>{formatPrice(tax, currency)}</span>\n </div>\n\n <Separator />\n\n <div className=\"flex justify-between text-lg font-semibold\">\n <span>{t(\"total\", \"Total\")}</span>\n <span>{formatPrice(finalTotal, currency)}</span>\n </div>\n\n {shipping > 0 && freeShippingThreshold && freeShippingThreshold > total && (\n <div className=\"text-sm text-muted-foreground bg-muted/50 p-3 rounded-lg\">\n {t(\"freeShippingMessage\", \"Add {{amount}} more for free shipping!\").replace(\n \"{{amount}}\",\n formatPrice(freeShippingThreshold - total, currency)\n )}\n </div>\n )}\n\n <Button asChild className=\"w-full\" size=\"lg\">\n <Link to=\"/checkout\">{t(\"proceedToCheckout\", \"Proceed to Checkout\")}</Link>\n </Button>\n\n <Button variant=\"outline\" asChild className=\"w-full\">\n <Link to=\"/products\">{t(\"continueShopping\", \"Continue Shopping\")}</Link>\n </Button>\n </CardContent>\n </Card>\n\n <Card>\n <CardContent className=\"p-4\">\n <div className=\"text-center space-y-2\">\n <div className=\"text-sm text-muted-foreground\">\n {t(\"secureCheckout\", \"Secure Checkout\")}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {t(\"secureCheckoutDescription\", \"Your payment information is encrypted and secure\")}\n </p>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default CartPage;\n"
27
27
  },
28
28
  {
29
29
  "path": "cart-page/lang/en.json",
30
30
  "type": "registry:lang",
31
31
  "target": "$modules$/cart-page/lang/en.json",
32
- "content": "{\r\n \"title\": \"Shopping Cart\",\r\n \"empty\": \"Your Cart is Empty\",\r\n \"emptyDescription\": \"Looks like you haven't added any items to your cart yet.\",\r\n \"addToCart\": \"Add to Cart\",\r\n \"removeItem\": \"Remove Item\",\r\n \"quantity\": \"Quantity\",\r\n \"subtotal\": \"Subtotal\",\r\n \"shipping\": \"Shipping\",\r\n \"tax\": \"Tax\",\r\n \"total\": \"Total\",\r\n \"proceedToCheckout\": \"Proceed to Checkout\",\r\n \"secureCheckout\": \"Secure Checkout\",\r\n \"orderSummary\": \"Order Summary\",\r\n \"itemsInCart\": \"items in your cart\",\r\n \"items\": \"items\",\r\n \"variant\": \"Variant\",\r\n \"each\": \"each\",\r\n \"freeShippingMessage\": \"Add {{amount}} more for free shipping!\",\r\n \"continueShopping\": \"Continue Shopping\",\r\n \"secureCheckoutDescription\": \"Your payment information is encrypted and secure\",\r\n \"free\": \"Free\"\r\n}\r\n"
32
+ "content": "{\r\n \"pageTitle\": \"Shopping Cart\",\r\n \"title\": \"Shopping Cart\",\r\n \"empty\": \"Your Cart is Empty\",\r\n \"emptyDescription\": \"Looks like you haven't added any items to your cart yet.\",\r\n \"addToCart\": \"Add to Cart\",\r\n \"removeItem\": \"Remove Item\",\r\n \"quantity\": \"Quantity\",\r\n \"subtotal\": \"Subtotal\",\r\n \"shipping\": \"Shipping\",\r\n \"tax\": \"Tax\",\r\n \"total\": \"Total\",\r\n \"proceedToCheckout\": \"Proceed to Checkout\",\r\n \"secureCheckout\": \"Secure Checkout\",\r\n \"orderSummary\": \"Order Summary\",\r\n \"itemsInCart\": \"items in your cart\",\r\n \"items\": \"items\",\r\n \"variant\": \"Variant\",\r\n \"each\": \"each\",\r\n \"freeShippingMessage\": \"Add {{amount}} more for free shipping!\",\r\n \"continueShopping\": \"Continue Shopping\",\r\n \"secureCheckoutDescription\": \"Your payment information is encrypted and secure\",\r\n \"free\": \"Free\"\r\n}\r\n"
33
33
  },
34
34
  {
35
35
  "path": "cart-page/lang/tr.json",
36
36
  "type": "registry:lang",
37
37
  "target": "$modules$/cart-page/lang/tr.json",
38
- "content": "{\r\n \"title\": \"Alışveriş Sepeti\",\r\n \"empty\": \"Sepetiniz Boş\",\r\n \"emptyDescription\": \"Henüz sepetinize hiç ürün eklememişsiniz.\",\r\n \"addToCart\": \"Sepete Ekle\",\r\n \"removeItem\": \"Ürünü Kaldır\",\r\n \"quantity\": \"Adet\",\r\n \"subtotal\": \"Ara Toplam\",\r\n \"shipping\": \"Kargo\",\r\n \"tax\": \"Vergi\",\r\n \"total\": \"Toplam\",\r\n \"proceedToCheckout\": \"Ödemeye Geç\",\r\n \"secureCheckout\": \"Güvenli Ödeme\",\r\n \"orderSummary\": \"Sipariş Özeti\",\r\n \"itemsInCart\": \"sepetinizdeki ürün\",\r\n \"items\": \"ürün\",\r\n \"variant\": \"Varyant\",\r\n \"each\": \"adet\",\r\n \"freeShippingMessage\": \"Ücretsiz kargo için {{amount}} daha ekleyin!\",\r\n \"continueShopping\": \"Alışverişe Devam Et\",\r\n \"secureCheckoutDescription\": \"Ödeme bilgileriniz şifreli ve güvenlidir\",\r\n \"free\": \"Ücretsiz\"\r\n}\r\n"
38
+ "content": "{\r\n \"pageTitle\": \"Alışveriş Sepeti\",\r\n \"title\": \"Alışveriş Sepeti\",\r\n \"empty\": \"Sepetiniz Boş\",\r\n \"emptyDescription\": \"Henüz sepetinize hiç ürün eklememişsiniz.\",\r\n \"addToCart\": \"Sepete Ekle\",\r\n \"removeItem\": \"Ürünü Kaldır\",\r\n \"quantity\": \"Adet\",\r\n \"subtotal\": \"Ara Toplam\",\r\n \"shipping\": \"Kargo\",\r\n \"tax\": \"Vergi\",\r\n \"total\": \"Toplam\",\r\n \"proceedToCheckout\": \"Ödemeye Geç\",\r\n \"secureCheckout\": \"Güvenli Ödeme\",\r\n \"orderSummary\": \"Sipariş Özeti\",\r\n \"itemsInCart\": \"sepetinizdeki ürün\",\r\n \"items\": \"ürün\",\r\n \"variant\": \"Varyant\",\r\n \"each\": \"adet\",\r\n \"freeShippingMessage\": \"Ücretsiz kargo için {{amount}} daha ekleyin!\",\r\n \"continueShopping\": \"Alışverişe Devam Et\",\r\n \"secureCheckoutDescription\": \"Ödeme bilgileriniz şifreli ve güvenlidir\",\r\n \"free\": \"Ücretsiz\"\r\n}\r\n"
39
39
  }
40
40
  ],
41
41
  "exports": {