Skip to content

Commit ebf99fe

Browse files
committed
Show swappable storage on the landing page
Rework the storage and deploy section around a code visual that keeps bot handlers stable while cycling only the Repository configuration. The supporting links remain available below the visual as storage and deploy options. Assisted-by: Codex:gpt-5.5
1 parent 79ceacd commit ebf99fe

1 file changed

Lines changed: 250 additions & 35 deletions

File tree

docs/.vitepress/theme/components/BotKitLanding.vue

Lines changed: 250 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}
147188
const 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

Comments
 (0)