webcake-landing-mcp 1.0.82 → 1.0.83

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.
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "v": "1.0.83",
4
+ "d": "16/06/2026",
5
+ "type": "Added",
6
+ "en": "The server web guide (GET /) now includes a bilingual video guides section with per-app installation walkthrough cards (Claude, Codex, Antigravity);…",
7
+ "vi": "Web guide của server (GET /) nay có thêm mục video hướng dẫn cài đặt song ngữ với các card theo từng ứng dụng (Claude, Codex, Antigravity); card có…"
8
+ },
2
9
  {
3
10
  "v": "1.0.82",
4
11
  "d": "16/06/2026",
@@ -33,12 +40,5 @@
33
40
  "type": "Added",
34
41
  "en": "The OAuth token store (clients, authorization codes, access and refresh tokens) now uses Postgres when DATABASE_URL, WEBCAKE_POSTGRES_URL, or…",
35
42
  "vi": "Kho lưu trữ OAuth token (clients, authorization code, access và refresh token) nay sử dụng Postgres khi DATABASE_URL, WEBCAKE_POSTGRES_URL, hoặc…"
36
- },
37
- {
38
- "v": "1.0.77",
39
- "d": "15/06/2026",
40
- "type": "Added",
41
- "en": "The remote serve transport now implements a spec-conformant OAuth 2.1 Authorization Server (Authorization Code + PKCE S256, Dynamic Client…",
42
- "vi": "Transport serve từ xa nay triển khai một OAuth 2.1 Authorization Server chuẩn spec (Authorization Code + PKCE S256, Dynamic Client Registration,…"
43
43
  }
44
44
  ]
package/dist/web-guide.js CHANGED
@@ -32,6 +32,16 @@ const INSTALL_ALL_CMD = "npx -y webcake-landing-mcp install --ide all --env prod
32
32
  const GITHUB_URL = "https://github.com/vuluu2k/webcake-landing-mcp";
33
33
  const NPM_URL = "https://www.npmjs.com/package/webcake-landing-mcp";
34
34
  const DOCS_URL = `${GITHUB_URL}#readme`;
35
+ // Per-app video walkthroughs for connecting the MCP, rendered as cards in the
36
+ // guide — each card embeds the YouTube video in a 16:9 iframe. `youtube` is the
37
+ // video ID (privacy-friendly youtube-nocookie embed). Claude has a recorded
38
+ // walkthrough; Codex/Antigravity are placeholders (empty `youtube` → a matching
39
+ // "coming soon" card) until their videos are published — just fill in the ID.
40
+ const VIDEO_GUIDES = [
41
+ { app: "Claude", icon: "brain", youtube: "NY-YMbSnlOE" },
42
+ { app: "Codex", icon: "terminal", youtube: "" },
43
+ { app: "Antigravity", icon: "rocket", youtube: "" },
44
+ ];
35
45
  export const LANGS = ["vi", "en"];
