Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/CopyText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const CopyContainer = ({ text, code, format, formatType, eventName, additionalIn

return (
<span className="copyContainer">
{code ? <code>{displayText}</code> : displayText}
{code ? <code data-1p-ignore>{displayText}</code> : displayText}
<button
className={clsx("copyBtn", "copy-iconbutton")}
style={{ height: "16px", width: "16px", minWidth: "12px" }}
Expand Down
25 changes: 25 additions & 0 deletions src/components/HeadCommon.astro
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,31 @@ const { title } = Astro.props
<!-- Scrollable a11y code helper - defer to not block render -->
<script src="/make-scrollable-code-focusable.js" defer></script>

<!-- Prism.js for client-side syntax highlighting (to fix browser extension conflicts) -->
<script is:inline>
window.Prism = window.Prism || {}
window.Prism.manual = true // Prevent auto-highlighting on load, we'll control it via our script
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"
integrity="sha512-9khQRAUBYEJDCDVP2yw3LRUQvjJ0Pjx0EShmaQjcHa6AXiOv6qHQu9lCAIR8O+/D8FtaCoJ2c0Tf9Xo7hYH01Q=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
defer></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"
integrity="sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
defer></script>
<script is:inline>
// Configure Prism autoloader to use CDN
window.Prism = window.Prism || {}
window.Prism.plugins = window.Prism.plugins || {}
window.Prism.plugins.autoloader = window.Prism.plugins.autoloader || {}
window.Prism.plugins.autoloader.languages_path = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/"
</script>

<!-- Preconnects to speed GTM/GA when they load -->
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin />
<link rel="preconnect" href="https://www.google-analytics.com" crossorigin />
Expand Down
157 changes: 157 additions & 0 deletions src/scripts/fix-prism-extension-conflict.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Fix for 1Password and other browser extensions that bundle Prism.js
*
* Problem: Extensions with bundled Prism.js (especially 1Password beta/nightly)
* run Prism.highlightAll() on page load, which strips our syntax highlighting
* by removing .token spans and even <pre> wrappers.
*
* Solution: Detect when highlighting is stripped and re-apply it.
* Only targets code BLOCKS (pre>code), not inline code elements.
*/

// Extend Window interface to include Prism
declare global {
interface Window {
Prism?: {
highlightAll: () => void
}
}
}

let isFixing = false
let retryCount = 0
const MAX_RETRIES = 3

/**
* Check if Prism highlighting has been compromised
* Returns true if we find code blocks that should be highlighted but aren't
*/
function isPrismCompromised(): boolean {
// Look for code elements with language-* class but no token spans inside
const codeBlocks = document.querySelectorAll('pre code[class*="language-"]')

console.log("[Prism Fix] Checking", codeBlocks.length, "code blocks")

for (const block of Array.from(codeBlocks)) {
const hasTokens = block.querySelector('[class*="token"]')
const textContent = block.textContent || ""
const hasContent = textContent.trim().length > 0

console.log("[Prism Fix] Block check:", {
hasTokens: !!hasTokens,
hasContent,
language: block.className,
})

if (!hasTokens && hasContent) {
// Found a code block that should be highlighted but isn't
console.log("[Prism Fix] Found compromised block - will attempt to fix:", block)
return true
}
}

console.log("[Prism Fix] All code blocks appear to be properly highlighted")
return false
}

/**
* Re-highlight all code blocks using Prism
*/
function reHighlightCode() {
if (isFixing) return

isFixing = true
retryCount++

try {
// Check if Prism is available
if (typeof window.Prism !== "undefined") {
// Re-run Prism highlighting on all code blocks
window.Prism.highlightAll()
console.log("[Prism Fix] Re-applied syntax highlighting (retry", retryCount, ")")
}
} catch (error) {
console.error("[Prism Fix] Error re-highlighting:", error)
} finally {
isFixing = false
}
}

/**
* Check and fix Prism highlighting after a delay
*/
function checkAndFix(delay = 100) {
setTimeout(() => {
console.log("[Prism Fix] Running check (retry count:", retryCount, "/", MAX_RETRIES, ")")
if (isPrismCompromised() && retryCount < MAX_RETRIES) {
console.log("[Prism Fix] Attempting to fix...")
reHighlightCode()
} else if (retryCount >= MAX_RETRIES) {
console.log("[Prism Fix] Max retries reached, stopping checks")
}
}, delay)
}

/**
* Initialize the fix
*/
function init() {
console.log("[Prism Fix] Initializing browser extension fix")
console.log("[Prism Fix] Prism available?", typeof window.Prism !== "undefined")

// Wait for Prism to load before attempting fixes
function waitForPrism() {
if (typeof window.Prism !== "undefined") {
console.log("[Prism Fix] Prism loaded successfully")
startChecking()
} else {
console.log("[Prism Fix] Waiting for Prism to load...")
setTimeout(waitForPrism, 100)
}
}

function startChecking() {
// Check immediately after DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => checkAndFix(50))
} else {
checkAndFix(50)
}

// Check again after a short delay (in case extension runs after us)
checkAndFix(200)
checkAndFix(500)
checkAndFix(1000)
}

waitForPrism()

// Set up a MutationObserver to detect if extension modifies code blocks
const observer = new MutationObserver((mutations) => {
// Check if any mutation affected code blocks
for (const mutation of mutations) {
if (mutation.type === "childList" || mutation.type === "characterData") {
const target = mutation.target as HTMLElement

// Check if the mutation is inside a code block
const codeBlock = target.closest('pre code[class*="language-"]')
if (codeBlock) {
checkAndFix(100)
break
}
}
}
})

// Start observing
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true,
})
}

