@@ -144,12 +144,60 @@ const repos = [
144144 { name: " RedisRepository" , href: " /concepts/repository#redisrepository" },
145145 { name: " PostgresRepository" , href: " /concepts/repository#postgresrepository" },
146146];
147+ const repositoryChoices = [
148+ {
149+ name: " KvRepository" ,
150+ code: " new KvRepository(kv)" ,
151+ },
152+ {
153+ name: " SqliteRepository" ,
154+ code: " new SqliteRepository(db)" ,
155+ },
156+ {
157+ name: " RedisRepository" ,
158+ code: " new RedisRepository(redis)" ,
159+ },
160+ {
161+ name: " PostgresRepository" ,
162+ code: " new PostgresRepository(pool)" ,
163+ },
164+ ] satisfies readonly {
165+ readonly name: string ;
166+ readonly code: string ;
167+ }[];
168+ const currentRepositoryIndex = ref (0 );
169+ let repositoryTimer: ReturnType <typeof setInterval > | null = null ;
170+
171+ function currentRepository() {
172+ return repositoryChoices [currentRepositoryIndex .value ];
173+ }
174+
175+ function startRepositoryCycle() {
176+ repositoryTimer = setInterval (() => {
177+ currentRepositoryIndex .value =
178+ (currentRepositoryIndex .value + 1 ) % repositoryChoices .length ;
179+ }, 2600 );
180+ }
181+
182+ function stopRepositoryCycle() {
183+ if (repositoryTimer !== null ) {
184+ clearInterval (repositoryTimer );
185+ repositoryTimer = null ;
186+ }
187+ }
147188const targets = [
148189 { name: " Deno Deploy" , href: " /deploy/deno-deploy" },
149190 { name: " Cloudflare Workers" , href: " /deploy/cfworkers" },
150191 { name: " Docker · Fly.io / Railway" , href: " /deploy/docker" },
151192 { name: " Self‑hosted" , href: " /deploy/self-hosting" },
152193];
194+
195+ onMounted (() => {
196+ if (! window .matchMedia (" (prefers-reduced-motion: reduce)" ).matches ) {
197+ startRepositoryCycle ();
198+ }
199+ });
200+ onUnmounted (() => stopRepositoryCycle ());
153201 </script >
154202
155203<template >
@@ -622,46 +670,68 @@ const targets = [
622670 <div class =" bk-section__head" >
623671 <h2 class =" bk-h2" >Store it and ship it anywhere</h2 >
624672 <p class =" bk-sub" >
625- BotKit keeps storage behind its own
626- <a href =" /concepts/repository" ><code >Repository</code ></a > interface,
627- so switching backends and deploy targets never touches your bot code.
673+ Put storage behind BotKit's
674+ <a href =" /concepts/repository" ><code >Repository</code ></a > interface.
675+ Your handlers keep the same shape when you move from memory to Redis,
676+ PostgreSQL, SQLite, Deno KV, or a hosted runtime.
628677 </p >
629678 </div >
630679
631- <div class =" bk-rows" >
632- <div class =" bk-row" >
633- <span class =" bk-row__label" >Storage</span >
634- <div class =" bk-row__body" >
635- <div class =" bk-chips" >
636- <a
637- v-for =" r in repos"
638- :key =" r.name"
639- class =" bk-chip bk-chip--code"
640- :href =" r.href"
641- >{{ r.name }}</a
642- >
680+ <div class =" bk-storage" >
681+ <div class =" bk-window" >
682+ <div class =" bk-window__bar" >
683+ <span class =" bk-dot" ></span ><span class =" bk-dot" ></span
684+ ><span class =" bk-dot" ></span >
685+ <span class =" bk-window__name" >bot.ts</span >
686+ </div >
687+ <div class =" bk-storage-code" aria-label =" Repository swap example" >
688+ <pre ><code ><span class =" line" ><span class =" kw" >const</span > bot = createBot({</span >
689+ <span class =" line" > username: <span class =" str" >"support"</span >,</span >
690+ <span class =" line line--swap" > repository: <Transition name="bk-repo-swap" mode="out-in"><span :key =" currentRepository().name" class =" bk-repo-code" >{{ currentRepository().code }}</span ></Transition >,</span >
691+ <span class =" line" >});</span >
692+ <span class =" line" ></span >
693+ <span class =" line" >bot.onMention = <span class =" kw" >async</span > (session, message) => {</span >
694+ <span class =" line" > <span class =" kw" >await</span > message.reply(text<span class =" str" >`Hello!`</span >);</span >
695+ <span class =" line" >};</span ></code ></pre >
696+ </div >
697+ </div >
698+
699+ <div class =" bk-storage__side" >
700+ <p class =" bk-storage__label" >
701+ Only the <code >repository</code > line changes
702+ </p >
703+ <p class =" bk-storage__note" >
704+ The repository line owns persistence. The event handler below it
705+ does not know whether the bot is backed by a process, a database,
706+ or a serverless key-value store.
707+ </p >
708+ <div class =" bk-storage__facts" aria-label =" Storage boundary" >
709+ <div >
710+ <span >Stable:</span >
711+ <p >Handlers, sessions, and message builders</p >
712+ </div >
713+ <div >
714+ <span >Swappable:</span >
715+ <p >Repository adapter and deploy target</p >
643716 </div >
644- <p class =" bk-row__note" >
645- <a href =" /concepts/repository#kvrepository"
646- ><code >KvRepository</code ></a
647- >
648- adapts any Fedify
649- <a href =" https://fedify.dev/manual/kv" ><code >KvStore</code ></a >
650- (Redis, PostgreSQL, Deno KV, or in‑memory);
651- <a href =" /concepts/repository#sqliterepository"
652- ><code >SqliteRepository</code ></a
653- >,
654- <a href =" /concepts/repository#redisrepository"
655- ><code >RedisRepository</code ></a
656- >, and
657- <a href =" /concepts/repository#postgresrepository"
658- ><code >PostgresRepository</code ></a
659- >
660- store to those backends directly.
661- </p >
662717 </div >
663718 </div >
664- <div class =" bk-row" >
719+ </div >
720+
721+ <div class =" bk-storage-options" >
722+ <div class =" bk-storage__group" >
723+ <span class =" bk-row__label" >Storage</span >
724+ <div class =" bk-chips" >
725+ <a
726+ v-for =" r in repos"
727+ :key =" r.name"
728+ class =" bk-chip bk-chip--code"
729+ :href =" r.href"
730+ >{{ r.name }}</a
731+ >
732+ </div >
733+ </div >
734+ <div class =" bk-storage__group" >
665735 <span class =" bk-row__label" >Deploy</span >
666736 <div class =" bk-chips" >
667737 <a
@@ -1484,6 +1554,141 @@ a.bk-notes__k:hover {
14841554}
14851555
14861556/* ── Store & ship ────────────────────────────────────────── */
1557+ .bk-storage {
1558+ display : grid ;
1559+ grid-template-columns : minmax (0 , 1.05fr ) minmax (320px , 0.95fr );
1560+ gap : 28px ;
1561+ align-items : center ;
1562+ }
1563+ .bk-storage-code {
1564+ margin : 0 ;
1565+ min-width : 0 ;
1566+ font-family : var (--vp-font-family-mono );
1567+ font-size : 0.82rem ;
1568+ line-height : 1.7 ;
1569+ color : var (--vp-c-text-1 );
1570+ background : transparent ;
1571+ tab-size : 2 ;
1572+ }
1573+ .bk-storage-code pre {
1574+ margin : 0 ;
1575+ padding : 20px 22px ;
1576+ overflow-x : auto ;
1577+ white-space : normal ;
1578+ }
1579+ .bk-storage-code code {
1580+ display : block ;
1581+ min-width : max-content ;
1582+ background : none ;
1583+ padding : 0 ;
1584+ font : inherit ;
1585+ color : inherit ;
1586+ white-space : normal ;
1587+ }
1588+ .bk-storage-code .line {
1589+ display : block ;
1590+ min-height : 1.7em ;
1591+ white-space : pre ;
1592+ }
1593+ .bk-storage-code .kw {
1594+ color : var (--vp-c-brand-1 );
1595+ }
1596+ .bk-storage-code .str {
1597+ color : var (--vp-c-text-2 );
1598+ }
1599+ .line--swap {
1600+ position : relative ;
1601+ margin : 2px -8px ;
1602+ padding : 2px 8px ;
1603+ border-radius : 8px ;
1604+ background : color-mix(in oklab, var (--vp-c-brand-1 ) 10% , transparent );
1605+ }
1606+ .bk-repo-code {
1607+ display : inline-block ;
1608+ color : var (--vp-c-brand-1 );
1609+ font-weight : 600 ;
1610+ }
1611+ .bk-repo-swap-enter-active ,
1612+ .bk-repo-swap-leave-active {
1613+ transition :
1614+ opacity 0.22s ease ,
1615+ transform 0.22s ease ;
1616+ }
1617+ .bk-repo-swap-enter-from {
1618+ opacity : 0 ;
1619+ transform : translateY (0.45em );
1620+ }
1621+ .bk-repo-swap-leave-to {
1622+ opacity : 0 ;
1623+ transform : translateY (-0.45em );
1624+ }
1625+ .bk-storage__side {
1626+ display : flex ;
1627+ flex-direction : column ;
1628+ gap : 18px ;
1629+ }
1630+ .bk-storage__label {
1631+ margin : 0 ;
1632+ color : var (--vp-c-text-1 );
1633+ font-weight : 600 ;
1634+ }
1635+ .bk-storage__label code {
1636+ font-family : var (--vp-font-family-mono );
1637+ font-size : 0.9em ;
1638+ color : var (--vp-c-brand-1 );
1639+ background : var (--vp-c-brand-soft );
1640+ padding : 3px 7px ;
1641+ border-radius : 7px ;
1642+ }
1643+ .bk-storage__note {
1644+ margin : 0 ;
1645+ color : var (--vp-c-text-2 );
1646+ font-size : 0.94rem ;
1647+ line-height : 1.6 ;
1648+ }
1649+ .bk-storage__facts {
1650+ display : grid ;
1651+ gap : 10px ;
1652+ padding-top : 2px ;
1653+ }
1654+ .bk-storage__facts div {
1655+ display : grid ;
1656+ grid-template-columns : 92px 1fr ;
1657+ gap : 14px ;
1658+ align-items : baseline ;
1659+ padding : 10px 0 ;
1660+ border-top : 1px solid var (--vp-c-divider );
1661+ }
1662+ .bk-storage__facts span {
1663+ font-family : var (--vp-font-family-mono );
1664+ font-size : 0.72rem ;
1665+ letter-spacing : 0.05em ;
1666+ text-transform : uppercase ;
1667+ color : var (--vp-c-brand-1 );
1668+ }
1669+ .bk-storage__facts p {
1670+ margin : 0 ;
1671+ color : var (--vp-c-text-2 );
1672+ font-size : 0.9rem ;
1673+ line-height : 1.45 ;
1674+ }
1675+ .bk-storage__group {
1676+ display : flex ;
1677+ flex-direction : column ;
1678+ gap : 10px ;
1679+ }
1680+ .bk-storage__group .bk-row__label {
1681+ width : auto ;
1682+ padding-top : 0 ;
1683+ }
1684+ .bk-storage-options {
1685+ display : grid ;
1686+ grid-template-columns : 1fr 1fr ;
1687+ gap : 20px ;
1688+ margin-top : 24px ;
1689+ padding-top : 20px ;
1690+ border-top : 1px solid var (--vp-c-divider );
1691+ }
14871692.bk-rows {
14881693 display : flex ;
14891694 flex-direction : column ;
@@ -1609,7 +1814,9 @@ a.bk-chip--code:hover {
16091814 .bk-hero__grid ,
16101815 .bk-code-grid ,
16111816 .bk-feature__grid ,
1612- .bk-fedify__grid {
1817+ .bk-fedify__grid ,
1818+ .bk-storage ,
1819+ .bk-storage-options {
16131820 grid-template-columns : 1fr ;
16141821 }
16151822 .bk-feature__grid {
@@ -1650,13 +1857,21 @@ a.bk-chip--code:hover {
16501857 padding : 18px 10px ;
16511858 font-size : 0.72rem ;
16521859 }
1860+ .bk-storage-code {
1861+ font-size : 0.72rem ;
1862+ }
1863+ .bk-storage-code pre {
1864+ padding : 18px 10px ;
1865+ }
16531866}
16541867
16551868@media (prefers-reduced-motion: reduce) {
16561869 .bk-card ,
16571870 .bk-btn ,
16581871 .bk-fade-enter-active ,
16591872 .bk-fade-leave-active ,
1873+ .bk-repo-swap-enter-active ,
1874+ .bk-repo-swap-leave-active ,
16601875 .bk-carousel__dot {
16611876 transition : none ;
16621877 }
0 commit comments