Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ figma-*.md
# Cursor IDE rules (personal config)
.cursor/rules/

# Personal todo files
# Personal todo + notes files
TODO-*.md
pat-notes.md

# Icon build script (local tool, requires license key)
scripts/export-icons.js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@
font-feature-settings: 'salt' 1;
}

/* Mobile (Playground): one screenful with the centered message, not the full
intrinsic skeleton height — so the API section isn't a tall empty void. */
@media (max-width: $breakpoint-layout-mobile) {
/* Stacked layouts (mobile + laptop): cap to one screenful with the centered
message, not the full intrinsic skeleton height — so the API section isn't a
tall empty void below the app. */
@media (max-width: ($breakpoint-layout-wide - 1px)) {
.root {
flex: none;
height: calc(100dvh - 52px);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
margin-right: -6px;
cursor: col-resize;
position: relative;
z-index: 1;
// Above the panel headers (sticky, z-index 3) so the divider + hover highlight
// run the full height instead of being clipped by the "API calls" header.
z-index: 4;

&::before {
content: '';
Expand All @@ -20,18 +22,24 @@
border-left: var(--stroke-xs) solid var(--border-primary);
}

// Hover highlight: a full-height line over the divider, matching the Mintlify
// docs sidebar resize edge exactly (2px; gray-300 light / gray-700 dark — the
// same values as the docs' --ls-gray-300 / --ls-gray-700).
&::after {
content: '';
position: absolute;
top: 50%;
top: 0;
bottom: 0;
left: 50%;
transform: translate(-50%, -50%);
width: 3px;
height: 32px;
border-radius: var(--corner-radius-2xs);
background: var(--border-secondary);
transform: translateX(-50%);
width: 2px;
background: var(--color-gray-300);
opacity: 0;
transition: opacity 150ms ease;

:global([data-theme='dark']) & {
background: var(--color-gray-700);
}
}

&:hover::after,
Expand Down
4 changes: 3 additions & 1 deletion components/grid-wallet-demo/src/hooks/useColumnResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { useCallback, useLayoutEffect, useRef, useState } from 'react';

const CONFIGURE_WIDTH = 475;
const MIN_APP = 320;
const MIN_API = 320;
// Never resize the API column below its default/snap width (= the configure
// column width); dragging only widens it from there.
const MIN_API = CONFIGURE_WIDTH;
const SNAP_THRESHOLD = 28;

/** Default + snap target — matches configure column width. */
Expand Down
9 changes: 7 additions & 2 deletions components/grid-wallet-demo/src/styles/breakpoints.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
/** App + API side-by-side inside the right column (large desktop). */
$breakpoint-layout-wide: 1800px;
/** App + API side-by-side inside the right column. Above this the layout is 3
columns: Configure (475) | App (phone) | API (475 default), so the App column
is roughly (viewport − 950). The phone needs ~466px (434 shell + 16px inset
each side) to render full size, i.e. ~1416px of viewport; below that the App
column squeezes the phone tiny. Stack at 1440px so the phone never shrinks
before App + API stack vertically (each full-width). Tune to taste. */
$breakpoint-layout-wide: 1440px;

/** Configure stacks above app + API (phone). */
$breakpoint-layout-mobile: 767px;
Expand Down
2 changes: 1 addition & 1 deletion mintlify/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@
},
"footer": {},
"head": {
"raw": "<style>@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Regular.woff2\") format(\"woff2\");font-weight:400;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Book.woff2\") format(\"woff2\");font-weight:450;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Medium.woff2\") format(\"woff2\");font-weight:500;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Medium.woff2\") format(\"woff2\");font-weight:700;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl Mono\";src:url(\"/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2\") format(\"woff2\");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:\"Suisse Intl Mono\";src:url(\"/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2\") format(\"woff2\");font-weight:700;font-style:normal;font-display:swap}*{font-feature-settings:\"salt\" on}</style><style>#header:empty,#page-title:empty{display:none!important}</style><script>if(location.pathname==='/'||location.pathname==='/index'||location.pathname===''){document.write('<style>header#header,#page-title,#page-context-menu-button,#page-context-menu,.eyebrow,#pagination,[class*=\"space-y-2\"]>*{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style>');document.documentElement.classList.add('is-homepage');}</script><style>html.is-homepage #pagination,body.is-homepage #pagination,html.is-homepage #footer,body.is-homepage #footer,html.is-homepage header#header,body.is-homepage header#header,html.is-homepage #page-title,body.is-homepage #page-title,html.is-homepage #page-context-menu-button,body.is-homepage #page-context-menu-button,html.is-homepage #page-context-menu,body.is-homepage #page-context-menu,html.is-homepage .eyebrow,body.is-homepage .eyebrow{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important}</style><script>if(location.pathname==='/flow-builder'){document.write('<style>header#header,#page-title,#page-context-menu-button,#page-context-menu,.eyebrow,#pagination{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style>');}</script><style>html:has(#flow-builder-container) header#header,html:has(#flow-builder-container) #page-title,html:has(#flow-builder-container) .eyebrow,html:has(#flow-builder-container) #page-context-menu-button,html:has(#flow-builder-container) #page-context-menu,html:has(#flow-builder-container) #pagination{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style>",
"raw": "<style>@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Regular.woff2\") format(\"woff2\");font-weight:400;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Book.woff2\") format(\"woff2\");font-weight:450;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Medium.woff2\") format(\"woff2\");font-weight:500;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl\";src:url(\"/fonts/suisse-intl/SuisseIntl-Medium.woff2\") format(\"woff2\");font-weight:700;font-style:normal;font-display:swap;ascent-override:85%;descent-override:15%;line-gap-override:0%}@font-face{font-family:\"Suisse Intl Mono\";src:url(\"/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2\") format(\"woff2\");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:\"Suisse Intl Mono\";src:url(\"/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2\") format(\"woff2\");font-weight:700;font-style:normal;font-display:swap}*{font-feature-settings:\"salt\" on}</style><style>#header:empty,#page-title:empty{display:none!important}</style><script>if(location.pathname==='/'||location.pathname==='/index'||location.pathname===''){document.write('<style>header#header,#page-title,#page-context-menu-button,#page-context-menu,.eyebrow,#pagination,[class*=\"space-y-2\"]>*{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style>');document.documentElement.classList.add('is-homepage');}</script><style>html.is-homepage #pagination,body.is-homepage #pagination,html.is-homepage #footer,body.is-homepage #footer,html.is-homepage header#header,body.is-homepage header#header,html.is-homepage #page-title,body.is-homepage #page-title,html.is-homepage #page-context-menu-button,body.is-homepage #page-context-menu-button,html.is-homepage #page-context-menu,body.is-homepage #page-context-menu,html.is-homepage .eyebrow,body.is-homepage .eyebrow{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important}</style><script>if(location.pathname==='/flow-builder'){document.write('<style>header#header,#page-title,#page-context-menu-button,#page-context-menu,.eyebrow,#pagination{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style>');}</script><style>html:has(#flow-builder-container) header#header,html:has(#flow-builder-container) #page-title,html:has(#flow-builder-container) .eyebrow,html:has(#flow-builder-container) #page-context-menu-button,html:has(#flow-builder-container) #page-context-menu,html:has(#flow-builder-container) #pagination{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;margin:0!important;padding:0!important}</style><script>(function(){try{var c=localStorage.getItem('ls-nav-collapsed');var p=location.pathname;var demo=p==='/global-accounts/demo'||p==='/global-accounts/demo/';if(c==='1'||demo){document.documentElement.classList.add('ls-nav-collapsed');}}catch(e){}})();</script>",
"links": [
{
"rel": "preload",
Expand Down
5 changes: 5 additions & 0 deletions mintlify/images/icons/IconSidebarLeftArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions mintlify/images/icons/IconSidebarRightArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
243 changes: 243 additions & 0 deletions mintlify/sidebar-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Collapsible + resizable docs sidebar (desktop).
//
// A rail on the right edge of #sidebar-content:
// - drag it to resize the sidebar (clamped MIN..MAX); release below the snap
// threshold to collapse.
// - click it (no drag) to toggle collapse.
// Collapsed shows a slim visible rail with a bare icon (click/pointer to
// reopen); expanded shows the toggle on hover at the edge (col-resize). Width +
// collapsed state persist in localStorage and are restored pre-paint by the
// inline script in docs.json head.raw, so there's no flash.

(function () {
var DESKTOP_MIN = 1024;
var KEY = 'ls-nav-collapsed';
// NOTE: keep in sync with the demo-path check in docs.json head.raw — the
// pre-paint script collapses the playground before this runs (no flash). The
// path lives in both because the pre-paint must run inline, before this file.
var DEMO_PATHS = ['/global-accounts/demo', '/global-accounts/demo/'];
Comment thread
greptile-apps[bot] marked this conversation as resolved.
var MIN_WIDTH = 280; // the original sidebar width — only resizes wider
var MAX_WIDTH = 420;
var SNAP_COLLAPSE = 240; // drag left past this x -> collapse
var DRAG_THRESHOLD = 4; // px of movement before a press counts as a drag

var rail = null;

function isDesktop() {
return window.innerWidth >= DESKTOP_MIN;
}

function isDemo() {
return DEMO_PATHS.indexOf(location.pathname) !== -1;
}

// #sidebar-content is in the DOM on every docs page, but custom-layout pages
// (frontmatter mode: "custom", e.g. the flow builder) keep it and hide it
// (display:none). getClientRects() is empty for a non-rendered element, so this
// is true only when there's a real sidebar to toggle — no sidebar, no rail.
function hasVisibleSidebar() {
var sc = document.getElementById('sidebar-content');
return !!sc && sc.getClientRects().length > 0;
}

function getPref() {
try {
return localStorage.getItem(KEY);
} catch (e) {
return null;
}
}

function setPref(value) {
try {
localStorage.setItem(KEY, value);
} catch (e) {
/* private mode — toggle still works for the session */
}
}

function clampWidth(w) {
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.round(w)));
}

// Width is session-only — not persisted, so a refresh resets to the 280px
// default (the CSS var fallback) with no post-paint resize jump.
function applyWidth(w) {
document.documentElement.style.setProperty('--ls-sidebar-width', w + 'px');
}

// The playground (demo) always starts collapsed — it needs the horizontal
// space — regardless of the saved preference. Every other page follows the
// remembered preference (default expanded).
function shouldCollapse() {
if (isDemo()) return true;
return getPref() === '1';
}

function isCollapsed() {
return document.documentElement.classList.contains('ls-nav-collapsed');
}

function updateRail() {
if (!rail) return;
var collapsed = isCollapsed();
rail.setAttribute('aria-label', collapsed ? 'Show navigation' : 'Hide navigation');
rail.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
}

function applyState(collapsed) {
document.documentElement.classList.toggle('ls-nav-collapsed', collapsed);
updateRail();
}

function removeRail() {
if (rail && rail.parentNode) rail.parentNode.removeChild(rail);
rail = null;
}

function ensureRail() {
if (!isDesktop() || !hasVisibleSidebar()) {
removeRail();
return;
}
if (rail && document.body.contains(rail)) return;

rail = document.createElement('button');
rail.type = 'button';
rail.className = 'ls-nav-rail';
rail.innerHTML = '<span class="ls-nav-rail-btn" aria-hidden="true"></span>';
attachInteractions(rail);
document.body.appendChild(rail);
updateRail();
}

// Drag = resize (expanded only); plain click = toggle collapse (either state,
// mouse or keyboard).
function attachInteractions(el) {
var startX = 0;
var moved = false;
var dragging = false;
var dragEndAt = 0;
var animTimer = 0;

function onMove(e) {
if (!moved && Math.abs(e.clientX - startX) > DRAG_THRESHOLD) {
moved = true;
dragging = true;
document.documentElement.classList.add('ls-nav-dragging');
document.body.style.userSelect = 'none';
}
if (!dragging) return;
// Live: crossing the snap threshold collapses immediately (no release
// needed); dragging back out reopens and resumes resizing.
if (e.clientX < SNAP_COLLAPSE) {
if (!isCollapsed()) applyState(true);
} else {
if (isCollapsed()) applyState(false);
applyWidth(clampWidth(e.clientX));
}
}

function onUp(e) {
document.removeEventListener('mousemove', onMove, true);
document.removeEventListener('mouseup', onUp, true);
if (!moved) return; // a click — handled by the click listener
dragEndAt = Date.now();
document.documentElement.classList.remove('ls-nav-dragging');
document.body.style.userSelect = '';
// State was already applied live during the drag — just persist it.
if (isCollapsed()) {
setPref('1');
} else {
applyWidth(clampWidth(e.clientX));
setPref('0');
}
}

// Resize only from the expanded edge; the collapsed rail is click-only.
el.addEventListener('mousedown', function (e) {
if (e.button !== 0 || isCollapsed()) return;
e.preventDefault();
startX = e.clientX;
moved = false;
dragging = false;
document.addEventListener('mousemove', onMove, true);
document.addEventListener('mouseup', onUp, true);
});

el.addEventListener('click', function () {
if (Date.now() - dragEndAt < 300) return; // swallow the click after a drag
var next = !isCollapsed();
setPref(next ? '1' : '0');
// ls-nav-animating turns the collapse transition on for this deliberate
// toggle (it's off by default so navigation never animates) and suppresses
// the hover reveal so the button/edge don't flash from the cursor sitting
// over the rail mid-transition. Add it + force a reflow before applyState
// so the width/opacity change animates from the current value, not snaps.
document.documentElement.classList.add('ls-nav-animating');
document.documentElement.getBoundingClientRect(); // force reflow to arm the transition
applyState(next);
clearTimeout(animTimer);
animTimer = setTimeout(function () {
document.documentElement.classList.remove('ls-nav-animating');
}, 320);
});
}

function sync() {
var root = document.documentElement;
// Navigation/first paint must never animate (only deliberate toggles do —
// see the click handler). Clear the animate flag, and snap the rail button
// for this navigation-driven state change so its icon doesn't ghost in/out
// between pages; restore its transition next frame so hover reveals animate.
root.classList.remove('ls-nav-animating');
root.classList.add('ls-nav-snap');
applyState(shouldCollapse());
ensureRail();
requestAnimationFrame(function () {
root.classList.remove('ls-nav-snap');
});
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', sync);
} else {
sync();
}

// SPA navigation: Mintlify swaps content without a full reload. On a path
// change, re-sync. Otherwise only re-add the rail if Mintlify wiped it — a
// cheap guard so we don't read layout every frame. Custom-layout pages are
// handled by sync() on navigation plus the CSS :has(.is-custom) rule, so the
// rail never lingers visibly even without per-frame polling here.
var lastPath = location.pathname;
var ensureScheduled = false;
function scheduleEnsureRail() {
if (ensureScheduled) return;
ensureScheduled = true;
requestAnimationFrame(function () {
ensureScheduled = false;
ensureRail();
});
}
var observer = new MutationObserver(function () {
if (location.pathname !== lastPath) {
lastPath = location.pathname;
sync();
} else if (!rail || !document.body.contains(rail)) {
scheduleEnsureRail();
}
});
observer.observe(document.body, { childList: true, subtree: true });
window.addEventListener('popstate', sync);

var rafPending = false;
window.addEventListener('resize', function () {
if (rafPending) return;
rafPending = true;
requestAnimationFrame(function () {
rafPending = false;
ensureRail();
});
});
})();
Loading
Loading