Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion site/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@ export default {
],
],

clientModules: [require.resolve('./src/clientModules/plausiblePageview.js')],
clientModules: [
require.resolve('./src/clientModules/plausiblePageview.js'),
require.resolve('./src/clientModules/kapa-sidebar.js'),
],

stylesheets: [
{
Expand Down
101 changes: 101 additions & 0 deletions site/src/clientModules/kapa-sidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// Injects the Kapa widget script with all configuration (including
// data-color-scheme-selector which Docusaurus strips due to nested quotes),
// then detects sidebar open/close and toggles .kapa-sidebar-open on <html>.

if (typeof window !== "undefined") {
// Inject the Kapa widget script tag with all attributes
const script = document.createElement("script");
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js";
script.async = true;
const attrs = {
"data-website-id": "de266438-2cd4-4ed5-9633-a1c6b931dd7d",
"data-project-name": "Move Book",
"data-project-color": "#298DFF",
"data-button-hide": "true",
"data-view-mode": "sidebar",
"data-modal-title": "Ask Move AI",
"data-modal-ask-ai-input-placeholder": "Ask me anything about Move!",
"data-modal-example-questions": "How do I define a struct in Move?,What are abilities in Move?,How do I publish a Move package?,What are dynamic fields?",
"data-modal-overlay-hidden": "true",
"data-modal-lock-scroll": "false",
"data-modal-logo-hidden": "true",
"data-color-scheme-selector": "[data-theme='dark']",
};
for (const [key, value] of Object.entries(attrs)) {
script.setAttribute(key, value);
}
document.head.appendChild(script);

// Sidebar open/close detection
const OPEN_CLASS = "kapa-sidebar-open";
let kapaOpen = false;
let hookedRef = null;

function syncClass() {
document.documentElement.classList.toggle(OPEN_CLASS, kapaOpen);
}

function hookKapa() {
if (!window.Kapa || !window.Kapa.open || window.Kapa.open === hookedRef) return;

const origOpen = window.Kapa.open;
const origClose = window.Kapa.close;

window.Kapa.open = function (...args) {
kapaOpen = true;
syncClass();
return origOpen.apply(this, args);
};

window.Kapa.close = function (...args) {
kapaOpen = false;
syncClass();
return origClose.apply(this, args);
};

hookedRef = window.Kapa.open;
}

// Check if Kapa sidebar is covering the right side of the viewport
function isSidebarVisible() {
const x = window.innerWidth - 50;
const y = window.innerHeight / 2;
const el = document.elementFromPoint(x, y);
if (!el) return false;
const docRoot = document.getElementById("__docusaurus");
if (docRoot && docRoot.contains(el)) return false;
if (el === document.body || el === document.documentElement) return false;
return true;
}

// Navigation hooks for SPA
const origPush = history.pushState.bind(history);
const origReplace = history.replaceState.bind(history);
history.pushState = function (...args) {
const r = origPush(...args);
syncClass();
return r;
};
history.replaceState = function (...args) {
const r = origReplace(...args);
syncClass();
return r;
};
window.addEventListener("popstate", syncClass);

// Poll every 300ms
setInterval(() => {
hookKapa();

const visible = isSidebarVisible();
if (visible && !kapaOpen) {
kapaOpen = true;
} else if (!visible && kapaOpen) {
kapaOpen = false;
}
syncClass();
}, 300);
}
87 changes: 87 additions & 0 deletions site/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,90 @@ html {
padding: 0;
font-size: 2.25em;
}

/* ---- Kapa sidebar content shift ---- */
/* Kapa renders in Shadow DOM so CSS :has() can't detect it.
A client module (kapa-sidebar.js) toggles .kapa-sidebar-open on <html>. */
html.kapa-sidebar-open body {
width: calc(100vw - 500px) !important;
overflow-x: hidden;
transition: width 0.3s ease;
}

html.kapa-sidebar-open .navbar,
html.kapa-sidebar-open .navbar--fixed-top {
right: 500px !important;
overflow: hidden;
transition: right 0.3s ease;
}

/* Hide the Ask AI and Search buttons in navbar when sidebar is open
since Kapa is already visible */
html.kapa-sidebar-open .kapa-trigger-btn,
html.kapa-sidebar-open .DocSearch-Button,
html.kapa-sidebar-open [class*="searchBarContainer"] {
display: none !important;
}

.navbar,
.navbar--fixed-top {
transition: right 0.3s ease;
}

/* Hide the desktop TOC column when sidebar is open to free space */
html.kapa-sidebar-open .col.col--3:has(.theme-doc-toc-desktop) {
display: none;
}

/* Let the main content column expand to fill the freed space */
html.kapa-sidebar-open .col[class*="docItemCol"] {
max-width: 100% !important;
flex: 1 !important;
}

body {
transition: width 0.3s ease;
}

@media (max-width: 768px) {
html.kapa-sidebar-open body {
width: 100vw !important;
}
}

/* Kapa trigger button styles */
.kapa-trigger-btn {
display: flex;
align-items: center;
gap: 0.4rem;
cursor: pointer;
background: none;
color: var(--ifm-font-color-base);
font-family: var(--ifm-font-family-base);
font-weight: var(--ifm-font-weight-bold);
font-size: var(--ifm-menu-link-font-size);
padding: 0.4rem 0.6rem;
border-radius: 4px;
border: none;
transition: color 0.15s, background-color 0.15s;
}

.kapa-trigger-btn:hover {
color: var(--ifm-menu-color-active);
background: var(--ifm-menu-color-background-hover);
}

[data-theme='dark'] .kapa-trigger-btn {
color: var(--ifm-font-color-base);
}

[data-theme='dark'] .kapa-trigger-btn:hover {
color: var(--ifm-menu-color-active);
}

/* Compact kapa button below full desktop width */
@media screen and (max-width: 996px) {
.navbar .kapa-trigger-btn span {
display: none;
}
}
107 changes: 107 additions & 0 deletions site/src/theme/Navbar/Content/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import { useThemeConfig, ErrorCauseBoundary } from "@docusaurus/theme-common";
import {
splitNavbarItems,
useNavbarMobileSidebar,
} from "@docusaurus/theme-common/internal";
import NavbarItem from "@theme/NavbarItem";
import NavbarMobileSidebarToggle from "@theme/Navbar/MobileSidebar/Toggle";
import NavbarLogo from "@theme/Navbar/Logo";
import NavbarSearch from "@theme/Navbar/Search";
import NavbarColorModeToggle from "@theme/Navbar/ColorModeToggle";
import SearchBar from "@theme/SearchBar";

function useNavbarItems() {
return useThemeConfig().navbar.items;
}

function useMobileSidebarSafe() {
try {
return useNavbarMobileSidebar();
} catch {
return { disabled: true, toggle: () => {} };
}
}

function NavbarItems({ items }) {
return (
<>
{items.map((item, i) => {
// Skip the search item — we render SearchBar separately
if (item.type === "search") return null;
return (
<ErrorCauseBoundary
key={i}
onError={(error) =>
new Error(
`A theme navbar item failed to render.
Please double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:
${JSON.stringify(item, null, 2)}`,
{ cause: error },
)
}
>
<NavbarItem {...item} />
</ErrorCauseBoundary>
);
})}
</>
);
}

function NavbarContentLayout({ left, right }) {
return (
<div className="navbar__inner">
<div className="navbar__items">{left}</div>
<div className="navbar__items navbar__items--right">{right}</div>
</div>
);
}

function KapaButton() {
const handleClick = () => {
if (typeof window !== "undefined" && window.Kapa) {
window.Kapa.open();
}
};

return (
<button
type="button"
onClick={handleClick}
className="kapa-trigger-btn"
>
<span>Ask Move AI</span>
</button>
);
}

export default function NavbarContent() {
const mobileSidebar = useMobileSidebarSafe();
const items = useNavbarItems();
const [leftItems, rightItems] = splitNavbarItems(items);

return (
<NavbarContentLayout
left={
<>
{!mobileSidebar.disabled && <NavbarMobileSidebarToggle />}
<NavbarLogo />
<NavbarItems items={leftItems} />
</>
}
right={
<>
<NavbarItems items={rightItems} />
<NavbarColorModeToggle />
<KapaButton />
<NavbarSearch>
<SearchBar />
</NavbarSearch>
</>
}
/>
);
}
Loading