### 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
CEO
CTO
CFO
CPO
``` -------------------------------- ### 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')); } ```