diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 22fbe802..29f6803f 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -10,7 +10,7 @@ GITHOOKS_DIR=`pwd -P` popd PROJECT_ROOT=`dirname $GITHOOKS_DIR` -MINOR_VERSION=$(cat $PROJECT_ROOT/VERSION | grep -o "[[:digit:]]\.[[:digit:]]") +MINOR_VERSION=$(cat $PROJECT_ROOT/VERSION | grep -oE "\\d+\\.\\d+") if [[ -z "${STAGE_STATIC_ASSETS:-}" ]]; then echo "[pre-commit]: Unstaging assets directory" diff --git a/VERSION b/VERSION index 7b0231f5..ed21137e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.3 \ No newline at end of file +1.10.0 \ No newline at end of file diff --git a/_layouts/spec.html b/_layouts/spec.html index 310e46b7..de79b690 100644 --- a/_layouts/spec.html +++ b/_layouts/spec.html @@ -1,5 +1,5 @@ --- -version_string: v1.9 +version_string: v1.10 --- @@ -40,7 +40,7 @@ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji" !important; } - + {%- endif %} \n // [contents]\n // \n // \n if (codeblock.childNodes.length === 1 &&\n codeblock.firstElementChild?.tagName === 'CODE') {\n return codeblock.firstElementChild;\n }\n return codeblock;\n}\n/**\n * Gather metadata about the code block, create the code block, then replace\n * the existing DOM node with the new enhanced code block.\n *\n * @param codeblocks Output from `.querySelectorAll()`\n * @param getContents A method that extracts a string with the codeblock contents given a codeblock element\n * @param startId The ID to use for the first enhanced code block\n */\nfunction enhanceBlocks(codeblocks, getCodeEl, startId = 0) {\n let nextCodeBlockId = startId;\n [...codeblocks]\n .filter((codeblock) => codeblock.querySelector(`.${CODEBLOCK_PROCESSED_CLASS}`) == null &&\n codeblock.closest(`.${CODEBLOCK_PROCESSED_CLASS}`) == null)\n .forEach((codeblock) => {\n if (shouldRetainLegacyCodeBlock(codeblock)) {\n // We decided not to enhance this block. Mark it as processed.\n codeblock.classList.add(CODEBLOCK_PROCESSED_CLASS);\n return;\n }\n const codeblockNumericId = nextCodeBlockId++;\n const codeblockParent = codeblock.parentElement;\n if (!codeblockParent) {\n console.warn('useEnhancedCodeBlocks: Codeblock missing parent');\n return;\n }\n const codeblockContentsEl = getCodeEl(codeblock);\n if (codeblockContentsEl == null) {\n return;\n }\n const codeblockContents = getCodeblockContents(codeblockContentsEl);\n const title = codeblock.dataset['title'] || null;\n const anchorId = title\n ? createCodeBlockAnchorId(codeblockNumericId, title)\n : null;\n const enhancedCodeBlock = createEnhancedCodeBlock({\n codeblockNumericId,\n rawContent: codeblockContents,\n language: getCodeBlockLanguage(codeblock),\n rawHighlightRanges: codeblock.dataset['highlight'] || null,\n title,\n anchorId,\n showLineNumbers: getCodeblockVariant(codeblock, codeblockContents) !==\n CodeblockVariant.NO_LINE_NUMBERS,\n });\n if (!enhancedCodeBlock) {\n return;\n }\n // Clear the old code block and replace with the enhanced block\n codeblockParent.replaceChild(JSXDom.h(\"div\", { id: anchorId ?? undefined, class: \"primer-spec-code-block\" }, enhancedCodeBlock), codeblock);\n });\n // We need to add anchors to Code Block titles if applicable\n new AnchorJS().add(`.${CODEBLOCK_TITLE_CLASS}`);\n return nextCodeBlockId;\n}\nfunction shouldRetainLegacyCodeBlock(codeblock) {\n // Don't mess with Mermaid blocks, they'll be handled by the Mermaid plugin.\n if (codeblock.querySelector('.language-mermaid') != null) {\n return true;\n }\n return getCodeblockVariant(codeblock) === CodeblockVariant.LEGACY;\n}\nfunction getCodeblockVariant(codeblock, rawContent) {\n const rawVariant = codeblock.dataset['variant']?.toLowerCase();\n if (rawVariant && Object.values(CodeblockVariant).includes(rawVariant)) {\n return rawVariant;\n }\n // Special handling if:\n // - A codeblock does not specify a variant\n // - The default code block variant is \"enhanced\" (aka show line numbers)\n // - The codeblock has only one line\n // Then DO NOT show line numbers. I've come to believe that line numbers\n // look confusing in single-line codeblocks.\n const codeblockHasOnlyOneLine = rawContent\n ? !rawContent.trim().includes('\\n')\n : false;\n if (Config.DEFAULT_CODEBLOCK_VARIANT === CodeblockVariant.ENHANCED &&\n codeblockHasOnlyOneLine) {\n return CodeblockVariant.NO_LINE_NUMBERS;\n }\n return Config.DEFAULT_CODEBLOCK_VARIANT;\n}\n/***********/\n/** UTILS **/\n/***********/\n/**\n * Given an element, return the codeblock's language (if present) if the\n * element's `classList` contains a class of the form `language-[language]`.\n */\nfunction getCodeBlockLanguage(codeblockSrc) {\n for (const className of codeblockSrc.classList) {\n if (className.startsWith('language-')) {\n return className.replace('language-', '');\n }\n }\n return null;\n}\nfunction createCodeBlockAnchorId(codeblockNumericId, title) {\n return `${slugify(title)}-${codeblockNumericId}`;\n}\n/**\n * Given a codeblock / pre element, return a string reprensenting the HTML of\n * the codeblock.\n *\n * One edge case that this method handles: Lines split within a single span.\n * Consider the following codeblock (observe lines 3-4):\n * ```html\n * Line 1\n * Line 2\n * Line 3\n * Line 4\n * ```\n * Since the rest of the code assumes that \"\\n\" characters separate lines, we\n * need to ensure that each line starts with its own span if necessary. The\n * output of this method should be:\n * ```html\n * Line 1\n * Line 2\n * Line 3\n * Line 4\n * ```\n */\nfunction getCodeblockContents(codeEl) {\n const resultNode = codeEl.cloneNode();\n codeEl.childNodes.forEach((childNode) => {\n if (childNode.nodeType === Node.ELEMENT_NODE) {\n if (childNode.tagName === 'SPAN' &&\n childNode.textContent != null) {\n const lines = childNode.textContent.split('\\n');\n lines.forEach((line, i) => {\n // Ignore empty lines within a span, but still insert the \\n.\n if (line) {\n const lineEl = childNode.cloneNode();\n lineEl.textContent = line;\n resultNode.appendChild(lineEl);\n }\n // Append a new line except after the last line in this span\n if (i < lines.length - 1) {\n resultNode.appendChild(document.createTextNode('\\n'));\n }\n });\n }\n }\n else {\n resultNode.appendChild(childNode.cloneNode(true));\n }\n });\n return resultNode.innerHTML;\n}\n","import * as JSXDom from 'jsx-dom';\n/**\n * A custom hook that renders \"mermaid code blocks\" into actual mermaid\n * diagrams.\n * @param mainElRef A ref to the `
` element from MainContent\n */\nexport default function useMermaidDiagrams(mainElRef, isDarkModeEnabled) {\n if (!mainElRef.current) {\n throw new Error('Primer Spec: Main Content: Expected main content ref to be initialized.');\n }\n if (!('mermaid' in window)) {\n return () => { };\n }\n mermaid.mermaidAPI.initialize({\n securityLevel: 'loose',\n startOnLoad: false,\n theme: isDarkModeEnabled ? 'dark' : 'default',\n });\n // Remove any existing mermaid diagrams\n mainElRef.current\n .querySelectorAll('.primer-spec-mermaid-output')\n .forEach((oldDiagram) => oldDiagram.remove());\n const mermaidBlocks = mainElRef.current.querySelectorAll('pre > code.language-mermaid');\n mermaidBlocks.forEach((mermaidBlock, i) => {\n const diagramID = `diagram-${i}`;\n const content = mermaidBlock.innerText;\n const parent = mermaidBlock.parentElement;\n if (parent == null) {\n return;\n }\n parent.style.display = 'none';\n const outputDiagram = (JSXDom.h(\"div\", { class: \"primer-spec-mermaid-output\", \"aria-label\": \"Mermaid-enhanced diagram\" }));\n parent.after(outputDiagram);\n mermaid.mermaidAPI.render(diagramID, content, (diagramHTML) => {\n outputDiagram.innerHTML = diagramHTML;\n const svgEl = outputDiagram.querySelector('svg');\n if (svgEl == null) {\n console.warn(\"Primer Spec: Mermaid diagram didn't have an SVG. Please report this issue at github.com/eeccs485staff/primer-spec/issues. Thanks!\");\n return;\n }\n // Make diagrams a bit more accessible to screen readers.\n // Based on pattern 11 from: https://www.deque.com/blog/creating-accessible-svgs/\n svgEl.setAttribute('role', 'img');\n // (1) If the spec author added a title, make it available.\n if (parent.dataset['title']) {\n svgEl.insertBefore(JSXDom.h(\"title\", { id: `${diagramID}-title` }, parent.dataset['title']), svgEl.firstChild);\n }\n // (2) If the spec author added a description, make it available.\n // Otherwise, use the diagram source code. (It isn't great, but it's\n // better than nothing.)\n let description = parent.dataset['description'];\n if (!description) {\n description = content;\n }\n svgEl.insertBefore(JSXDom.h(\"desc\", { id: `${diagramID}-desc` }, description), svgEl.firstChild);\n svgEl.setAttribute('aria-labelledby', `${diagramID}-title ${diagramID}-desc`);\n });\n });\n return () => { };\n}\n","/**\n * A custom hook that transforms elements of the form:\n * ```html\n * ...\n * ```\n * with modified attributes that show a dynamic mobile-friendly tooltip.\n * @param mainElRef A ref to the `
` element from MainContent\n */\nexport default function useTooltippedAbbreviations(mainElRef) {\n if (!mainElRef.current) {\n throw new Error('Primer Spec: Main Content: Expected main content ref to be initialized.');\n }\n // The structure of a kramdown abbreviation is:\n // ...\n // Wrap the entire contents of each
  • in a