| | <!DOCTYPE html> |
| | <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head> |
| |
|
| | <meta charset="utf-8"> |
| | <meta name="generator" content="quarto-1.8.27"> |
| |
|
| | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> |
| |
|
| |
|
| | <title>1 What is an AI Pattern? – AI Design Patterns for GLAM</title> |
| | <style> |
| | code{white-space: pre-wrap;} |
| | span.smallcaps{font-variant: small-caps;} |
| | div.columns{display: flex; gap: min(4vw, 1.5em);} |
| | div.column{flex: auto; overflow-x: auto;} |
| | div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} |
| | ul.task-list{list-style: none;} |
| | ul.task-list li input[type="checkbox"] { |
| | width: 0.8em; |
| | margin: 0 0.8em 0.2em -1em; |
| | vertical-align: middle; |
| | } |
| | </style> |
| |
|
| |
|
| | <script src="../site_libs/quarto-nav/quarto-nav.js"></script> |
| | <script src="../site_libs/quarto-nav/headroom.min.js"></script> |
| | <script src="../site_libs/clipboard/clipboard.min.js"></script> |
| | <script src="../site_libs/quarto-search/autocomplete.umd.js"></script> |
| | <script src="../site_libs/quarto-search/fuse.min.js"></script> |
| | <script src="../site_libs/quarto-search/quarto-search.js"></script> |
| | <meta name="quarto:offset" content="../"> |
| | <link href="../patterns/structured-generation/intro.html" rel="next"> |
| | <link href="../index.html" rel="prev"> |
| | <script src="../site_libs/quarto-html/quarto.js" type="module"></script> |
| | <script src="../site_libs/quarto-html/tabsets/tabsets.js" type="module"></script> |
| | <script src="../site_libs/quarto-html/axe/axe-check.js" type="module"></script> |
| | <script src="../site_libs/quarto-html/popper.min.js"></script> |
| | <script src="../site_libs/quarto-html/tippy.umd.min.js"></script> |
| | <script src="../site_libs/quarto-html/anchor.min.js"></script> |
| | <link href="../site_libs/quarto-html/tippy.css" rel="stylesheet"> |
| | <link href="../site_libs/quarto-html/quarto-syntax-highlighting-ed96de9b727972fe78a7b5d16c58bf87.css" rel="stylesheet" id="quarto-text-highlighting-styles"> |
| | <script src="../site_libs/bootstrap/bootstrap.min.js"></script> |
| | <link href="../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet"> |
| | <link href="../site_libs/bootstrap/bootstrap-27c261d06b905028a18691de25d09dde.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light"> |
| | <script id="quarto-search-options" type="application/json">{ |
| | "location": "sidebar", |
| | "copy-button": false, |
| | "collapse-after": 3, |
| | "panel-placement": "start", |
| | "type": "textbox", |
| | "limit": 50, |
| | "keyboard-shortcut": [ |
| | "f", |
| | "/", |
| | "s" |
| | ], |
| | "show-item-context": false, |
| | "language": { |
| | "search-no-results-text": "No results", |
| | "search-matching-documents-text": "matching documents", |
| | "search-copy-link-title": "Copy link to search", |
| | "search-hide-matches-text": "Hide additional matches", |
| | "search-more-match-text": "more match in this document", |
| | "search-more-matches-text": "more matches in this document", |
| | "search-clear-button-title": "Clear", |
| | "search-text-placeholder": "", |
| | "search-detached-cancel-button-title": "Cancel", |
| | "search-submit-button-title": "Submit", |
| | "search-label": "Search" |
| | } |
| | }</script> |
| |
|
| |
|
| | </head> |
| |
|
| | <body class="nav-sidebar floating quarto-light"> |
| |
|
| | <div id="quarto-search-results"></div> |
| | <header id="quarto-header" class="headroom fixed-top"> |
| | <nav class="quarto-secondary-nav"> |
| | <div class="container-fluid d-flex"> |
| | <button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }"> |
| | <i class="bi bi-layout-text-sidebar-reverse"></i> |
| | </button> |
| | <nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="../patterns/what-is-an-ai-pattern.html">Design Patterns</a></li><li class="breadcrumb-item"><a href="../patterns/what-is-an-ai-pattern.html"><span class="chapter-number">1</span> <span class="chapter-title">What is an AI Pattern?</span></a></li></ol></nav> |
| | <a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }"> |
| | </a> |
| | <button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();"> |
| | <i class="bi bi-search"></i> |
| | </button> |
| | </div> |
| | </nav> |
| | </header> |
| | |
| | <div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article"> |
| | |
| | <nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto"> |
| | <div class="pt-lg-2 mt-2 text-left sidebar-header"> |
| | <div class="sidebar-title mb-0 py-0"> |
| | <a href="../">AI Design Patterns for GLAM</a> |
| | </div> |
| | </div> |
| | <div class="mt-2 flex-shrink-0 align-items-center"> |
| | <div class="sidebar-search"> |
| | <div id="quarto-search" class="" title="Search"></div> |
| | </div> |
| | </div> |
| | <div class="sidebar-menu-container"> |
| | <ul class="list-unstyled mt-1"> |
| | <li class="sidebar-item"> |
| | <div class="sidebar-item-container"> |
| | <a href="../index.html" class="sidebar-item-text sidebar-link"> |
| | <span class="menu-text">Welcome</span></a> |
| | </div> |
| | </li> |
| | <li class="sidebar-item sidebar-item-section"> |
| | <span class="sidebar-item-text sidebar-link text-start"> |
| | <span class="menu-text">Beyond Chat Interfaces to Collections?</span></span> |
| | </li> |
| | <li class="sidebar-item sidebar-item-section"> |
| | <div class="sidebar-item-container"> |
| | <a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true"> |
| | <span class="menu-text">Design Patterns</span></a> |
| | <a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true" aria-label="Toggle section"> |
| | <i class="bi bi-chevron-right ms-2"></i> |
| | </a> |
| | </div> |
| | <ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 show"> |
| | <li class="sidebar-item"> |
| | <div class="sidebar-item-container"> |
| | <a href="../patterns/what-is-an-ai-pattern.html" class="sidebar-item-text sidebar-link active"> |
| | <span class="menu-text"><span class="chapter-number">1</span> <span class="chapter-title">What is an AI Pattern?</span></span></a> |
| | </div> |
| | </li> |
| | </ul> |
| | </li> |
| | <li class="sidebar-item sidebar-item-section"> |
| | <div class="sidebar-item-container"> |
| | <a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true"> |
| | <span class="menu-text">Structured Information Extraction</span></a> |
| | <a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true" aria-label="Toggle section"> |
| | <i class="bi bi-chevron-right ms-2"></i> |
| | </a> |
| | </div> |
| | <ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 show"> |
| | <li class="sidebar-item"> |
| | <div class="sidebar-item-container"> |
| | <a href="../patterns/structured-generation/intro.html" class="sidebar-item-text sidebar-link"> |
| | <span class="menu-text"><span class="chapter-number">2</span> <span class="chapter-title">Structured Document Processing</span></span></a> |
| | </div> |
| | </li> |
| | <li class="sidebar-item"> |
| | <div class="sidebar-item-container"> |
| | <a href="../patterns/structured-generation/vlm-structured-generation.html" class="sidebar-item-text sidebar-link"> |
| | <span class="menu-text"><span class="chapter-number">3</span> <span class="chapter-title">Structured Information Extraction with Vision Language Models</span></span></a> |
| | </div> |
| | </li> |
| | </ul> |
| | </li> |
| | </ul> |
| | </div> |
| | </nav> |
| | <div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div> |
| | |
| | <div id="quarto-margin-sidebar" class="sidebar margin-sidebar"> |
| | <nav id="TOC" role="doc-toc" class="toc-active"> |
| | <h2 id="toc-title">Table of contents</h2> |
| | |
| | <ul> |
| | <li><a href="#why-patterns" id="toc-why-patterns" class="nav-link active" data-scroll-target="#why-patterns"><span class="header-section-number">1.1</span> Why Patterns?</a></li> |
| | <li><a href="#anatomy-of-a-pattern" id="toc-anatomy-of-a-pattern" class="nav-link" data-scroll-target="#anatomy-of-a-pattern"><span class="header-section-number">1.2</span> Anatomy of a Pattern</a></li> |
| | <li><a href="#patterns-in-this-book" id="toc-patterns-in-this-book" class="nav-link" data-scroll-target="#patterns-in-this-book"><span class="header-section-number">1.3</span> Patterns in This Book</a></li> |
| | </ul> |
| | </nav> |
| | </div> |
| | |
| | <main class="content" id="quarto-document-content"> |
| |
|
| | <header id="title-block-header" class="quarto-title-block default"><nav class="quarto-page-breadcrumbs quarto-title-breadcrumbs d-none d-lg-block" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="../patterns/what-is-an-ai-pattern.html">Design Patterns</a></li><li class="breadcrumb-item"><a href="../patterns/what-is-an-ai-pattern.html"><span class="chapter-number">1</span> <span class="chapter-title">What is an AI Pattern?</span></a></li></ol></nav> |
| | <div class="quarto-title"> |
| | <h1 class="title"><span class="chapter-number">1</span> <span class="chapter-title">What is an AI Pattern?</span></h1> |
| | </div> |
| |
|
| |
|
| |
|
| | <div class="quarto-title-meta"> |
| |
|
| | |
| | |
| | |
| | </div> |
| | |
| |
|
| |
|
| | </header> |
| |
|
| |
|
| | <p>A pattern is a reusable solution to a commonly occurring problem. The concept comes from architecture—Christopher Alexander’s work on design patterns—and was later adopted by software engineering. In this book, we apply the same idea to AI implementations in GLAM contexts.</p> |
| | <section id="why-patterns" class="level2" data-number="1.1"> |
| | <h2 data-number="1.1" class="anchored" data-anchor-id="why-patterns"><span class="header-section-number">1.1</span> Why Patterns?</h2> |
| | <p>AI and machine learning are evolving rapidly. The models, APIs, and frameworks we use today will be superseded—often within months. But the underlying problems—extracting structured data from historical documents, assessing condition at scale, making collections discoverable—persist.</p> |
| | <p>Patterns help us in three ways:</p> |
| | <p><strong>They’re technology-agnostic.</strong> A pattern describes <em>what</em> problem you’re solving and <em>why</em> an approach works, not just <em>which</em> model to use. When better models emerge, the pattern still applies.</p> |
| | <p><strong>They’re communicable.</strong> Patterns give teams a shared vocabulary. Saying “we’re using a structured extraction pattern” conveys more than listing the specific models and APIs involved.</p> |
| | <p><strong>They’re adaptable.</strong> The same pattern can be implemented differently depending on your constraints—budget, infrastructure, staff expertise, risk tolerance.</p> |
| | </section> |
| | <section id="anatomy-of-a-pattern" class="level2" data-number="1.2"> |
| | <h2 data-number="1.2" class="anchored" data-anchor-id="anatomy-of-a-pattern"><span class="header-section-number">1.2</span> Anatomy of a Pattern</h2> |
| | <p>Each pattern in this book follows a consistent structure:</p> |
| | <p><strong>The Challenge</strong> What recurring problem does this pattern address? What makes it difficult or impossible to solve with traditional approaches?</p> |
| | <p><strong>Solution Overview</strong> The high-level approach. What makes this work? What are the key components?</p> |
| | <p><strong>Implementation</strong> Technical walkthrough with working code. We use real examples from GLAM collections, not toy datasets.</p> |
| | <p><strong>Considerations</strong> When should you use this pattern? What are the tradeoffs? What might go wrong?</p> |
| | </section> |
| | <section id="patterns-in-this-book" class="level2" data-number="1.3"> |
| | <h2 data-number="1.3" class="anchored" data-anchor-id="patterns-in-this-book"><span class="header-section-number">1.3</span> Patterns in This Book</h2> |
| | <p>This book currently covers:</p> |
| | <ul> |
| | <li><strong>Structured Information Extraction</strong> — Using Vision Language Models to extract structured metadata from document images (index cards, forms, registers)</li> |
| | </ul> |
| | <p>Additional patterns will be added as the book develops.</p> |
| |
|
| |
|
| | </section> |
| |
|
| | </main> |
| | <script id="quarto-html-after-body" type="application/javascript"> |
| | window.document.addEventListener("DOMContentLoaded", function (event) { |
| | const icon = ""; |
| | const anchorJS = new window.AnchorJS(); |
| | anchorJS.options = { |
| | placement: 'right', |
| | icon: icon |
| | }; |
| | anchorJS.add('.anchored'); |
| | const isCodeAnnotation = (el) => { |
| | for (const clz of el.classList) { |
| | if (clz.startsWith('code-annotation-')) { |
| | return true; |
| | } |
| | } |
| | return false; |
| | } |
| | const onCopySuccess = function(e) { |
| | |
| | const button = e.trigger; |
| | |
| | button.blur(); |
| | |
| | button.classList.add('code-copy-button-checked'); |
| | var currentTitle = button.getAttribute("title"); |
| | button.setAttribute("title", "Copied!"); |
| | let tooltip; |
| | if (window.bootstrap) { |
| | button.setAttribute("data-bs-toggle", "tooltip"); |
| | button.setAttribute("data-bs-placement", "left"); |
| | button.setAttribute("data-bs-title", "Copied!"); |
| | tooltip = new bootstrap.Tooltip(button, |
| | { trigger: "manual", |
| | customClass: "code-copy-button-tooltip", |
| | offset: [0, -8]}); |
| | tooltip.show(); |
| | } |
| | setTimeout(function() { |
| | if (tooltip) { |
| | tooltip.hide(); |
| | button.removeAttribute("data-bs-title"); |
| | button.removeAttribute("data-bs-toggle"); |
| | button.removeAttribute("data-bs-placement"); |
| | } |
| | button.setAttribute("title", currentTitle); |
| | button.classList.remove('code-copy-button-checked'); |
| | }, 1000); |
| | |
| | e.clearSelection(); |
| | } |
| | const getTextToCopy = function(trigger) { |
| | const outerScaffold = trigger.parentElement.cloneNode(true); |
| | const codeEl = outerScaffold.querySelector('code'); |
| | for (const childEl of codeEl.children) { |
| | if (isCodeAnnotation(childEl)) { |
| | childEl.remove(); |
| | } |
| | } |
| | return codeEl.innerText; |
| | } |
| | const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', { |
| | text: getTextToCopy |
| | }); |
| | clipboard.on('success', onCopySuccess); |
| | if (window.document.getElementById('quarto-embedded-source-code-modal')) { |
| | const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', { |
| | text: getTextToCopy, |
| | container: window.document.getElementById('quarto-embedded-source-code-modal') |
| | }); |
| | clipboardModal.on('success', onCopySuccess); |
| | } |
| | var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//); |
| | var mailtoRegex = new RegExp(/^mailto:/); |
| | var filterRegex = new RegExp('/' + window.location.host + '/'); |
| | var isInternal = (href) => { |
| | return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href); |
| | } |
| | |
| | var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)'); |
| | for (var i=0; i<links.length; i++) { |
| | const link = links[i]; |
| | if (!isInternal(link.href)) { |
| | |
| | |
| | if (link.dataset.originalHref !== undefined) { |
| | link.href = link.dataset.originalHref; |
| | } |
| | } |
| | } |
| | function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) { |
| | const config = { |
| | allowHTML: true, |
| | maxWidth: 500, |
| | delay: 100, |
| | arrow: false, |
| | appendTo: function(el) { |
| | return el.parentElement; |
| | }, |
| | interactive: true, |
| | interactiveBorder: 10, |
| | theme: 'quarto', |
| | placement: 'bottom-start', |
| | }; |
| | if (contentFn) { |
| | config.content = contentFn; |
| | } |
| | if (onTriggerFn) { |
| | config.onTrigger = onTriggerFn; |
| | } |
| | if (onUntriggerFn) { |
| | config.onUntrigger = onUntriggerFn; |
| | } |
| | window.tippy(el, config); |
| | } |
| | const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); |
| | for (var i=0; i<noterefs.length; i++) { |
| | const ref = noterefs[i]; |
| | tippyHover(ref, function() { |
| | |
| | let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href'); |
| | try { href = new URL(href).hash; } catch {} |
| | const id = href.replace(/^#\/?/, ""); |
| | const note = window.document.getElementById(id); |
| | if (note) { |
| | return note.innerHTML; |
| | } else { |
| | return ""; |
| | } |
| | }); |
| | } |
| | const xrefs = window.document.querySelectorAll('a.quarto-xref'); |
| | const processXRef = (id, note) => { |
| | |
| | const stripColumnClz = (el) => { |
| | el.classList.remove("page-full", "page-columns"); |
| | if (el.children) { |
| | for (const child of el.children) { |
| | stripColumnClz(child); |
| | } |
| | } |
| | } |
| | stripColumnClz(note) |
| | if (id === null || id.startsWith('sec-')) { |
| | |
| | const container = document.createElement("div"); |
| | if (note.children && note.children.length > 2) { |
| | container.appendChild(note.children[0].cloneNode(true)); |
| | for (let i = 1; i < note.children.length; i++) { |
| | const child = note.children[i]; |
| | if (child.tagName === "P" && child.innerText === "") { |
| | continue; |
| | } else { |
| | container.appendChild(child.cloneNode(true)); |
| | break; |
| | } |
| | } |
| | if (window.Quarto?.typesetMath) { |
| | window.Quarto.typesetMath(container); |
| | } |
| | return container.innerHTML |
| | } else { |
| | if (window.Quarto?.typesetMath) { |
| | window.Quarto.typesetMath(note); |
| | } |
| | return note.innerHTML; |
| | } |
| | } else { |
| | |
| | const anchorLink = note.querySelector('a.anchorjs-link'); |
| | if (anchorLink) { |
| | anchorLink.remove(); |
| | } |
| | if (window.Quarto?.typesetMath) { |
| | window.Quarto.typesetMath(note); |
| | } |
| | if (note.classList.contains("callout")) { |
| | return note.outerHTML; |
| | } else { |
| | return note.innerHTML; |
| | } |
| | } |
| | } |
| | for (var i=0; i<xrefs.length; i++) { |
| | const xref = xrefs[i]; |
| | tippyHover(xref, undefined, function(instance) { |
| | instance.disable(); |
| | let url = xref.getAttribute('href'); |
| | let hash = undefined; |
| | if (url.startsWith('#')) { |
| | hash = url; |
| | } else { |
| | try { hash = new URL(url).hash; } catch {} |
| | } |
| | if (hash) { |
| | const id = hash.replace(/^#\/?/, ""); |
| | const note = window.document.getElementById(id); |
| | if (note !== null) { |
| | try { |
| | const html = processXRef(id, note.cloneNode(true)); |
| | instance.setContent(html); |
| | } finally { |
| | instance.enable(); |
| | instance.show(); |
| | } |
| | } else { |
| | |
| | fetch(url.split('#')[0]) |
| | .then(res => res.text()) |
| | .then(html => { |
| | const parser = new DOMParser(); |
| | const htmlDoc = parser.parseFromString(html, "text/html"); |
| | const note = htmlDoc.getElementById(id); |
| | if (note !== null) { |
| | const html = processXRef(id, note); |
| | instance.setContent(html); |
| | } |
| | }).finally(() => { |
| | instance.enable(); |
| | instance.show(); |
| | }); |
| | } |
| | } else { |
| | |
| | |
| | fetch(url) |
| | .then(res => res.text()) |
| | .then(html => { |
| | const parser = new DOMParser(); |
| | const htmlDoc = parser.parseFromString(html, "text/html"); |
| | const note = htmlDoc.querySelector('main.content'); |
| | if (note !== null) { |
| | |
| | |
| | |
| | if (note.children.length > 0 && note.children[0].tagName === "HEADER") { |
| | note.children[0].remove(); |
| | } |
| | const html = processXRef(null, note); |
| | instance.setContent(html); |
| | } |
| | }).finally(() => { |
| | instance.enable(); |
| | instance.show(); |
| | }); |
| | } |
| | }, function(instance) { |
| | }); |
| | } |
| | let selectedAnnoteEl; |
| | const selectorForAnnotation = ( cell, annotation) => { |
| | let cellAttr = 'data-code-cell="' + cell + '"'; |
| | let lineAttr = 'data-code-annotation="' + annotation + '"'; |
| | const selector = 'span[' + cellAttr + '][' + lineAttr + ']'; |
| | return selector; |
| | } |
| | const selectCodeLines = (annoteEl) => { |
| | const doc = window.document; |
| | const targetCell = annoteEl.getAttribute("data-target-cell"); |
| | const targetAnnotation = annoteEl.getAttribute("data-target-annotation"); |
| | const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation)); |
| | const lines = annoteSpan.getAttribute("data-code-lines").split(","); |
| | const lineIds = lines.map((line) => { |
| | return targetCell + "-" + line; |
| | }) |
| | let top = null; |
| | let height = null; |
| | let parent = null; |
| | if (lineIds.length > 0) { |
| | |
| | const el = window.document.getElementById(lineIds[0]); |
| | top = el.offsetTop; |
| | height = el.offsetHeight; |
| | parent = el.parentElement.parentElement; |
| | if (lineIds.length > 1) { |
| | const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]); |
| | const bottom = lastEl.offsetTop + lastEl.offsetHeight; |
| | height = bottom - top; |
| | } |
| | if (top !== null && height !== null && parent !== null) { |
| | |
| | let div = window.document.getElementById("code-annotation-line-highlight"); |
| | if (div === null) { |
| | div = window.document.createElement("div"); |
| | div.setAttribute("id", "code-annotation-line-highlight"); |
| | div.style.position = 'absolute'; |
| | parent.appendChild(div); |
| | } |
| | div.style.top = top - 2 + "px"; |
| | div.style.height = height + 4 + "px"; |
| | div.style.left = 0; |
| | let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); |
| | if (gutterDiv === null) { |
| | gutterDiv = window.document.createElement("div"); |
| | gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter"); |
| | gutterDiv.style.position = 'absolute'; |
| | const codeCell = window.document.getElementById(targetCell); |
| | const gutter = codeCell.querySelector('.code-annotation-gutter'); |
| | gutter.appendChild(gutterDiv); |
| | } |
| | gutterDiv.style.top = top - 2 + "px"; |
| | gutterDiv.style.height = height + 4 + "px"; |
| | } |
| | selectedAnnoteEl = annoteEl; |
| | } |
| | }; |
| | const unselectCodeLines = () => { |
| | const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"]; |
| | elementsIds.forEach((elId) => { |
| | const div = window.document.getElementById(elId); |
| | if (div) { |
| | div.remove(); |
| | } |
| | }); |
| | selectedAnnoteEl = undefined; |
| | }; |
| | |
| | window.addEventListener( |
| | "resize", |
| | throttle(() => { |
| | elRect = undefined; |
| | if (selectedAnnoteEl) { |
| | selectCodeLines(selectedAnnoteEl); |
| | } |
| | }, 10) |
| | ); |
| | function throttle(fn, ms) { |
| | let throttle = false; |
| | let timer; |
| | return (...args) => { |
| | if(!throttle) { |
| | fn.apply(this, args); |
| | throttle = true; |
| | } else { |
| | if(timer) clearTimeout(timer); |
| | timer = setTimeout(() => { |
| | fn.apply(this, args); |
| | timer = throttle = false; |
| | }, ms); |
| | } |
| | }; |
| | } |
| | |
| | const annoteDls = window.document.querySelectorAll('dt[data-target-cell]'); |
| | for (const annoteDlNode of annoteDls) { |
| | annoteDlNode.addEventListener('click', (event) => { |
| | const clickedEl = event.target; |
| | if (clickedEl !== selectedAnnoteEl) { |
| | unselectCodeLines(); |
| | const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active'); |
| | if (activeEl) { |
| | activeEl.classList.remove('code-annotation-active'); |
| | } |
| | selectCodeLines(clickedEl); |
| | clickedEl.classList.add('code-annotation-active'); |
| | } else { |
| | |
| | unselectCodeLines(); |
| | clickedEl.classList.remove('code-annotation-active'); |
| | } |
| | }); |
| | } |
| | const findCites = (el) => { |
| | const parentEl = el.parentElement; |
| | if (parentEl) { |
| | const cites = parentEl.dataset.cites; |
| | if (cites) { |
| | return { |
| | el, |
| | cites: cites.split(' ') |
| | }; |
| | } else { |
| | return findCites(el.parentElement) |
| | } |
| | } else { |
| | return undefined; |
| | } |
| | }; |
| | var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]'); |
| | for (var i=0; i<bibliorefs.length; i++) { |
| | const ref = bibliorefs[i]; |
| | const citeInfo = findCites(ref); |
| | if (citeInfo) { |
| | tippyHover(citeInfo.el, function() { |
| | var popup = window.document.createElement('div'); |
| | citeInfo.cites.forEach(function(cite) { |
| | var citeDiv = window.document.createElement('div'); |
| | citeDiv.classList.add('hanging-indent'); |
| | citeDiv.classList.add('csl-entry'); |
| | var biblioDiv = window.document.getElementById('ref-' + cite); |
| | if (biblioDiv) { |
| | citeDiv.innerHTML = biblioDiv.innerHTML; |
| | } |
| | popup.appendChild(citeDiv); |
| | }); |
| | return popup.innerHTML; |
| | }); |
| | } |
| | } |
| | }); |
| | </script> |
| | <nav class="page-navigation"> |
| | <div class="nav-page nav-page-previous"> |
| | <a href="../index.html" class="pagination-link" aria-label="Welcome"> |
| | <i class="bi bi-arrow-left-short"></i> <span class="nav-page-text">Welcome</span> |
| | </a> |
| | </div> |
| | <div class="nav-page nav-page-next"> |
| | <a href="../patterns/structured-generation/intro.html" class="pagination-link" aria-label="Structured Document Processing"> |
| | <span class="nav-page-text"><span class="chapter-number">2</span> <span class="chapter-title">Structured Document Processing</span></span> <i class="bi bi-arrow-right-short"></i> |
| | </a> |
| | </div> |
| | </nav> |
| | </div> |
| |
|
| |
|
| |
|
| |
|
| | </body></html> |