docs: collapsible doc nav on mobile (0.91.0)
a mobile audit (no overflow anywhere; agent modal + on-this-page TOC both fine) turned up one weak spot: on phones the doc tree was a cramped 210px scroll area pinned above every page, and it never scrolled the active doc into view — so you couldn't tell which doc you were reading or reach half of them easily. - the sidebar now collapses to a compact bar showing the CURRENT doc + a chevron; tap to reveal the full tree (active doc highlighted), tap a doc to navigate and auto-collapse - desktop is byte-for-byte unchanged: the collapsible wrapper is display:contents off-mobile, so the tree stays a normal flex child (sidebar still 280px) - verified live: mobile collapsed shows 'API reference', expands to 8 docs, selecting Quickstart navigates + collapses + relabels; desktop toggle hidden, tree intact
This commit is contained in:
parent
0fd1ed2b5c
commit
e5e79d75aa
@ -17,7 +17,7 @@
|
|||||||
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }
|
||||||
</style>
|
</style>
|
||||||
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
||||||
<meta name="version" content="0.90.0-agenturls" />
|
<meta name="version" content="0.91.0-docsnav" />
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amerc-site",
|
"name": "amerc-site",
|
||||||
"version": "0.90.0-agenturls",
|
"version": "0.91.0-docsnav",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -33,6 +33,7 @@ function DocsWorkspace({ canEdit }) {
|
|||||||
const [draft, setDraft] = useState({ title: '', folder: '', body: '' });
|
const [draft, setDraft] = useState({ title: '', folder: '', body: '' });
|
||||||
const [err, setErr] = useState('');
|
const [err, setErr] = useState('');
|
||||||
const [q, setQ] = useState('');
|
const [q, setQ] = useState('');
|
||||||
|
const [navOpen, setNavOpen] = useState(false);
|
||||||
const renderRef = useRef(null);
|
const renderRef = useRef(null);
|
||||||
const pendingSection = useRef(null);
|
const pendingSection = useRef(null);
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ function DocsWorkspace({ canEdit }) {
|
|||||||
// shareable doc + section URLs: #/<doc-slug> or #/<doc-slug>/<section-slug>
|
// shareable doc + section URLs: #/<doc-slug> or #/<doc-slug>/<section-slug>
|
||||||
const openDoc = useCallback((d) => {
|
const openDoc = useCallback((d) => {
|
||||||
setSel(d.id);
|
setSel(d.id);
|
||||||
|
setNavOpen(false);
|
||||||
pendingSection.current = null;
|
pendingSection.current = null;
|
||||||
const slug = slugify(d.title);
|
const slug = slugify(d.title);
|
||||||
if (window.location.hash !== `#/${slug}`) window.history.pushState(null, '', `${window.location.pathname}${window.location.search}#/${slug}`);
|
if (window.location.hash !== `#/${slug}`) window.history.pushState(null, '', `${window.location.pathname}${window.location.search}#/${slug}`);
|
||||||
@ -134,6 +136,11 @@ function DocsWorkspace({ canEdit }) {
|
|||||||
return (
|
return (
|
||||||
<div className="docs">
|
<div className="docs">
|
||||||
<aside className="docs-side">
|
<aside className="docs-side">
|
||||||
|
<button className="docs-nav-toggle" type="button" onClick={() => setNavOpen((v) => !v)} aria-expanded={navOpen}>
|
||||||
|
<span className="docs-nav-cur">{doc ? doc.title : 'Documents'}</span>
|
||||||
|
<span className="docs-nav-chev" aria-hidden="true">▾</span>
|
||||||
|
</button>
|
||||||
|
<div className={`docs-side-body${navOpen ? ' open' : ''}`}>
|
||||||
<div className="docs-side-top">
|
<div className="docs-side-top">
|
||||||
<input placeholder="search docs…" value={q} onChange={(e) => setQ(e.target.value)} />
|
<input placeholder="search docs…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||||
{canEdit && <button className="cs-btn cs-primary" onClick={create}>+ New</button>}
|
{canEdit && <button className="cs-btn cs-primary" onClick={create}>+ New</button>}
|
||||||
@ -149,6 +156,7 @@ function DocsWorkspace({ canEdit }) {
|
|||||||
))}
|
))}
|
||||||
{!filtered.length && <p className="cs-hint">No documents yet. Create one — humans and agents (via API key) can both read & write here.</p>}
|
{!filtered.length && <p className="cs-hint">No documents yet. Create one — humans and agents (via API key) can both read & write here.</p>}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<section className="docs-main">
|
<section className="docs-main">
|
||||||
{err && <p className="cs-err">{err}</p>}
|
{err && <p className="cs-err">{err}</p>}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { api } from './api.js';
|
|||||||
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
|
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
|
||||||
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);
|
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);
|
||||||
|
|
||||||
const RELEASE = '0.90.0-agenturls';
|
const RELEASE = '0.91.0-docsnav';
|
||||||
|
|
||||||
const NAV = [
|
const NAV = [
|
||||||
{ id: 'home', label: 'Tavern' },
|
{ id: 'home', label: 'Tavern' },
|
||||||
|
|||||||
@ -837,6 +837,12 @@ button {
|
|||||||
.docs-side-top { display: flex; gap: 8px; padding: 12px; border-bottom: 1px solid #1b2536; }
|
.docs-side-top { display: flex; gap: 8px; padding: 12px; border-bottom: 1px solid #1b2536; }
|
||||||
.docs-side-top input { flex: 1; padding: 8px; border-radius: 7px; background: #0a0f1a; border: 1px solid #233044; color: #eaf2ff; }
|
.docs-side-top input { flex: 1; padding: 8px; border-radius: 7px; background: #0a0f1a; border: 1px solid #233044; color: #eaf2ff; }
|
||||||
.docs-tree { overflow: auto; padding: 8px; }
|
.docs-tree { overflow: auto; padding: 8px; }
|
||||||
|
/* docs nav: collapsible on mobile, transparent (display:contents) on desktop (0.91) */
|
||||||
|
.docs-nav-toggle { display: none; width: 100%; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; background: #0c111c; border: none; border-bottom: 1px solid #1b2536; color: #eaf2ff; font-family: inherit; font-size: 14px; cursor: pointer; text-align: left; }
|
||||||
|
.docs-nav-cur { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.docs-nav-chev { color: #8a78b4; flex: 0 0 auto; font-size: 12px; transition: transform 0.18s; }
|
||||||
|
.docs-nav-toggle[aria-expanded="true"] .docs-nav-chev { transform: rotate(180deg); }
|
||||||
|
.docs-side-body { display: contents; }
|
||||||
.docs-folder-name { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #5f6e88; margin: 10px 6px 4px; }
|
.docs-folder-name { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #5f6e88; margin: 10px 6px 4px; }
|
||||||
.docs-item { display: block; width: 100%; text-align: left; padding: 7px 10px; border-radius: 7px; color: #c3d0e6; cursor: pointer; font-size: 13px; }
|
.docs-item { display: block; width: 100%; text-align: left; padding: 7px 10px; border-radius: 7px; color: #c3d0e6; cursor: pointer; font-size: 13px; }
|
||||||
.docs-item:hover { background: #141d2e; }
|
.docs-item:hover { background: #141d2e; }
|
||||||
@ -1484,7 +1490,11 @@ button {
|
|||||||
/* docs responsive — stack on mobile so the prose gets full width (0.77) */
|
/* docs responsive — stack on mobile so the prose gets full width (0.77) */
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.docs { flex-direction: column; }
|
.docs { flex-direction: column; }
|
||||||
.docs-side { width: 100%; max-height: 210px; overflow-y: auto; border-right: none; border-bottom: 2px solid #1b2536; }
|
.docs-side { width: 100%; overflow: visible; border-right: none; border-bottom: 2px solid #1b2536; }
|
||||||
|
.docs-nav-toggle { display: flex; }
|
||||||
|
.docs-side-body { display: none; }
|
||||||
|
.docs-side-body.open { display: block; border-bottom: 1px solid #1b2536; }
|
||||||
|
.docs-side-body.open .docs-tree { max-height: 58vh; overflow-y: auto; }
|
||||||
.docs-main { padding: 20px 16px; }
|
.docs-main { padding: 20px 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user