feat(print): add print configuration options and enhance print functionality (#720)

This commit is contained in:
Cell
2026-03-22 20:06:27 +08:00
committed by GitHub
parent 0f2712f182
commit a53ef80035
5 changed files with 158 additions and 33 deletions
@@ -216,3 +216,16 @@ type = "file"
{{% /tab %}}
{{< /tabs >}}
### MathJax inside tabs
{{< tabs >}}
{{% tab title="Inline" %}}
$c = \pm\sqrt{a^2 + b^2}$ and \(f(x)=\int_{-\infty}^{\infty} \hat{f}(\xi) e^{2 \pi i \xi x} d \xi\)
{{% /tab %}}
{{% tab title="Block" %}}
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
\[ f(a) = \frac{1}{2\pi i} \oint\frac{f(z)}{z-a}dz \]
{{% /tab %}}
{{< /tabs >}}
+39 -33
View File
@@ -1613,44 +1613,50 @@ class FixIt {
initPrint() {
window.addEventListener('beforeprint', () => {
const $content = document.getElementById('content');
// revert code tabs to code blocks for better printing support
Util.forEach($content.querySelectorAll('.code-tabs'), ($codeTabs) => {
// restore action buttons to the active tab's code-header before reverting
const $actions = $codeTabs.querySelector('.tabs-actions');
const $activeBlock = $codeTabs.querySelector('.code-block.active');
if ($actions && $activeBlock) {
const $codeHeader = $activeBlock.querySelector('.code-header');
if ($codeHeader) {
Array.from($actions.children).forEach(btn => $codeHeader.appendChild(btn));
const printConfig = this.config.print || {};
if (printConfig.expandAdmonition) {
Util.forEach($content.querySelectorAll('.admonition'), ($el) => $el.classList.add('open'));
}
if (printConfig.expandCode) {
// revert code tabs to code blocks for better printing support
Util.forEach($content.querySelectorAll('.code-tabs'), ($codeTabs) => {
// restore action buttons to the active tab's code-header before reverting
const $actions = $codeTabs.querySelector('.tabs-actions');
const $activeBlock = $codeTabs.querySelector('.code-block.active');
if ($actions && $activeBlock) {
const $codeHeader = $activeBlock.querySelector('.code-header');
if ($codeHeader) {
Array.from($actions.children).forEach(btn => $codeHeader.appendChild(btn));
}
}
}
const $codeBlocks = $codeTabs.querySelectorAll('.code-block');
$codeBlocks.forEach(($codeBlock) => {
delete $codeBlock.dataset.tabInit;
$codeTabs.parentElement.insertBefore($codeBlock, $codeTabs);
const $codeBlocks = $codeTabs.querySelectorAll('.code-block');
$codeBlocks.forEach(($codeBlock) => {
delete $codeBlock.dataset.tabInit;
$codeTabs.parentElement.insertBefore($codeBlock, $codeTabs);
});
$codeTabs.parentElement.removeChild($codeTabs);
});
$codeTabs.parentElement.removeChild($codeTabs);
});
Util.forEach($content.querySelectorAll('.code-block'), ($el) => {
// line wrapping
$el.classList.add('line-wrapping');
// expand all code blocks
$el.classList.remove('is-collapsed');
// expand code preview
if ($el.querySelector('.code-expand-btn')) {
$el.classList.add('is-expanded');
}
});
FileTree.expandAll($content);
Util.forEach($content.querySelectorAll('.details'), ($el) => {
$el.classList.add('open');
});
Util.forEach($content.querySelectorAll('details'), ($el) => {
$el.setAttribute('open', '');
});
Util.forEach($content.querySelectorAll('.code-block'), ($el) => {
// line wrapping
$el.classList.add('line-wrapping');
// expand all code blocks
$el.classList.remove('is-collapsed');
// expand code preview
if ($el.querySelector('.code-expand-btn')) {
$el.classList.add('is-expanded');
}
});
}
if (printConfig.expandDetails) {
Util.forEach($content.querySelectorAll('details'), ($el) => $el.setAttribute('open', ''));
}
for (let event of this.beforeprintEventSet) {
event();
}
if (printConfig.expandFileTree) {
FileTree.expandAll($content);
}
}, false);
window.addEventListener('afterprint', () => {
+11
View File
@@ -1178,6 +1178,17 @@ folderSlash = false
# list of file or folder names to ignore
ignoreList = []
# FixIt 0.4.5 | NEW Print config
[params.print]
# whether to expand all admonitions before printing
expand_admonition = true
# whether to expand all code blocks and code tabs before printing
expand_code = true
# whether to expand all details elements before printing
expand_details = true
# whether to expand all file trees before printing
expand_file_tree = false
# FixIt 0.3.12 | NEW Custom partials config
# Custom partials must be stored in the /layouts/_partials/ directory.
# Depends on open custom blocks https://fixit.lruihao.cn/references/blocks/
+4
View File
@@ -216,6 +216,7 @@ FixIt theme assets partial
{{- /* Cell Tooltip */ -}}
{{- if eq .Site.Params.tooltip true -}}
{{- /* [todo] 临时可行性验证,需要寻找一个更稳定的替代品(Floating UI */ -}}
{{- $source := $cdn.cellTooltipJS | default "lib/cell-tooltip/cell-tooltip.umd.js" -}}
{{- dict "Source" $source "Fingerprint" $fingerprint "Defer" true | dict "Page" . "Data" | partial "store/script.html" -}}
{{- $config = dict "tooltip" true | merge $config -}}
@@ -307,6 +308,9 @@ FixIt theme assets partial
{{- end -}}
{{- end -}}
{{- /* Print */ -}}
{{- $config = partial "function/snake2camel.html" .Site.Params.print | dict "print" | merge $config -}}
{{- /* PostChat */ -}}
{{- partial "plugin/post-chat-ai.html" . -}}
@@ -0,0 +1,91 @@
{{- /*
Convert snake_case config keys to camelCase recursively.
This is mainly used to avoid problems caused by Hugo's case-insensitive config key parsing.
Keep config keys in snake_case, then convert them here for stable access in templates and JS.
@param {map} . - The input map/object with snake_case keys
@return {map} A new map with camelCase keys, or the input if it's not a map
@example
// Simple Map conversion
Input: dict "code_block" true "max_shown_lines" 10 "line_nos" false
Output: dict "codeBlock" true "maxShownLines" 10 "lineNos" false
@example
// Nested Map conversion
Input: dict "code_block" (dict "enable_wrapper" true)
Output: dict "codeBlock" (dict "enableWrapper" true)
@example
// Array of Maps conversion
Input: slice (dict "user_name" "John") (dict "user_age" 30)
Output: slice (dict "userName" "John") (dict "userAge" 30)
@example
// Practical usage in templates
{{- $siteParams := partial "function/snake2camel.html" .Site.Params }}
// Now access parameters with camelCase: $siteParams.codeBlock.enableWrapper
// [todo] refactor all config to use snake_case in Hugo config files, and camelCase in templates and JS (v1.0 breaking change)
*/ -}}
{{- $input := . -}}
{{- $output := dict -}}
{{- if reflect.IsMap $input -}}
{{- /* Process map: iterate through all key-value pairs */ -}}
{{- range $key, $value := $input -}}
{{- /*
Convert key from snake_case to camelCase
Example: "max_shown_lines" -> "maxShownLines"
Strategy: split by "_", capitalize first letter of each part (except first), then concatenate
*/ -}}
{{- $parts := split $key "_" -}}
{{- $newKey := index $parts 0 -}}
{{- range $i, $part := $parts -}}
{{- if gt $i 0 -}}
{{- /* Capitalize first letter, preserve the rest */ -}}
{{- $first := upper (substr $part 0 1) -}}
{{- $rest := substr $part 1 -}}
{{- $newKey = printf "%s%s%s" $newKey $first $rest -}}
{{- end -}}
{{- end -}}
{{- /*
Recursively process value based on its type
- For nested maps: recursively convert all nested keys
- For arrays: process each map element in the array
- For scalar values: keep as-is
*/ -}}
{{- $newValue := $value -}}
{{- if reflect.IsMap $value -}}
{{- $newValue = partial "function/snake2camel.html" $value -}}
{{- else if reflect.IsSlice $value -}}
{{- $newValue = slice -}}
{{- range $item := $value -}}
{{- if reflect.IsMap $item -}}
{{- $newValue = $newValue | append (partial "function/snake2camel.html" $item) -}}
{{- else -}}
{{- $newValue = $newValue | append $item -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* Add the converted key-value pair to output */ -}}
{{- $output = merge $output (dict $newKey $newValue) -}}
{{- end -}}
{{- return $output -}}
{{- else if reflect.IsSlice $input -}}
{{- /* Process array: recursively convert each map element */ -}}
{{- $output := slice -}}
{{- range $item := $input -}}
{{- if reflect.IsMap $item -}}
{{- $output = $output | append (partial "function/snake2camel.html" $item) -}}
{{- else -}}
{{- $output = $output | append $item -}}
{{- end -}}
{{- end -}}
{{- return $output -}}
{{- else -}}
{{- /* Return scalar values unchanged */ -}}
{{- return $input -}}
{{- end -}}