Skip to content

Commit 224d38d

Browse files
committed
Drop the eyebrow; add the docs' top page controls
The docs have no eyebrow line, so compiled pages no longer render one (old sources still parse; the field is ignored). The title row gains the docs' top controls: Copy page (copies the page text) and prev/next chevrons wired to the same neighbors as the footer cards, disabled-dim when a side is missing.
1 parent 39bac75 commit 224d38d

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

‎apps/sim/lib/workspace-files/artifact-stylesheet.ts‎

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,27 @@ figcaption { font-size: 0.875em; line-height: 1.4285714; color: var(--text-prima
430430
border-radius: 0.5rem;
431431
}
432432
.art-search::placeholder { color: var(--text-muted); }
433+
/* Top-of-page controls: Copy page + prev/next chevrons on the title row. */
434+
.page-actions {
435+
display: flex; justify-content: flex-end; align-items: center; gap: 2px;
436+
margin-bottom: -2.4rem; position: relative; z-index: 2;
437+
}
438+
.pa-copy {
439+
display: inline-flex; align-items: center; gap: 6px;
440+
font: inherit; font-size: var(--text-sm); color: var(--text-body);
441+
background: none; border: none; border-radius: 0.5rem;
442+
padding: 6px 8px; cursor: pointer; transition: background-color 0.15s;
443+
}
444+
.pa-copy:hover { background: var(--surface-active); }
445+
.pa-copy svg { color: var(--text-icon); }
446+
.pa-nav {
447+
display: inline-flex; align-items: center; justify-content: center;
448+
width: 30px; height: 30px; border-radius: 0.5rem;
449+
color: var(--text-icon); transition: background-color 0.15s;
450+
}
451+
.pa-nav:hover { background: var(--surface-active); opacity: 1; }
452+
.pa-nav.is-disabled { opacity: 0.35; pointer-events: none; }
453+
433454
/* The docs' theme toggle: 30px, rounded-lg, --text-icon, --surface-active hover. */
434455
.art-theme {
435456
display: inline-flex; align-items: center; justify-content: center;
@@ -611,6 +632,7 @@ export const SIM_ARTIFACT_SHELL = `<script>
611632
if (headings.length === 0) return
612633
page.dataset.shell = 'ready'
613634
635+
const ICON_ATTRS = 'viewBox="-1 -2 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.55" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"'
614636
const slug = (text, i) =>
615637
(text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'section') + '-' + i
616638
headings.forEach((h, i) => { if (!h.id) h.id = slug(h.textContent || '', i) })
@@ -693,7 +715,6 @@ export const SIM_ARTIFACT_SHELL = `<script>
693715
// stylesheet block, so flipping the attribute is the whole mechanism.
694716
// Storage is unavailable in the sandboxed preview, so the choice lives
695717
// for the page view.
696-
const ICON_ATTRS = 'viewBox="-1 -2 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.55" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"'
697718
const SUN = '<svg ' + ICON_ATTRS + '><circle cx="10.25" cy="9.75" r="4"/><path d="M10.25 3.75V1.25M16.25 9.75H18.75M10.25 15.75V18.25M4.25 9.75H1.75"/><path d="M14.49 5.51L16.26 3.74M14.49 13.99L16.26 15.76M6.01 13.99L4.24 15.76M6.01 5.51L4.24 3.74"/></svg>'
698719
const MOON = '<svg ' + ICON_ATTRS + '><path d="M10.25 1.5A5.84 5.84 0 0 0 18.5 9.75A8.25 8.25 0 1 1 10.25 1.5Z"/></svg>'
699720
const themeButton = document.createElement('button')
@@ -761,6 +782,45 @@ export const SIM_ARTIFACT_SHELL = `<script>
761782
}
762783
cols.append(left, mid, right)
763784
785+
// Top-of-page controls, like the docs: Copy page plus prev/next
786+
// chevrons on the title row, wired to the same targets as the footer.
787+
const DUPLICATE_ICON = '<svg ' + ICON_ATTRS + '><path d="M14.25 0.75H2.75C1.64543 0.75 0.75 1.64543 0.75 2.75V14.25"/><rect x="5.25" y="5.25" width="14" height="14" rx="2"/></svg>'
788+
const CHEV_L = '<svg ' + ICON_ATTRS + '><path d="M13.25 3L6.25 10.25L13.25 17.5"/></svg>'
789+
const CHEV_R = '<svg ' + ICON_ATTRS + '><path d="M6.25 3L13.25 10.25L6.25 17.5"/></svg>'
790+
const actions = document.createElement('div')
791+
actions.className = 'page-actions'
792+
const copyBtn = document.createElement('button')
793+
copyBtn.className = 'pa-copy'
794+
copyBtn.type = 'button'
795+
copyBtn.innerHTML = DUPLICATE_ICON + '<span>Copy page</span>'
796+
copyBtn.addEventListener('click', async () => {
797+
const label = copyBtn.querySelector('span')
798+
try {
799+
await navigator.clipboard.writeText(mid.innerText || '')
800+
if (label) label.textContent = 'Copied'
801+
} catch {
802+
if (label) label.textContent = 'Select + copy'
803+
}
804+
setTimeout(() => { const l = copyBtn.querySelector('span'); if (l) l.textContent = 'Copy page' }, 1500)
805+
})
806+
actions.appendChild(copyBtn)
807+
const prevCard = page.querySelector('.page-nav-card.prev')
808+
const nextCard = page.querySelector('.page-nav-card.next')
809+
if (prevCard || nextCard) {
810+
const mkNav = (card, svg, label) => {
811+
const a = document.createElement('a')
812+
a.className = 'pa-nav'
813+
a.setAttribute('aria-label', label)
814+
const href = card ? card.getAttribute('href') : null
815+
if (href) a.href = href
816+
else a.classList.add('is-disabled')
817+
a.innerHTML = svg
818+
return a
819+
}
820+
actions.append(mkNav(prevCard, CHEV_L, 'Previous page'), mkNav(nextCard, CHEV_R, 'Next page'))
821+
}
822+
mid.insertBefore(actions, mid.firstChild)
823+
764824
const navLinks = [...leftList.querySelectorAll('a')]
765825
search.addEventListener('input', () => {
766826
const q = search.value.trim().toLowerCase()

‎apps/sim/lib/workspace-files/page-compile.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const SIM_PAGE_CONTENT_TYPE = 'text/x-sim-page'
3636

3737
const frontmatterSchema = z.object({
3838
title: z.string().min(1),
39+
/** Tolerated for old sources; no longer rendered — the docs have no eyebrow. */
3940
eyebrow: z.string().optional(),
4041
lede: z.string().optional(),
4142
layout: z.enum(['docs', 'report']).optional(),
@@ -362,7 +363,6 @@ function compileSimPageDocument(source: string, lenient = false): string {
362363
'</head>',
363364
'<body>',
364365
`<div class="page" data-layout="${meta.layout ?? 'docs'}">`,
365-
...(meta.eyebrow ? [`<p class="eyebrow">${escapeHtml(meta.eyebrow)}</p>`] : []),
366366
`<h1>${escapeHtml(meta.title)}</h1>`,
367367
...(meta.lede ? [`<p class="lede">${escapeHtml(meta.lede)}</p>`] : []),
368368
compileBody(rest, lenient),

0 commit comments

Comments
 (0)