36
46
  export function normalizeLang(input) {
37
47
  return input === "en" ? "en" : "vi";
@@ -247,6 +257,10 @@ const T = {
247
257
  "<b>Bấm Add</b> (hoặc lưu file) rồi chờ một chút. Khi biểu tượng Webcake chuyển xanh là dùng được.",
248
258
  ],
249
259
  m2Note: "⚠️ Link có chứa mã đăng nhập riêng của bạn — hãy coi như mật khẩu, đừng chia sẻ cho ai.",
260
+ videoH2: "Video hướng dẫn cài đặt",
261
+ videoSub: "Xem video làm theo từng bước cho đúng ứng dụng AI bạn đang dùng.",
262
+ videoCta: "Xem video",
263
+ videoSoon: "Sắp có",
250
264
  afterH2: "Kết nối xong, bạn chỉ cần nói",
251
265
  examples: [
252
266
  {
@@ -271,6 +285,14 @@ const T = {
271
285
  starBtn: "Tặng sao trên GitHub",
272
286
  footGuide: "Hướng dẫn",
273
287
  switchLabel: "English",
288
+ nav: [
289
+ { href: "#flow", label: "Cách hoạt động" },
290
+ { href: "#build", label: "Tạo được gì" },
291
+ { href: "#clone", label: "Copy trang" },
292
+ { href: "#connect", label: "Kết nối" },
293
+ { href: "#video", label: "Video" },
294
+ { href: "#faq", label: "Hỏi đáp" },
295
+ ],
274
296
  },
275
297
  en: {
276
298
  sub: "Let AI build & edit your Webcake landing pages, just by talking to it",
@@ -378,6 +400,10 @@ const T = {
378
400
  "<b>Hit Add</b> (or save the file) and wait a moment. When the Webcake icon turns green, you're good to go.",
379
401
  ],
380
402
  m2Note: "⚠️ The link contains your personal login code — treat it like a password and never share it.",
403
+ videoH2: "Video install guides",
404
+ videoSub: "Follow a step-by-step video for the AI app you use.",
405
+ videoCta: "Watch video",
406
+ videoSoon: "Coming soon",
381
407
  afterH2: "Once connected, just say",
382
408
  examples: [
383
409
  {
@@ -402,6 +428,14 @@ const T = {
402
428
  starBtn: "Star on GitHub",
403
429
  footGuide: "Docs",
404
430
  switchLabel: "Tiếng Việt",
431
+ nav: [
432
+ { href: "#flow", label: "How it works" },
433
+ { href: "#build", label: "What you build" },
434
+ { href: "#clone", label: "Clone a page" },
435
+ { href: "#connect", label: "Connect" },
436
+ { href: "#video", label: "Videos" },
437
+ { href: "#faq", label: "FAQ" },
438
+ ],
405
439
  },
406
440
  };
407
441
  const CHANGELOG = loadChangelog();
@@ -534,11 +568,11 @@ export function guideHtml(origin, lang = "vi") {
534
568
  (the toggle); [data-theme="light"] forces light even on a dark OS. */
535
569
  :root{--g:#1DB954;--g7:#178f43;--ink:#11231b;--mut:#5e6d65;--bg:#f5f9f7;--card:#ffffff;
536
570
  --line:rgba(16,40,30,.09);--shadow:0 1px 2px rgba(16,40,30,.05),0 6px 20px -12px rgba(16,40,30,.18);--code:#0e1714;
537
- --ic-fg:#178f43;--btn-hover:#178f43}
571
+ --ic-fg:#178f43;--btn-hover:#178f43;--navbg:rgba(245,249,247,.82)}
538
572
  @media(prefers-color-scheme:dark){:root:not([data-theme="light"]){--ink:#e8f0ec;--mut:#9aaba2;--bg:#0b110e;--card:#141b17;
539
- --line:rgba(255,255,255,.07);--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.7);--code:#070f0b;--g7:#5ee08a;--ic-fg:#6fe79a;--btn-hover:#21c264}}
573
+ --line:rgba(255,255,255,.07);--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.7);--code:#070f0b;--g7:#5ee08a;--ic-fg:#6fe79a;--btn-hover:#21c264;--navbg:rgba(11,17,14,.82)}}
540
574
  :root[data-theme="dark"]{--ink:#e8f0ec;--mut:#9aaba2;--bg:#0b110e;--card:#141b17;
541
- --line:rgba(255,255,255,.07);--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.7);--code:#070f0b;--g7:#5ee08a;--ic-fg:#6fe79a;--btn-hover:#21c264}
575
+ --line:rgba(255,255,255,.07);--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.7);--code:#070f0b;--g7:#5ee08a;--ic-fg:#6fe79a;--btn-hover:#21c264;--navbg:rgba(11,17,14,.82)}
542
576
  *{box-sizing:border-box}
543
577
  /* Smooth scrolling only AFTER first load — applied globally it makes the
544
578
  browser *animate* scroll-position restoration on reload, which reads as a
@@ -583,7 +617,7 @@ export function guideHtml(origin, lang = "vi") {
583
617
  color:var(--g7);background:rgba(29,185,84,.10);border:1px solid var(--line)}
584
618
  .dot{width:8px;height:8px;border-radius:50%;background:var(--g);box-shadow:0 0 0 0 rgba(29,185,84,.5);animation:pulse 2s infinite}
585
619
  @keyframes pulse{70%{box-shadow:0 0 0 7px rgba(29,185,84,0)}100%{box-shadow:0 0 0 0 rgba(29,185,84,0)}}
586
- h2{font-size:1.32rem;margin:46px 0 16px;font-weight:800;letter-spacing:-.01em}
620
+ h2{font-size:1.32rem;margin:46px 0 16px;font-weight:800;letter-spacing:-.01em;scroll-margin-top:72px}
587
621
  .ic{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;flex:0 0 auto;color:var(--ic-fg);
588
622
  background:rgba(29,185,84,.11);border:1px solid var(--line);transition:transform .2s ease}
589
623
  .ic .i{width:22px;height:22px}
@@ -640,6 +674,49 @@ export function guideHtml(origin, lang = "vi") {
640
674
  .btn:hover{transform:translateY(-1px);background:var(--btn-hover)}
641
675
  .btn.ghost{background:var(--card);color:var(--ink);border:1px solid var(--line);box-shadow:none}
642
676
  .btn.ghost:hover{border-color:var(--g);background:var(--card)}
677
+ /* Video-guide cards: a 16:9 clickable poster (YouTube thumbnail + play badge)
678
+ that opens the video in the modal below, or a matching "coming soon"
679
+ placeholder — so every card lines up to the same height. */
680
+ .vid-head{display:flex;align-items:center;gap:11px;margin-bottom:14px}
681
+ .vid-head .ic{margin-bottom:0}
682
+ .vid-head h3{margin:0;font-size:1.04rem}
683
+ .vid-play,.vid-soon{aspect-ratio:16/9;border-radius:12px;overflow:hidden;width:100%}
684
+ .vid-play{position:relative;display:block;padding:0;cursor:pointer;background:#000;
685
+ border:1px solid var(--line);transition:transform .2s ease,box-shadow .2s ease}
686
+ .vid-play:hover{transform:translateY(-2px);box-shadow:0 12px 28px -14px rgba(16,40,30,.5)}
687
+ .vid-play img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;border:0}
688
+ .vid-play::after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.18);transition:background .2s ease}
689
+ .vid-play:hover::after{background:rgba(0,0,0,.04)}
690
+ .vid-play .pbtn{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:54px;height:54px;
691
+ border-radius:50%;background:rgba(29,185,84,.92);display:grid;place-items:center;z-index:1;
692
+ box-shadow:0 6px 18px -4px rgba(0,0,0,.5);transition:transform .2s ease,background .2s ease}
693
+ .vid-play:hover .pbtn{transform:translate(-50%,-50%) scale(1.08);background:var(--g)}
694
+ .vid-play .pbtn svg{width:23px;height:23px;color:#fff;margin-left:3px}
695
+ .vid-soon{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
696
+ color:var(--mut);font-size:.9rem;font-weight:600;background:rgba(29,185,84,.05);border:1px dashed var(--line)}
697
+ .vid-soon .i{width:24px;height:24px}
698
+ /* Video modal (lightbox): a single overlay reused for every card's video. */
699
+ .modal{position:fixed;inset:0;z-index:200;display:none;align-items:center;justify-content:center;
700
+ padding:24px;background:rgba(4,10,7,.74);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
701
+ .modal.open{display:flex}
702
+ .modal-box{position:relative;width:min(960px,100%);aspect-ratio:16/9;background:#000;border-radius:14px;
703
+ overflow:hidden;box-shadow:0 24px 60px -20px rgba(0,0,0,.8)}
704
+ .modal-box iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
705
+ .modal-x{position:absolute;top:-13px;right:-13px;width:38px;height:38px;border-radius:50%;cursor:pointer;
706
+ background:var(--g);color:#fff;border:2px solid #fff;display:grid;place-items:center;z-index:2;
707
+ box-shadow:0 4px 14px -2px rgba(0,0,0,.5)}
708
+ .modal-x svg{width:18px;height:18px}
709
+ @media(max-width:640px){.modal{padding:14px}.modal-x{top:-11px;right:-11px;width:34px;height:34px}}
710
+ /* Sticky section nav — pins to the top once you scroll past the hero so the
711
+ reader can jump to any section; horizontally scrollable on narrow screens. */
712
+ .nav{position:sticky;top:0;z-index:60;display:flex;gap:6px;align-items:center;overflow-x:auto;
713
+ margin:18px -20px 6px;padding:9px 20px;background:var(--navbg);border-bottom:1px solid var(--line);
714
+ backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);scrollbar-width:none}
715
+ .nav::-webkit-scrollbar{display:none}
716
+ .nav a{flex:0 0 auto;font-size:.84rem;font-weight:600;color:var(--mut);text-decoration:none;
717
+ padding:7px 13px;border-radius:999px;white-space:nowrap;transition:color .15s ease,background .15s ease}
718
+ .nav a:hover{color:var(--g7);background:rgba(29,185,84,.10)}
719
+ .nav a.active{color:var(--g7);background:rgba(29,185,84,.13)}
643
720
  .uses{display:grid;gap:14px;grid-template-columns:1fr 1fr;padding:0;margin:0;list-style:none}
644
721
  @media(max-width:640px){.uses{grid-template-columns:1fr}}
645
722
  .uses li{display:flex;gap:13px;padding:16px 18px;align-items:flex-start;transition:transform .2s ease,border-color .2s ease,box-shadow .2s ease}
@@ -772,7 +849,11 @@ export function guideHtml(origin, lang = "vi") {
772
849
  <a class="btn ghost" href="${GITHUB_URL}">${icon("star")} ${t.ctaStar}</a>
773
850
  </div>
774
851
 
775
- <h2 class="reveal">${t.flowH2}</h2>
852
+ <nav class="nav" aria-label="${L === "en" ? "Sections" : "Mục lục"}">
853
+ ${t.nav.map((n) => `<a href="${n.href}">${n.label}</a>`).join("\n ")}
854
+ </nav>
855
+
856
+ <h2 id="flow" class="reveal">${t.flowH2}</h2>
776
857
  <div class="glass flow reveal">
777
858
  ${t.flow
778
859
  .map((n, i) => `<div class="node"><span class="ic" style="animation-delay:${(i * 0.8).toFixed(1)}s">${icon(n.icon)}</span><b>${n.t}</b><span>${n.s}</span></div>` +
@@ -790,14 +871,14 @@ export function guideHtml(origin, lang = "vi") {
790
871
  .join("\n ")}
791
872
  </div>
792
873
 
793
- <h2 class="reveal">${t.buildH2}</h2>
874
+ <h2 id="build" class="reveal">${t.buildH2}</h2>
794
875
  <ul class="uses">
795
876
  ${t.uses
796
877
  .map((u) => `<li class="glass reveal">${tile(u.icon)}<div><b>${u.t}</b><span>${u.e}</span></div></li>`)
797
878
  .join("\n ")}
798
879
  </ul>
799
880
 
800
- <h2 class="reveal">${t.cloneH2}</h2>
881
+ <h2 id="clone" class="reveal">${t.cloneH2}</h2>
801
882
  <p class="flow-cap reveal" style="margin-bottom:16px">${t.cloneSub}</p>
802
883
  <div class="grid">
803
884
  ${t.clone
@@ -826,6 +907,14 @@ export function guideHtml(origin, lang = "vi") {
826
907
  <p class="note">${t.m2Note}</p>
827
908
  </div>
828
909
 
910
+ <h2 id="video" class="reveal">${t.videoH2}</h2>
911
+ <p class="flow-cap reveal" style="margin-bottom:16px">${t.videoSub}</p>
912
+ <div class="grid">
913
+ ${VIDEO_GUIDES.map((v) => `<div class="glass card vid reveal"><div class="vid-head">${tile(v.icon)}<h3>${v.app}</h3></div>${v.youtube
914
+ ? `<button type="button" class="vid-play" data-yt="${v.youtube}" data-title="${v.app} — ${t.videoCta}" aria-label="${v.app} — ${t.videoCta}"><img src="https://i.ytimg.com/vi/${v.youtube}/hqdefault.jpg" alt="" loading="lazy"><span class="pbtn"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span></button>`
915
+ : `<div class="vid-soon">${icon("clock")}<span>${t.videoSoon}</span></div>`}</div>`).join("\n ")}
916
+ </div>
917
+
829
918
  <h2 class="reveal">${t.afterH2}</h2>
830
919
  <ul class="feat">
831
920
  ${t.examples
@@ -843,7 +932,7 @@ export function guideHtml(origin, lang = "vi") {
843
932
  </div>`
844
933
  : ""}
845
934
 
846
- <h2 class="reveal">${t.faqH2}</h2>
935
+ <h2 id="faq" class="reveal">${t.faqH2}</h2>
847
936
  ${faq.map((f) => `<details class="glass reveal"><summary>${f.q}</summary><p>${f.a}</p></details>`).join("\n ")}
848
937
 
849
938
  <div class="glass star reveal">
@@ -860,6 +949,13 @@ export function guideHtml(origin, lang = "vi") {
860
949
  <a href="${selfPath === "/" ? "/health" : "/health"}">Health</a>
861
950
  </footer>
862
951
 
952
+ <div class="modal" id="vmodal" role="dialog" aria-modal="true" aria-label="${t.videoH2}">
953
+ <div class="modal-box">
954
+ <button type="button" class="modal-x" id="vclose" aria-label="Close" title="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button>
955
+ <iframe id="vframe" title="${t.videoH2}" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
956
+ </div>
957
+ </div>
958
+
863
959
  </div>
864
960
  <script>
865
961
  (function(){
@@ -916,6 +1012,47 @@ export function guideHtml(origin, lang = "vi") {
916
1012
  // Enable smooth scrolling only after the browser has restored scroll position
917
1013
  // on (re)load — applying it globally animates that restore into a jerky scroll.
918
1014
  window.addEventListener('load',function(){requestAnimationFrame(function(){html.classList.add('smooth');});});
1015
+
1016
+ // Video lightbox — clicking a card poster opens the YouTube embed in a modal.
1017
+ // The iframe src is only set on open (no YouTube network/tracking until the
1018
+ // user clicks) and cleared on close (which stops playback). Esc, the backdrop
1019
+ // and the × button all close it; focus is restored to the trigger on close.
1020
+ var modal=document.getElementById('vmodal'),vframe=document.getElementById('vframe'),
1021
+ vclose=document.getElementById('vclose'),lastFocus=null;
1022
+ function openVideo(id,title){
1023
+ if(!modal||!vframe||!id)return;
1024
+ vframe.src='https://www.youtube-nocookie.com/embed/'+id+'?autoplay=1&rel=0';
1025
+ if(title)vframe.title=title;
1026
+ lastFocus=document.activeElement;
1027
+ modal.classList.add('open');document.body.style.overflow='hidden';
1028
+ if(vclose)vclose.focus();
1029
+ }
1030
+ function closeVideo(){
1031
+ if(!modal||!modal.classList.contains('open'))return;
1032
+ modal.classList.remove('open');vframe.src='';document.body.style.overflow='';
1033
+ if(lastFocus&&lastFocus.focus)lastFocus.focus();
1034
+ }
1035
+ document.querySelectorAll('.vid-play').forEach(function(b){
1036
+ b.addEventListener('click',function(){openVideo(b.getAttribute('data-yt'),b.getAttribute('data-title')||'');});
1037
+ });
1038
+ if(vclose)vclose.addEventListener('click',closeVideo);
1039
+ if(modal)modal.addEventListener('click',function(e){if(e.target===modal)closeVideo();});
1040
+ document.addEventListener('keydown',function(e){if(e.key==='Escape')closeVideo();});
1041
+
1042
+ // Sticky-nav scrollspy — highlight the link for the section currently on screen.
1043
+ var navLinks={};
1044
+ document.querySelectorAll('.nav a').forEach(function(a){navLinks[a.getAttribute('href').slice(1)]=a;});
1045
+ var spySecs=[].slice.call(document.querySelectorAll('h2[id]'));
1046
+ var spyTick=false;
1047
+ function spy(){
1048
+ spyTick=false;var cur=null;
1049
+ spySecs.forEach(function(s){if(s.getBoundingClientRect().top<=96)cur=s.id;});
1050
+ Object.keys(navLinks).forEach(function(id){navLinks[id].classList.toggle('active',id===cur);});
1051
+ }
1052
+ if(spySecs.length&&Object.keys(navLinks).length){
1053
+ window.addEventListener('scroll',function(){if(!spyTick){spyTick=true;requestAnimationFrame(spy);}},{passive:true});
1054
+ spy();
1055
+ }
919
1056
  })();
920
1057
  </script>
921
1058
  </body></html>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-landing-mcp",
3
- "version": "1.0.82",
3
+ "version": "1.0.83",
4
4
  "description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
5
5
  "mcpName": "io.github.vuluu2k/webcake-landing-mcp",
6
6
  "type": "module",