### Setup Speaker Notes UI
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index_files/libs/revealjs/plugin/notes/speaker-view.html
Initializes variables for accessing the speaker notes elements within the DOM.
```javascript
function setupNotes() {
notes = document.querySelector( '.speaker-controls-notes' );
notesValue = document.querySelector( '.speaker-controls-notes .value' );
}
```
--------------------------------
### Matrix Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Create a Matrix diagram, typically a 2x2 grid. Each 'item' div represents a quadrant.
```html
Question Marks
Stars
Dogs
Cash Cows
```
--------------------------------
### Pie Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Render a Pie diagram with items as wedges. Each 'item' div corresponds to a slice of the pie.
```html
Alice
Bob
Carol
David
Eve
```
--------------------------------
### Pyramid Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Generate a Pyramid diagram with stacked bands narrowing towards the top. Each 'item' div represents a level.
```html
Vision
Strategy
Tactics
Operations
```
--------------------------------
### Setup Heartbeat for Connection
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index_files/libs/revealjs/plugin/notes/speaker-view.html
Establishes a recurring heartbeat to maintain a connection with the main presentation window, enabling reconnection after reloads.
```javascript
function setupHeartbeat() {
setInterval( () => {
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'heartbeat'})
, '*'
);
}, 1000 );
}
```
--------------------------------
### Initialize Venn Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/process.html
Initializes the Venn diagram setup by selecting the relevant items from the container. Supports 2- and 3-set diagrams.
```javascript
function initVenn(container) { const items = Array.from(container.querySelectorAll('.item')); if (items.length < 2 || items.length > 3) return; // only 2- and 3-set const n = items.length; const palet
```
--------------------------------
### Initialize Speaker View and Connection Logic
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index_files/libs/revealjs/plugin/notes/speaker-view.html
Sets up the speaker view, establishes a connection with the main presentation window, and handles cross-origin security checks. It includes logic for message handling, state synchronization, and API calls to the main Reveal.js instance.
```javascript
(function() { var notes, notesValue, currentState, currentSlide, upcomingSlide, layoutLabel, layoutDropdown, pendingCalls = {}, lastRevealApiCallId = 0, connected = false; var connectionStatus = document.querySelector( '#connection-status' ); var SPEAKER_LAYOUTS = { 'default': 'Default', 'wide': 'Wide', 'tall': 'Tall', 'notes-only': 'Notes only' }; setupLayout(); let openerOrigin; try { openerOrigin = window.opener.location.origin; } catch ( error ) { console.warn( error ) } // In order to prevent XSS, the speaker view will only run if its // opener has the same origin as itself if( window.location.origin !== openerOrigin ) { connectionStatus.innerHTML = 'Cross origin error.\n
The speaker window can only be opened from the same origin.'; return; } var connectionTimeout = setTimeout( function() { connectionStatus.innerHTML = 'Error connecting to main window.\n
Please try closing and reopening the speaker view.'; }, 5000 ); window.addEventListener( 'message', function( event ) { // Validate the origin of all messages to avoid parsing messages // that aren't meant for us. Ignore when running off file:// so // that the speaker view continues to work without a web server. if( window.location.origin !== event.origin && window.location.origin !== 'file://' ) { return } clearTimeout( connectionTimeout ); connectionStatus.style.display = 'none'; var data = JSON.parse( event.data ); // The overview mode is only useful to the reveal.js instance // where navigation occurs so we don't sync it if( data.state ) delete data.state.overview; // Messages sent by the notes plugin inside of the main window if( data && data.namespace === 'reveal-notes' ) { if( data.type === 'connect' ) { handleConnectMessage( data ); } else if( data.type === 'state' ) { handleStateMessage( data ); } else if( data.type === 'return' ) { pendingCalls[data.callId](data.result); delete pendingCalls[data.callId]; } } // Messages sent by the reveal.js inside of the current slide preview else if( data && data.namespace === 'reveal' ) { if( /ready/.test( data.eventName ) ) { // Send a message back to notify that the handshake is complete window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'}), '*' ); } else if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) { dispatchStateToMainWindow( data.state ); } } } ); / ** * Updates the presentation in the main window to match the state * of the presentation in the notes window. * / const dispatchStateToMainWindow = debounce(( state ) => { window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ state ]} ), '*' ); }, 500); / ** * Asynchronously calls the Reveal.js API of the main frame. * / function callRevealApi( methodName, methodArguments, callback ) { var callId = ++lastRevealApiCallId; pendingCalls[callId] = callback; window.opener.postMessage( JSON.stringify( { namespace: 'reveal-notes', type: 'call', callId: callId, methodName: methodName, arguments: methodArguments } ), '*' ); } / ** * Called when the main window is trying to establish a * connection. * / function handleConnectMessage( data ) { if( connected === false ) { connected = true; setupIframes( data ); setupKeyboard(); setupNotes(); setupTimer(); setupHeartbeat(); } } / ** * Called when the main window sends an updated state. * / function handleStateMessage( data ) { // Store the most recently set state to avoid circular loops // applying the same state currentState = JSON.stringify( data.state ); // No need for updating the notes in case of fragment changes if ( data.notes ) { notes.classList.remove( 'hidden' ); notesValue.style.whiteSpace = data.whitespace; if( data.markdown ) { notesValue.innerHTML = marked( data.notes ); } else { notesValue.innerHTML = data.notes; } } else { notes.classList.add( 'hidden' ); } // Update the note slides currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' ); upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' ); upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' ); } // Limit to max one state update per X ms handleStateMessage = debounce( handleStateMessage, 200 ); / ** * Forward keyboard events to the current slide window. * This enables keyboard events to work even if focus * isn't set on the current slide iframe. * * Block F5 default handling, it reloads and disconnects * the speaker notes window. * / function setupKeyboard() { document.addEventListener( 'keydown', function( event ) { if( event.keyCode === 116 || ( event.metaKey && event.keyCode === 82 ) ) { event.preventDefault(); return false; } currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' ); } ); } / ** * Creates the preview iframes. * / function setupIframes( data ) {
```
--------------------------------
### Install quarto-diagrams Extension
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Install the quarto-diagrams extension using the Quarto CLI. This command adds the extension to your project.
```bash
quarto add EmilHvitfeldt/quarto-diagrams
```
--------------------------------
### Render SVG Arrows (Thin/Double Types)
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/_extensions/diagrams/diagrams.html
Renders straight-line arrows with defined start and end markers. Supports 'double' arrows by adding a start marker.
```javascript
// thin and double — straight lines with arrowheads
const markerSize = 12;
addArrowMarker(defs, uid + '-end', arrowColor, markerSize, false);
if (arrowType === 'double') addArrowMarker(defs, uid + '-start', arrowColor, markerSize, true);
positions.forEach((from, i) => {
const to = positions[(i + 1) % n];
let p1 = nodeEdgePoint(from, to, nodeRadius, nodeType);
let p2 = nodeEdgePoint(to, from, nodeRadius, nodeType);
// ... rest of the code for rendering straight lines
```
--------------------------------
### Reveal.js Initialization with Full Configuration
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index.html
This snippet shows how to initialize Reveal.js with a wide range of configuration options. It covers presentation controls, animations, navigation, and various display settings. Use this as a template for customizing your Reveal.js presentations.
```javascript
window.backupDefine = window.define;
window.define = undefined;
window.define = window.backupDefine;
window.backupDefine = undefined;
// Full list of configuration options available at:
// https://revealjs.com/config/
Reveal.initialize({
'controlsAuto': true,
'previewLinksAuto': false,
'pdfSeparateFragments': false,
'autoAnimateEasing': "ease",
'autoAnimateDuration': 1,
'autoAnimateUnmatched': true,
'jumpToSlide': true,
'menu': {"side":"left","useTextContentForMissingTitles":true,"markers":false,"loadIcons":false,"custom":[{"title":"Tools","icon":"","content":""}],"openButton":true},
'smaller': false,
// Display controls in the bottom right corner
controls: false,
// Help the user learn the controls by providing hints, for example by
// bouncing the down arrow when they first encounter a vertical slide
controlsTutorial: false,
// Determines where controls appear, "edges" or "bottom-right"
controlsLayout: 'edges',
// Visibility rule for backwards navigation arrows; "faded", "hidden"
// or "visible"
controlsBackArrows: 'faded',
// Display a presentation progress bar
progress: true,
// Display the page number of the current slide
slideNumber: false,
// 'all', 'print', or 'speaker'
showSlideNumber: 'all',
// Add the current slide number to the URL hash so that reloading the
// page/copying the URL will return you to the same slide
hash: true,
// Start with 1 for the hash rather than 0
hashOneBasedIndex: false,
// Flags if we should monitor the hash and change slides accordingly
respondToHashChanges: true,
// Push each slide change to the browser history
history: true,
// Enable keyboard shortcuts for navigation
keyboard: true,
// Enable the slide overview mode
overview: true,
// Disables the default reveal.js slide layout (scaling and centering)
// so that you can use custom CSS layout
disableLayout: false,
// Vertical centering of slides
center: false,
// Enables touch navigation on devices with touch input
touch: true,
// Loop the presentation
loop: false,
// Change the presentation direction to be RTL
rtl: false,
// see https://revealjs.com/vertical-slides/#navigation-mode
navigationMode: 'linear',
// Randomizes the order of slides each time the presentation loads
shuffle: false,
// Turns fragments on and off globally
fragments: true,
// Flags whether to include the current fragment in the URL,
// so that reloading brings you to the same fragment position
fragmentInURL: false,
// Flags if the presentation is running in an embedded mode,
// i.e. contained within a limited portion of the screen
embedded: false,
// Flags if we should show a help overlay when the questionmark // key is pressed
help: true,
// Flags if it should be possible to pause the presentation (blackout)
pause: true,
// Flags if speaker notes should be visible to all viewers
showNotes: false,
// Global override for autoplaying embedded media (null/true/false)
autoPlayMedia: null,
// Global override for preloading lazy-loaded iframes (null/true/false)
preloadIframes: null,
// Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides
autoSlide: 0,
// Stop auto-sliding after user input
autoSlideStoppable: true,
// Use this method for navigation when auto-sliding
autoSlideMethod: null,
// Specify the average time in seconds that you think you will spend
// presenting each slide. This is used to show a pacing timer in the
// speaker view
defaultTiming: null,
// Enable slide navigation via mouse wheel
mouseWheel: false,
// The display mode that will be used to show slides
display: 'block',
// Hide cursor if inactive
hideInactiveCursor: true,
// Time before the cursor is hidden (in ms)
hideCursorTime: 5000,
// Opens links in an iframe preview overlay
previewLinks: false,
// Transition style (none/fade/slide/convex/concave/zoom)
transition: 'none',
// Transition speed (default/fast/slow)
transit
});
```
--------------------------------
### Initialize Matrix Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/colors.html
Sets up a matrix visualization, potentially with an outline. Further details depend on the specific implementation of '.item' elements and matrix structure.
```javascript
function initMatrix(container) { const items = Array.from(container.querySelectorAll('.item')); if (items.length === 0) return; const outline = container.classList.contains('outline'); const arrows = container.class
```
--------------------------------
### Start Local HTTP Server
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/CLAUDE.md
A local HTTP server is required for Playwright testing as file:// protocol is blocked. This command starts a server on port 8765.
```bash
python3 -m http.server 8765
```
--------------------------------
### Initialize Matrix Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/pie.html
Sets up a matrix diagram by selecting elements with the class 'item'. This function is a placeholder and requires further implementation for rendering the matrix.
```javascript
const items = Array.from(container.querySelectorAll('.item'));
if (items.length === 0) return;
```
--------------------------------
### Hierarchy Layout Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/DESIGN.md
This example demonstrates the basic structure for a hierarchy layout using nested 'item' divs. The nesting expresses parent-child relationships, and the 'CEO' is the root element.
```html
::::: hierarchy
:::: item
CEO
:::
::: item
CTO
:::
::: item
CFO
:::
::::
:::::
```
--------------------------------
### Initialize Matrix Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/customization.html
Sets up a 2x2 matrix diagram, drawing quadrant rectangles and optionally adding outlines and arrows. Labels are positioned at the center of each quadrant.
```javascript
const items = Array.from(container.querySelectorAll('.item'));
if (items.length === 0) return;
const outline = container.classList.contains('outline');
const arrows = container.classList.contains('arrows');
const hasGap = container.classList.contains('gap');
const defaultColor = container.dataset.nodeColor || '#2e6b8a';
const lineColor = container.dataset.arrowColor || '#333333';
// Square bounds (leave a gutter for axis labels/titles)
const x0 = 70, y0 = 60, x1 = 430, y1 = 420;
const cx = (x0 + x1) / 2, cy = (y0 + y1) / 2;
container.style.position = 'relative';
container.style.width = '500px';
container.style.height = '500px';
container.style.margin = '0 auto';
container.style.display = 'block';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '500');
svg.setAttribute('height', '500');
svg.style.cssText = 'position:absolute;left:0;top:0;overflow:visible;';
container.appendChild(svg);
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
svg.appendChild(defs);
// Quadrant rects in item order: TL, TR, BL, BR
const quads = [
{ x: x0, y: y0, w: cx - x0, h: cy - y0 },
{ x: cx, y: y0, w: x1 - cx, h: cy - y0 },
{ x: x0, y: cy, w: cx - x0, h: y1 - cy },
{ x: cx, y: cy, w: x1 - cx, h: y1 - cy }
];
const gapInset = 6;
const labelEls = [];
quads.forEach((q, i) => {
const item = items[i];
let rx = q.x, ry = q.y, rw = q.w, rh = q.h;
if (hasGap) {
rx += gapInset;
ry += gapInset;
rw -= 2 * gapInset;
rh -= 2 * gapInset;
}
if (!outline) {
const color = (item && (item.getAttribute('color') || item.dataset.color)) || defaultColor;
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', rx);
rect.setAttribute('y', ry);
rect.setAttribute('width', rw);
rect.setAttribute('height', rh);
rect.setAttribute('fill', color);
if (!hasGap) {
rect.setAttribute('stroke', 'white');
rect.setAttribute('stroke-width', '3');
}
svg.appendChild(rect);
} else {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', rx);
rect.setAttribute('y', ry);
rect.setAttribute('width', rw);
rect.setAttribute('height', rh);
rect.setAttribute('fill', 'none');
rect.setAttribute('stroke', lineColor);
rect.setAttribute('stroke-width', '2');
svg.appendChild(rect);
}
if (item) {
const label = document.createElement('div');
label.className = 'slice-label';
label.textContent = item.textContent.trim();
label.style.left = (q.x + q.w / 2) + 'px';
label.style.top = (q.y + q.h / 2) + 'px';
if (outline) label.style.color = '#222222';
container.appendChild(label);
labelEls.push(label);
item.style.display = 'none';
}
});
// Center cross lines (skipped in gap mode; outline mode draws its own cell borders)
if (!hasGap && !outline) {
[['v', cx, y0, cx, y1], ['h', x0, cy, x1, cy]].forEach(([axis, ax, ay, bx, by]) => {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', ax);
line.setAttribute('y1', ay);
line.setAttribute('x2', bx);
line.setAttribute('y2', by);
line.setAttribute('stroke', lineColor);
line.setAttribute('stroke-width', '2');
svg.appendChild(line);
});
}
```
--------------------------------
### Donut Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Create a Donut diagram, which is a pie with a hollow center. This can optionally include a label in the middle.
```html
Alice
Bob
Carol
David
```
--------------------------------
### Venn Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Generate a Venn diagram for two or three overlapping sets. Each 'item' div represents a set.
```html
Frontend
Backend
```
--------------------------------
### Initialize Process Flow Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs-src/_extensions/diagrams/diagrams.html
Sets up a process flow diagram based on container classes and data attributes, determining layout, node types, and arrow styles.
```javascript
function initProcess(container) {
const items = Array.from(container.querySelectorAll('.item'));
if (items.length === 0) return;
const n = items.length;
const vertical = container.classList.contains('vertical');
const nodeType = container.classList.contains('node-circle') ? 'circle' : container.classList.contains('node-none') ? 'none' : 'box';
const arrowType = container.classList.contains('arrow-thin') ? 'thin' : container.classList.contains('arrow-double') ? 'double' : 'chevron';
const chevron = container.classList.contains('chevron');
const defaultNodeColor = container.dataset.nodeColor || '#2e6b8a';
const arrowColor = container.dataset.arrowColor || '#2e6b8a';
container.style.position = 'relative';
const cs = getComputedStyle(container);
const totalW = parseFloat(cs.width) || (vertical ? 200 : 900);
const totalH = parseFloat(cs.height) || (vertical ? 600 : 200);
const mainAxis = vertical ? totalH : totalW;
const crossAxis = vertical ? totalW : totalH;
const gapWeights = items.map((item, i) => (i > 0 && item.classList.contains('gap')) ? 1.6 : 1);
const totalGapWeight = gapWeights.slice(1).reduce((a, b) => a + b, 0);
let boxCross, boxMain, arrowSlot = 0, notchPx = 0;
if (chevron) {
boxCross = Math.min(crossAxis * 0.9, (mainAxis / n) * 0.85);
boxMain = mainAxis / n;
notchPx = boxCross * 0.4;
const gutter = boxCross * 0.08;
const extraGap = boxCross * 0.35;
const gapPx = gapWeights.map(w => (w - 1) * extraGap);
const sumGap = gapPx.slice(1).reduce((a, b) => a + b, 0);
}
}
```
--------------------------------
### Basic Hierarchy with Node Styling
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/hierarchy.html
Demonstrates how to create a basic hierarchy and change node styles using `.node-circle` or `.node-none`. The default style is `.node-box`.
```html
::::: {.hierarchy .node-none}
:::: item
Root
:::
Child A
:::
:::
Child B
:::
::::
:::::
```
--------------------------------
### Initialize Speaker View Slides
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index_files/libs/revealjs/plugin/notes/speaker-view.html
Sets up the current and upcoming slide iframes for the speaker view. It constructs URLs with specific parameters for each slide.
```javascript
var params = [
'receiver',
'progress=false',
'history=false',
'transition=none',
'autoSlide=0',
'backgroundTransition=none'
].join( '&' );
var urlSeparator = /\?/.test(data.url) ? '&' : '?';
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
var currentURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&postMessageEvents=true' + hash;
var upcomingURL = data.url + urlSeparator + params + '&scrollActivationWidth=false&controls=false' + hash;
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );
currentSlide.setAttribute( 'height', 1024 );
currentSlide.setAttribute( 'src', currentURL );
document.querySelector( '#current-slide' ).appendChild( currentSlide );
upcomingSlide = document.createElement( 'iframe' );
upcomingSlide.setAttribute( 'width', 640 );
upcomingSlide.setAttribute( 'height', 512 );
upcomingSlide.setAttribute( 'src', upcomingURL );
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
```
--------------------------------
### Hierarchy Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Build a Hierarchy diagram (org chart/tree) from nested items. The nesting of 'item' divs defines the structure.
```html
```
--------------------------------
### Initialize Diagram Container and SVG
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/_extensions/diagrams/diagrams.html
Sets up the main container and SVG element for rendering the diagram. It configures basic styles for positioning and dimensions.
```javascript
const hasGap = container.classList.contains('gap');
const defaultColor = container.dataset.nodeColor || '#2e6b8a';
const lineColor = container.dataset.arrowColor || '#333333';
// Square bounds (leave a gutter for axis labels/titles)
const x0 = 70, y0 = 60, x1 = 430, y1 = 420;
const cx = (x0 + x1) / 2, cy = (y0 + y1) / 2;
container.style.position = 'relative';
container.style.width = '500px';
container.style.height = '500px';
container.style.margin = '0 auto';
container.style.display = 'block';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '500');
svg.setAttribute('height', '500');
svg.style.cssText = 'position:absolute;left:0;top:0;overflow:visible;';
container.appendChild(svg);
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
svg.appendChild(defs);
```
--------------------------------
### Initialize Process Diagram Layout
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/pie.html
Sets up the layout for a linear process diagram, calculating dimensions and spacing for nodes and arrows based on container size and specified styles.
```javascript
function initProcess(container) {
const items = Array.from(container.querySelectorAll('.item'));
if (items.length === 0) return;
const n = items.length;
const vertical = container.classList.contains('vertical');
const nodeType = container.classList.contains('node-circle') ? 'circle' : container.classList.contains('node-none') ? 'none' : 'box';
const arrowType = container.classList.contains('arrow-thin') ? 'thin' : container.classList.contains('arrow-double') ? 'double' : 'chevron';
const chevron = container.classList.contains('chevron');
const defaultNodeColor = container.dataset.nodeColor || '#2e6b8a';
const arrowColor = container.dataset.arrowColor || '#2e6b8a';
// Read container size from CSS (allows user override)
container.style.position = 'relative';
const cs = getComputedStyle(container);
const totalW = parseFloat(cs.width) || (vertical ? 200 : 900);
const totalH = parseFloat(cs.height) || (vertical ? 600 : 200);
const mainAxis = vertical ? totalH : totalW;
const crossAxis = vertical ? totalW : totalH;
// Per-item gap multiplier (extra space before .gap items, except the first)
const gapWeights = items.map((item, i) => (i > 0 && item.classList.contains('gap')) ? 1.6 : 1);
const totalGapWeight = gapWeights.slice(1).reduce((a, b) => a + b, 0);
let boxCross, boxMain, arrowSlot = 0, notchPx = 0;
const centers = [];
if (chevron) {
// Chevrons are the steps themselves: big interlocking tiles, no connectors.
boxCross = Math.min(crossAxis * 0.9, (mainAxis / n) * 0.85);
boxMain = mainAxis / n;
// refined below to account for overlap + gaps
notchPx = boxCross * 0.4;
const gutter = boxCross * 0.08; // thin slanted gap between tiles so seams read
const extraGap = boxCross * 0.35;
const gapPx = gapWeights.map(w => (w - 1) * extraGap);
const sumGap = gapPx.slice(1).reduce((a, b) => a + b, 0);
// Tiles interlock (tip nests in next notch) separated by a gutter; fill main
}
```
--------------------------------
### Reveal.js Configuration Options
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/index.html
Configuration settings for reveal.js, including background transitions, slide visibility, and presentation dimensions. MathJax configuration is also included.
```javascript
Reveal.initialize({
// Transition style for full page slide backgrounds
// (none/fade/slide/convex/concave/zoom)
backgroundTransition: 'none',
// Number of slides away from the current that are visible
viewDistance: 3,
// Number of slides away from the current that are visible on mobile
// devices. It is advisable to set this to a lower number than
// viewDistance in order to save resources.
mobileViewDistance: 2,
// The "normal" size of the presentation, aspect ratio will be preserved
// when the presentation is scaled to fit different resolutions. Can be
// specified using percentage units.
width: 1050,
height: 700,
// Factor of the display size that should remain empty around the content
margin: 0.1,
math: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js',
config: 'TeX-AMS_HTML-full',
tex2jax: {
inlineMath: [['\\(','\\)']],
displayMath: [['\\[','\\]']],
balanceBraces: true,
processEscapes: false,
processRefs: true,
processEnvironments: true,
preview: 'TeX',
skipTags: ['script','noscript','style','textarea','pre','code'],
ignoreClass: 'tex2jax_ignore',
processClass: 'tex2jax_process'
},
},
// reveal.js plugins
plugins: [QuartoLineHighlight, PdfExport, RevealMenu, QuartoSupport, RevealMath, RevealNotes, RevealSearch, RevealZoom ]
});
```
--------------------------------
### Get Direct Item Children for Hierarchy
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/node-shapes.html
Filters direct children of an element that have the 'item' class. This is used to traverse hierarchy structures.
```javascript
// Direct-child .item divs only (querySelectorAll would flatten the tree)
function directItemChildren(el) {
return Array.from(el.children).filter(c => c.classList.contains('item'));
}
```
--------------------------------
### Stacked Venn Diagram with Angle Control
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/stacked-venn.html
Use `angle=` to add clockwise rotation to the diagram. This example rotates the nesting towards the bottom-left.
```html
::: {.stacked-venn angle="45"}
:::
::: item
Everyone
:::
::: item
Customers
:::
::: item
Subscribers
:::
:::
```
--------------------------------
### Funnel Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Construct a Funnel diagram with narrowing stages. Stages can optionally be sized by value. Each 'item' div represents a stage.
```html
Visitors
Leads
Qualified
Customers
```
--------------------------------
### Initialize Venn Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/colors.html
Sets up a Venn diagram for 2 or 3 sets, applying colors and opacity. Labels for sets and overlaps are positioned and styled.
```javascript
function initVenn(container) {
const items = Array.from(container.querySelectorAll('.item'));
if (items.length < 2 || items.length > 3) return; // only 2- and 3-set
const n = items.length;
const palette = ['#2e6b8a', '#c0584f', '#5a9367'];
const baseColor = container.dataset.nodeColor;
container.style.position = 'relative';
container.style.width = '500px';
container.style.height = '500px';
container.style.margin = '0 auto';
container.style.display = 'block';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '500');
svg.setAttribute('height', '500');
svg.style.cssText = 'position:absolute;left:0;top:0;';
container.appendChild(svg);
// Circle centers/radius per set count
const circles = n === 2 ?
[{ cx: 175, cy: 250, r: 150 }, { cx: 325, cy: 250, r: 150 }]
: [{ cx: 250, cy: 155, r: 140 }, { cx: 320, cy: 275, r: 140 }, { cx: 180, cy: 275, r: 140 }];
items.forEach((item, i) => {
const color = item.getAttribute('color') || item.dataset.color || baseColor || palette[i];
const c = circles[i];
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', c.cx);
circle.setAttribute('cy', c.cy);
circle.setAttribute('r', c.r);
circle.setAttribute('fill', color);
circle.setAttribute('fill-opacity', '0.45');
circle.style.mixBlendMode = 'multiply';
svg.appendChild(circle);
item.style.display = 'none';
});
// Labels: set labels (from item text) + overlap labels (from container attrs)
const setLabels = n === 2 ?
[{ x: 120, y: 250 }, { x: 380, y: 250 }]
: [{ x: 250, y: 95 }, { x: 380, y: 330 }, { x: 120, y: 330 }];
const overlaps = n === 2 ?
[{ key: 'ab', x: 250, y: 250 }]
: [{ key: 'ab', x: 320, y: 200 }, { key: 'ac', x: 180, y: 200 }, { key: 'bc', x: 250, y: 330 }, { key: 'abc', x: 250, y: 245 }];
const labelEls = [];
items.forEach((item, i) => {
const label = document.createElement('div');
label.className = 'set-label';
label.textContent = item.textContent.trim();
label.style.left = setLabels[i].x + 'px';
label.style.top = setLabels[i].y + 'px';
container.appendChild(label);
labelEls.push(label);
});
const overlapColor = container.dataset.overlapColor;
overlaps.forEach(o => {
const text = container.dataset[o.key];
if (!text) return;
const label = document.createElement('div');
label.className = 'overlap-label';
label.textContent = text;
label.style.left = o.x + 'px';
label.style.top = o.y + 'px';
if (overlapColor) label.style.color = overlapColor;
container.appendChild(label);
});
// Shrink set labels so the longest fits wi
```
--------------------------------
### Circle Flow Diagram Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/index.html
Create a Circle Flow diagram using nested divs. Each 'item' div represents a node on the ring.
```html
Alice
Bob
Carol
```
--------------------------------
### Pyramid Layout Example
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/DESIGN.md
Use the `.pyramid` class on the container to create a stacked-triangle layout. Each `.item` represents a horizontal band, with the first item being the apex and the last being the base.
```html
::: pyramid
:::
::: item
Vision
:::
::: item
Strategy
:::
:::
:::
```
--------------------------------
### Get Own Label for Hierarchy Node
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/node-shapes.html
Extracts the text content of a node, excluding any nested '.item' children. This represents the node's own label.
```javascript
// A node's own label = its text excluding any nested .item children
function ownLabel(el) {
const clone = el.cloneNode(true);
Array.from(clone.children)
.filter(c => c.classList.contains('item'))
.forEach(c => clone.removeChild(c));
return clone.textContent.trim();
}
```
--------------------------------
### Initialize Pyramid Diagram
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/funnel.html
Sets up the SVG container and calculates dimensions for drawing pyramid shapes. Handles inverted and gapped pyramid configurations.
```javascript
// --- Pyramid (stacked triangle bands) ---
function initPyramid(container) {
const items = Array.from(container.querySelectorAll('.item'));
if (items.length === 0) return;
const n = items.length;
const cx = 250;
const apexY = 20, baseY = 480, H = baseY - apexY;
const baseHalf = 230;
const inverted = container.classList.contains('inverted');
const hasGap = container.classList.contains('gap');
const defaultColor = container.dataset.nodeColor || '#2e6b8a';
container.style.position = 'relative';
container.style.width = '500px';
container.style.height = '500px';
container.style.margin = '0 auto';
container.style.display = 'block';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '500');
svg.setAttribute('height', '500');
svg.style.cssText = 'position:absolute;left:0;top:0;';
container.appendChild(svg);
// half-width of the triangle at vertical fraction p (0 = top, 1 = bottom)
const halfAt = p => (inverted ? (1 - p) : p) * baseHalf;
const labelEls = [];
const bandW
```
--------------------------------
### Get Direct Item Children in Hierarchy
Source: https://github.com/emilhvitfeldt/quarto-diagrams/blob/main/docs/hierarchy.html
Filters the direct children of an element to include only those with the 'item' class. Used for traversing hierarchy structures.
```javascript
// Direct-child .item divs only (querySelectorAll would flatten the tree)
function directItemChildren(el) {
return Array.from(el.children).filter(c => c.classList.contains('item'));
}
```