|
| 1 | +-- hr.lua — Quarto shortcode for decorative horizontal rules |
| 2 | +-- |
| 3 | +-- Usage in .qmd files: |
| 4 | +-- |
| 5 | +-- {{< hr >}} |
| 6 | +-- {{< hr style="dashed" color="sky" >}} |
| 7 | +-- {{< hr style="dotted" thickness="3px" width="50%" align="center" >}} |
| 8 | +-- {{< hr text="§" >}} |
| 9 | +-- {{< hr text="Continue Reading" style="solid" color="#6366f1" >}} |
| 10 | +-- {{< hr preset="gradient-shimmer" >}} |
| 11 | +-- {{< hr preset="fade" width="60%" >}} |
| 12 | +-- |
| 13 | +-- Pure Lua implementation — no Python helper needed. |
| 14 | +-- All styling is handled via CSS classes in great-docs.scss. |
| 15 | + |
| 16 | +local function kwarg_str(kwargs, key) |
| 17 | + local raw = kwargs[key] |
| 18 | + if raw == nil then return "" end |
| 19 | + local s = pandoc.utils.stringify(raw) |
| 20 | + return s or "" |
| 21 | +end |
| 22 | + |
| 23 | +-- Named palette colors that map to CSS custom properties |
| 24 | +local PALETTE_COLORS = { |
| 25 | + sky = true, |
| 26 | + peach = true, |
| 27 | + prism = true, |
| 28 | + lilac = true, |
| 29 | + slate = true, |
| 30 | + honey = true, |
| 31 | + dusk = true, |
| 32 | + mint = true, |
| 33 | + accent = true, |
| 34 | +} |
| 35 | + |
| 36 | +-- Valid presets |
| 37 | +local PRESETS = { |
| 38 | + ["gradient-shimmer"] = true, |
| 39 | + ["gradient-static"] = true, |
| 40 | + ["fade"] = true, |
| 41 | + ["fade-edges"] = true, |
| 42 | + ["dots"] = true, |
| 43 | + ["diamond"] = true, |
| 44 | + ["ornament"] = true, |
| 45 | + ["wave"] = true, |
| 46 | + ["double-line"] = true, |
| 47 | +} |
| 48 | + |
| 49 | +-- Valid line styles |
| 50 | +local STYLES = { |
| 51 | + solid = true, dotted = true, dashed = true, double = true, |
| 52 | +} |
| 53 | + |
| 54 | +-- Named thickness values |
| 55 | +local THICKNESS_MAP = { |
| 56 | + thin = "1px", |
| 57 | + medium = "2px", |
| 58 | + thick = "4px", |
| 59 | +} |
| 60 | + |
| 61 | +-- Named spacing values |
| 62 | +local SPACING_MAP = { |
| 63 | + compact = "1rem", |
| 64 | + normal = "2rem", |
| 65 | + spacious = "4rem", |
| 66 | +} |
| 67 | + |
| 68 | +return { |
| 69 | + ["hr"] = function(args, kwargs) |
| 70 | + local preset = kwarg_str(kwargs, "preset") |
| 71 | + local style = kwarg_str(kwargs, "style") |
| 72 | + local color = kwarg_str(kwargs, "color") |
| 73 | + local thickness = kwarg_str(kwargs, "thickness") |
| 74 | + local width = kwarg_str(kwargs, "width") |
| 75 | + local align = kwarg_str(kwargs, "align") |
| 76 | + local text = kwarg_str(kwargs, "text") |
| 77 | + local text_color = kwarg_str(kwargs, "text-color") |
| 78 | + local text_size = kwarg_str(kwargs, "text-size") |
| 79 | + local spacing = kwarg_str(kwargs, "spacing") |
| 80 | + |
| 81 | + -- Build CSS classes |
| 82 | + local classes = { "gd-hr" } |
| 83 | + |
| 84 | + if preset ~= "" and PRESETS[preset] then |
| 85 | + table.insert(classes, "gd-hr--" .. preset) |
| 86 | + end |
| 87 | + |
| 88 | + if style ~= "" and STYLES[style] then |
| 89 | + table.insert(classes, "gd-hr--" .. style) |
| 90 | + end |
| 91 | + |
| 92 | + if align == "left" then |
| 93 | + table.insert(classes, "gd-hr--left") |
| 94 | + elseif align == "right" then |
| 95 | + table.insert(classes, "gd-hr--right") |
| 96 | + end |
| 97 | + |
| 98 | + -- Build inline styles for dynamic values |
| 99 | + local inline_styles = {} |
| 100 | + |
| 101 | + -- Color: palette name → CSS var, otherwise raw CSS color |
| 102 | + if color ~= "" then |
| 103 | + if PALETTE_COLORS[color] then |
| 104 | + table.insert(inline_styles, |
| 105 | + "--gd-hr-color: var(--gd-palette-" .. color .. ", var(--gd-accent, #6366f1))") |
| 106 | + else |
| 107 | + table.insert(inline_styles, "--gd-hr-color: " .. color) |
| 108 | + end |
| 109 | + end |
| 110 | + |
| 111 | + -- Thickness |
| 112 | + if thickness ~= "" then |
| 113 | + local resolved = THICKNESS_MAP[thickness] or thickness |
| 114 | + table.insert(inline_styles, "--gd-hr-thickness: " .. resolved) |
| 115 | + end |
| 116 | + |
| 117 | + -- Width |
| 118 | + if width ~= "" then |
| 119 | + if width == "full" then |
| 120 | + width = "100%" |
| 121 | + end |
| 122 | + table.insert(inline_styles, "--gd-hr-width: " .. width) |
| 123 | + end |
| 124 | + |
| 125 | + -- Spacing |
| 126 | + if spacing ~= "" then |
| 127 | + local resolved = SPACING_MAP[spacing] or spacing |
| 128 | + table.insert(inline_styles, "--gd-hr-spacing: " .. resolved) |
| 129 | + end |
| 130 | + |
| 131 | + local style_attr = "" |
| 132 | + if #inline_styles > 0 then |
| 133 | + style_attr = ' style="' .. table.concat(inline_styles, "; ") .. '"' |
| 134 | + end |
| 135 | + |
| 136 | + local class_attr = table.concat(classes, " ") |
| 137 | + |
| 138 | + -- If there's embedded text, use the flex wrapper structure |
| 139 | + if text ~= "" then |
| 140 | + local text_classes = { "gd-hr-text" } |
| 141 | + if text_size == "sm" then |
| 142 | + table.insert(text_classes, "gd-hr-text--sm") |
| 143 | + elseif text_size == "lg" then |
| 144 | + table.insert(text_classes, "gd-hr-text--lg") |
| 145 | + end |
| 146 | + |
| 147 | + local text_style = "" |
| 148 | + if text_color ~= "" then |
| 149 | + text_style = ' style="color: ' .. text_color .. '"' |
| 150 | + end |
| 151 | + |
| 152 | + local html = '<div class="' .. class_attr .. ' gd-hr--with-text"' .. style_attr .. '>' |
| 153 | + .. '<span class="gd-hr-line"></span>' |
| 154 | + .. '<span class="' .. table.concat(text_classes, " ") .. '"' .. text_style .. '>' |
| 155 | + .. text |
| 156 | + .. '</span>' |
| 157 | + .. '<span class="gd-hr-line"></span>' |
| 158 | + .. '</div>' |
| 159 | + |
| 160 | + return pandoc.RawBlock("html", html) |
| 161 | + end |
| 162 | + |
| 163 | + -- Simple <hr> element |
| 164 | + local html = '<hr class="' .. class_attr .. '"' .. style_attr .. '>' |
| 165 | + return pandoc.RawBlock("html", html) |
| 166 | + end, |
| 167 | +} |
0 commit comments