@expressive-code/plugin-frames
Advanced tools
Comparing version 0.7.0 to 0.8.0
import { StyleSettings, PluginTexts, ExpressiveCodePlugin, AttachedPluginData } from '@expressive-code/core'; | ||
declare const framesStyleSettings: StyleSettings<"shadowColor" | "frameBoxShadowCssValue" | "editorActiveTabBackground" | "editorActiveTabForeground" | "editorActiveTabBorder" | "editorActiveTabBorderTop" | "editorActiveTabBorderBottom" | "editorActiveTabMarginInlineStart" | "editorActiveTabMarginBlockStart" | "editorTabBorderRadius" | "editorTabBarBackground" | "editorTabBarBorderColor" | "editorTabBarBorderBottom" | "editorBackground" | "terminalTitlebarDotsForeground" | "terminalTitlebarBackground" | "terminalTitlebarForeground" | "terminalTitlebarBorderBottom" | "terminalBackground" | "inlineButtonForeground" | "inlineButtonHoverBackground" | "inlineButtonActiveBorder" | "tooltipSuccessBackground" | "tooltipSuccessForeground">; | ||
declare const framesStyleSettings: StyleSettings<"shadowColor" | "frameBoxShadowCssValue" | "editorActiveTabBackground" | "editorActiveTabForeground" | "editorActiveTabBorder" | "editorActiveTabBorderTop" | "editorActiveTabBorderBottom" | "editorActiveTabMarginInlineStart" | "editorActiveTabMarginBlockStart" | "editorTabBorderRadius" | "editorTabBarBackground" | "editorTabBarBorderColor" | "editorTabBarBorderBottom" | "editorBackground" | "terminalTitlebarDotsForeground" | "terminalTitlebarBackground" | "terminalTitlebarForeground" | "terminalTitlebarBorderBottom" | "terminalBackground" | "inlineButtonForeground" | "inlineButtonBorder" | "inlineButtonHoverOrFocusBackground" | "inlineButtonActiveBackground" | "tooltipSuccessBackground" | "tooltipSuccessForeground">; | ||
@@ -5,0 +5,0 @@ interface PluginFramesOptions { |
@@ -28,12 +28,13 @@ // src/index.ts | ||
inlineButtonForeground: ({ coreStyles }) => coreStyles.codeForeground, | ||
inlineButtonHoverBackground: ({ resolveSetting }) => setAlpha(resolveSetting("inlineButtonForeground"), 0.2), | ||
inlineButtonActiveBorder: ({ resolveSetting }) => setAlpha(resolveSetting("inlineButtonForeground"), 0.4), | ||
inlineButtonBorder: ({ resolveSetting }) => setAlpha(resolveSetting("inlineButtonForeground"), 0.4), | ||
inlineButtonHoverOrFocusBackground: ({ resolveSetting }) => setAlpha(resolveSetting("inlineButtonForeground"), 0.2), | ||
inlineButtonActiveBackground: ({ resolveSetting }) => setAlpha(resolveSetting("inlineButtonForeground"), 0.3), | ||
tooltipSuccessBackground: "#177d07", | ||
tooltipSuccessForeground: "white" | ||
}); | ||
function getFramesBaseStyles(theme, coreStyles, styleOverrides) { | ||
function getFramesBaseStyles(theme, coreStyles, options) { | ||
const framesStyles = framesStyleSettings.resolve({ | ||
theme, | ||
coreStyles, | ||
styleOverrides | ||
styleOverrides: options.styleOverrides | ||
}); | ||
@@ -72,196 +73,224 @@ const dotsColor = toRgbaString(framesStyles.terminalTitlebarDotsForeground); | ||
const activeTabBackground = activeTabBackgrounds.join(","); | ||
const styles = ` | ||
.frame { | ||
all: unset; | ||
const frameStyles = `.frame { | ||
all: unset; | ||
position: relative; | ||
display: block; | ||
--header-border-radius: calc(${coreStyles.borderRadius} + ${coreStyles.borderWidth}); | ||
--button-spacing: 0.4rem; | ||
--code-background: ${framesStyles.editorBackground}; | ||
border-radius: var(--header-border-radius); | ||
box-shadow: ${framesStyles.frameBoxShadowCssValue}; | ||
.header { | ||
display: none; | ||
z-index: 1; | ||
position: relative; | ||
display: block; | ||
--header-border-radius: calc(${coreStyles.borderRadius} + ${coreStyles.borderWidth}); | ||
--button-spacing: 0.4rem; | ||
border-radius: var(--header-border-radius); | ||
box-shadow: ${framesStyles.frameBoxShadowCssValue}; | ||
.header { | ||
display: none; | ||
z-index: 1; | ||
position: relative; | ||
border: ${coreStyles.borderWidth} solid ${coreStyles.borderColor}; | ||
border-bottom: none; | ||
border-radius: var(--header-border-radius) var(--header-border-radius) 0 0; | ||
} | ||
border: ${coreStyles.borderWidth} solid ${coreStyles.borderColor}; | ||
border-bottom: none; | ||
border-radius: var(--header-border-radius) var(--header-border-radius) 0 0; | ||
} | ||
/* Styles to apply if we have a title bar or tab bar */ | ||
&.has-title, | ||
&.is-terminal { | ||
--button-spacing: 2.4rem; | ||
/* Styles to apply if we have a title bar or tab bar */ | ||
&.has-title, | ||
&.is-terminal { | ||
& pre, & code { | ||
border-top: none; | ||
border-top-left-radius: 0; | ||
border-top-right-radius: 0; | ||
} | ||
& pre, & code { | ||
border-top: none; | ||
border-top-left-radius: 0; | ||
border-top-right-radius: 0; | ||
} | ||
} | ||
/* Prevent empty window titles from collapsing in height */ | ||
.title:empty:before { | ||
content: '\\a0'; | ||
/* Prevent empty window titles from collapsing in height */ | ||
.title:empty:before { | ||
content: '\\a0'; | ||
} | ||
/* Editor tab bar */ | ||
&.has-title:not(.is-terminal) { | ||
/* Active editor tab */ | ||
& .title { | ||
position: relative; | ||
color: ${framesStyles.editorActiveTabForeground}; | ||
background: ${activeTabBackground}; | ||
background-clip: padding-box; | ||
margin-block-start: ${framesStyles.editorActiveTabMarginBlockStart}; | ||
padding: ${coreStyles.uiPaddingBlock} ${coreStyles.uiPaddingInline}; | ||
border-radius: ${framesStyles.editorTabBorderRadius} ${framesStyles.editorTabBorderRadius} 0 0; | ||
border: ${coreStyles.borderWidth} solid ${framesStyles.editorActiveTabBorder}; | ||
border-bottom: none; | ||
} | ||
/* Editor tab bar */ | ||
&.has-title:not(.is-terminal) { | ||
--button-spacing: 0.15rem; | ||
/* Active editor tab */ | ||
& .title { | ||
position: relative; | ||
color: ${framesStyles.editorActiveTabForeground}; | ||
background: ${activeTabBackground}; | ||
background-clip: padding-box; | ||
margin-block-start: ${framesStyles.editorActiveTabMarginBlockStart}; | ||
padding: ${coreStyles.uiPaddingBlock} ${coreStyles.uiPaddingInline}; | ||
border-radius: ${framesStyles.editorTabBorderRadius} ${framesStyles.editorTabBorderRadius} 0 0; | ||
border: ${coreStyles.borderWidth} solid ${framesStyles.editorActiveTabBorder}; | ||
border-bottom: none; | ||
/* Tab bar background */ | ||
& .header { | ||
border-color: ${framesStyles.editorTabBarBorderColor}; | ||
display: flex; | ||
background: ${framesStyles.editorTabBarBackground}; | ||
background-clip: padding-box; | ||
&::before { | ||
padding-inline-start: ${framesStyles.editorActiveTabMarginInlineStart}; | ||
} | ||
/* Tab bar background */ | ||
& .header { | ||
border-color: ${framesStyles.editorTabBarBorderColor}; | ||
display: flex; | ||
background: ${framesStyles.editorTabBarBackground}; | ||
background-clip: padding-box; | ||
&::before { | ||
padding-inline-start: ${framesStyles.editorActiveTabMarginInlineStart}; | ||
} | ||
&::after { | ||
flex-grow: 1; | ||
} | ||
&::before, | ||
&::after { | ||
content: ''; | ||
border-bottom: ${coreStyles.borderWidth} solid ${framesStyles.editorTabBarBorderBottom}; | ||
} | ||
&::after { | ||
flex-grow: 1; | ||
} | ||
&::before, | ||
&::after { | ||
content: ''; | ||
border-bottom: ${coreStyles.borderWidth} solid ${framesStyles.editorTabBarBorderBottom}; | ||
} | ||
} | ||
} | ||
/* Terminal window */ | ||
&.is-terminal { | ||
--button-spacing: 0.075rem; | ||
/* Terminal window */ | ||
&.is-terminal { | ||
--code-background: ${framesStyles.terminalBackground}; | ||
/* Terminal title bar */ | ||
& .header { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
padding-block: ${coreStyles.uiPaddingBlock}; | ||
position: relative; | ||
/* Terminal title bar */ | ||
& .header { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
padding-block: ${coreStyles.uiPaddingBlock}; | ||
position: relative; | ||
font-weight: 500; | ||
letter-spacing: 0.025ch; | ||
font-weight: 500; | ||
letter-spacing: 0.025ch; | ||
color: ${framesStyles.terminalTitlebarForeground}; | ||
background: ${framesStyles.terminalTitlebarBackground}; | ||
background-clip: padding-box; | ||
color: ${framesStyles.terminalTitlebarForeground}; | ||
background: ${framesStyles.terminalTitlebarBackground}; | ||
background-clip: padding-box; | ||
/* Display three dots at the left side of the header */ | ||
&::before { | ||
content: ${terminalTitlebarDots}; | ||
position: absolute; | ||
left: ${coreStyles.uiPaddingInline}; | ||
width: 2.1rem; | ||
line-height: 0; | ||
} | ||
/* Display a border below the header */ | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
inset: 0; | ||
border-bottom: ${coreStyles.borderWidth} solid ${framesStyles.terminalTitlebarBorderBottom}; | ||
} | ||
/* Display three dots at the left side of the header */ | ||
&::before { | ||
content: ${terminalTitlebarDots}; | ||
position: absolute; | ||
left: ${coreStyles.uiPaddingInline}; | ||
width: 2.1rem; | ||
line-height: 0; | ||
} | ||
/* Terminal content */ | ||
& pre { | ||
background: ${framesStyles.terminalBackground}; | ||
background-clip: padding-box; | ||
/* Display a border below the header */ | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
inset: 0; | ||
border-bottom: ${coreStyles.borderWidth} solid ${framesStyles.terminalTitlebarBorderBottom}; | ||
} | ||
} | ||
} | ||
.copy { | ||
display: flex; | ||
gap: 0.25rem; | ||
flex-direction: row-reverse; | ||
position: absolute; | ||
inset-block-start: calc(${coreStyles.borderWidth} + var(--button-spacing)); | ||
inset-inline-end: calc(${coreStyles.borderWidth} + ${coreStyles.uiPaddingInline} / 2); | ||
button { | ||
align-self: flex-end; | ||
width: 1.75rem; | ||
height: 1.75rem; | ||
margin: 0; | ||
padding: 0.4rem; | ||
border-radius: 0.2rem; | ||
z-index: 2; | ||
cursor: pointer; | ||
/* Code */ | ||
& pre { | ||
background: var(--code-background); | ||
background-clip: padding-box; | ||
} | ||
}`; | ||
const copyButtonStyles = `.copy { | ||
display: flex; | ||
gap: 0.25rem; | ||
flex-direction: row-reverse; | ||
position: absolute; | ||
inset-block-start: calc(${coreStyles.borderWidth} + var(--button-spacing)); | ||
inset-inline-end: calc(${coreStyles.borderWidth} + ${coreStyles.uiPaddingInline} / 2); | ||
background: transparent; | ||
border: ${coreStyles.borderWidth} solid transparent; | ||
opacity: 0.5; | ||
transition-property: opacity, background, border-color; | ||
transition-duration: 0.2s; | ||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); | ||
button { | ||
align-self: flex-end; | ||
margin: 0; | ||
padding: 0.4rem; | ||
border-radius: 0.2rem; | ||
z-index: 1; | ||
cursor: pointer; | ||
&:hover, &:focus:focus-visible { | ||
opacity: 0.75; | ||
background: ${framesStyles.inlineButtonHoverBackground}; | ||
} | ||
transition-property: opacity, background, border-color, box-shadow; | ||
transition-duration: 0.2s; | ||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); | ||
&:active { | ||
transition-duration: 0s; | ||
opacity: 1; | ||
border-color: ${framesStyles.inlineButtonActiveBorder}; | ||
} | ||
/* Mobile-first styles: Make the button visible and tappable */ | ||
width: 2.5rem; | ||
height: 2.5rem; | ||
background: var(--code-background); | ||
border: ${coreStyles.borderWidth} solid ${framesStyles.inlineButtonBorder}; | ||
opacity: 0.75; | ||
&::before { | ||
content: ${copyToClipboard}; | ||
line-height: 0; | ||
} | ||
/* On hover or focus, make the button fully opaque */ | ||
&:hover, &:focus:focus-visible { | ||
opacity: 1; | ||
box-shadow: inset 0 0 0 2.5rem ${framesStyles.inlineButtonHoverOrFocusBackground}; | ||
} | ||
.feedback { | ||
--tooltip-arrow-size: 0.35rem; | ||
--tooltip-bg: ${framesStyles.tooltipSuccessBackground}; | ||
color: ${framesStyles.tooltipSuccessForeground}; | ||
pointer-events: none; | ||
user-select: none; | ||
-webkit-user-select: none; | ||
position: relative; | ||
align-self: center; | ||
background-color: var(--tooltip-bg); | ||
z-index: 99; | ||
padding: 0.125rem 0.75rem; | ||
border-radius: 0.2rem; | ||
margin-inline-end: var(--tooltip-arrow-size); | ||
opacity: 0; | ||
transition-property: opacity, transform; | ||
transition-duration: 0.2s; | ||
transition-timing-function: ease-in-out; | ||
transform: translate3d(0, 0.25rem, 0); | ||
/* On press, also make the background more prominent */ | ||
&:active { | ||
opacity: 1; | ||
box-shadow: inset 0 0 0 2.5rem ${framesStyles.inlineButtonActiveBackground}; | ||
} | ||
&::after { | ||
position: absolute; | ||
content: ''; | ||
top: calc(50% - var(--tooltip-arrow-size)); | ||
inset-inline-end: calc(-2 * (var(--tooltip-arrow-size) - 0.5px)); | ||
border: var(--tooltip-arrow-size) solid transparent; | ||
border-inline-start-color: var(--tooltip-bg); | ||
} | ||
&::before { | ||
content: ${copyToClipboard}; | ||
line-height: 0; | ||
} | ||
} | ||
button:focus + .feedback.show { | ||
opacity: 1; | ||
transform: translate3d(0, 0, 0); | ||
.feedback { | ||
--tooltip-arrow-size: 0.35rem; | ||
--tooltip-bg: ${framesStyles.tooltipSuccessBackground}; | ||
color: ${framesStyles.tooltipSuccessForeground}; | ||
pointer-events: none; | ||
user-select: none; | ||
-webkit-user-select: none; | ||
position: relative; | ||
align-self: center; | ||
background-color: var(--tooltip-bg); | ||
z-index: 99; | ||
padding: 0.125rem 0.75rem; | ||
border-radius: 0.2rem; | ||
margin-inline-end: var(--tooltip-arrow-size); | ||
opacity: 0; | ||
transition-property: opacity, transform; | ||
transition-duration: 0.2s; | ||
transition-timing-function: ease-in-out; | ||
transform: translate3d(0, 0.25rem, 0); | ||
&::after { | ||
position: absolute; | ||
content: ''; | ||
top: calc(50% - var(--tooltip-arrow-size)); | ||
inset-inline-end: calc(-2 * (var(--tooltip-arrow-size) - 0.5px)); | ||
border: var(--tooltip-arrow-size) solid transparent; | ||
border-inline-start-color: var(--tooltip-bg); | ||
} | ||
} | ||
`; | ||
return styles; | ||
button:focus + .feedback.show { | ||
opacity: 1; | ||
transform: translate3d(0, 0, 0); | ||
} | ||
} | ||
@media (hover: hover) { | ||
/* If a mouse is available, hide the button by default and make it smaller */ | ||
.copy button { | ||
opacity: 0; | ||
width: 2rem; | ||
height: 2rem; | ||
} | ||
/* Reveal the non-hovered button in the following cases: | ||
- when the frame is hovered | ||
- when the frame is focused | ||
- when the frame contains a visible feedback message | ||
*/ | ||
.frame:hover .copy button:not(:hover), | ||
.frame:focus-within:has(:focus-visible) .copy button:not(:hover), | ||
.frame:has(.feedback.show) .copy button:not(:hover) { | ||
opacity: 0.75; | ||
} | ||
}`; | ||
const styles = [ | ||
// Always add base frame styles | ||
frameStyles, | ||
// Add copy button styles if enabled | ||
options.showCopyToClipboardButton ? copyButtonStyles : "" | ||
]; | ||
return styles.join("\n"); | ||
} | ||
@@ -413,8 +442,11 @@ | ||
function pluginFrames(options = {}) { | ||
const extractFileNameFromCode = options.extractFileNameFromCode ?? true; | ||
const showCopyToClipboardButton = options.showCopyToClipboardButton ?? true; | ||
options = { | ||
extractFileNameFromCode: true, | ||
showCopyToClipboardButton: true, | ||
...options | ||
}; | ||
return { | ||
name: "Frames", | ||
baseStyles: ({ theme, coreStyles }) => getFramesBaseStyles(theme, coreStyles, options.styleOverrides || {}), | ||
jsModules: showCopyToClipboardButton ? [getCopyJsModule(`.expressive-code .copy button`)] : void 0, | ||
baseStyles: ({ theme, coreStyles }) => getFramesBaseStyles(theme, coreStyles, options), | ||
jsModules: options.showCopyToClipboardButton ? [getCopyJsModule(`.expressive-code .copy button`)] : void 0, | ||
hooks: { | ||
@@ -432,3 +464,3 @@ preprocessMetadata: ({ codeBlock }) => { | ||
preprocessCode: ({ codeBlock }) => { | ||
if (!extractFileNameFromCode) | ||
if (!options.extractFileNameFromCode) | ||
return; | ||
@@ -456,3 +488,3 @@ const blockData = pluginFramesData.getOrCreateFor(codeBlock); | ||
const extraElements = []; | ||
if (showCopyToClipboardButton) { | ||
if (options.showCopyToClipboardButton) { | ||
const codeToCopy = codeBlock.code.replace(/\n/g, "\x7F"); | ||
@@ -459,0 +491,0 @@ extraElements.push( |
{ | ||
"name": "@expressive-code/plugin-frames", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "Frames plugin for Expressive Code. Wraps code blocks in a styled editor or terminal frame with support for titles, multiple tabs and more.", | ||
@@ -21,7 +21,7 @@ "keywords": [], | ||
"dependencies": { | ||
"@expressive-code/core": "^0.7.0", | ||
"@expressive-code/core": "^0.8.0", | ||
"hastscript": "^7.2.0" | ||
}, | ||
"devDependencies": { | ||
"@internal/test-utils": "^0.2.2", | ||
"@internal/test-utils": "^0.2.3", | ||
"hast-util-select": "^5.0.5", | ||
@@ -28,0 +28,0 @@ "hast-util-to-html": "^8.0.4" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
134570
1566
+ Added@expressive-code/core@0.8.1(transitive)
- Removed@expressive-code/core@0.7.0(transitive)
Updated@expressive-code/core@^0.8.0