mirror of
https://github.com/hugo-fixit/FixIt.git
synced 2026-08-24 15:28:57 +00:00
feat(print): add print configuration options and enhance print functionality (#720)
This commit is contained in:
@@ -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
@@ -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', () => {
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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 -}}
|
||||
Reference in New Issue
Block a user