// Only run in browser
if (typeof window !== "undefined") {
init()
}
1 change: 1 addition & 0 deletions src/scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ import "./fix-remix-urls"
import "./fix-external-links"
import "./copyToClipboard/copyToClipboard"
import "./scroll-to-search"
import "./fix-prism-extension-conflict"
53 changes: 53 additions & 0 deletions src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,59 @@ strong {
word-break: break-word;
}

/* Override browser extensions (1Password, etc.) that inject dark themes into INLINE code elements ONLY */
/* Specifically target ONLY inline code, never code inside pre tags or with token classes */
/* Target: inline code in paragraphs, tables, and CopyText components */
/* Use descendant selector (space) to catch nested code (e.g., inside <strong>) */
body p code:not([class*="token"]),
body li code:not([class*="token"]),
body td code:not([class*="token"]),
body th code:not([class*="token"]),
body .copyContainer code:not([class*="token"]),
body h1 code:not([class*="token"]),
body h2 code:not([class*="token"]),
body h3 code:not([class*="token"]),
body h4 code:not([class*="token"]),
body h5 code:not([class*="token"]),
body h6 code:not([class*="token"]),
body blockquote code:not([class*="token"]),
body a code:not([class*="token"]),
body strong code:not([class*="token"]),
body em code:not([class*="token"]),
body b code:not([class*="token"]),
body i code:not([class*="token"]),
body span code:not([class*="token"]) {
background: var(--theme-code-inline-bg) !important;
background-color: var(--theme-code-inline-bg) !important;
color: var(--theme-code-inline-text) !important;
font-family: var(--font-mono) !important;
padding: var(--padding-block) var(--padding-inline) !important;
border-radius: var(--border-radius) !important;
white-space: normal !important;
word-break: break-word !important;
line-height: inherit !important;
font-size: 0.85em !important;
font-weight: bold !important;
}

/* Make absolutely sure we NEVER style code blocks */
pre code,
pre > code,
.code-block-container code,
[class*="code-block"] code {
background: none !important;
background-color: transparent !important;
color: inherit !important;
font-family: inherit !important;
padding: 0 !important;
border-radius: 0 !important;
white-space: pre !important;
word-break: normal !important;
line-height: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
}

/* Inline code in headings should inherit heading font size and weight */
:is(h1, h2, h3, h4, h5, h6) :not(pre) > code,
:is(h1, h2, h3, h4, h5, h6) code {
Expand Down
Loading