Skip to content

Commit 1e70664

Browse files
chore(deps): update 2 Quarto extensions (#175)
Co-authored-by: mcanouil-dev[bot] <211049963+mcanouil-dev[bot]@users.noreply.github.com>
1 parent 7e77662 commit 1e70664

15 files changed

Lines changed: 782 additions & 80 deletions

File tree

docs/_extensions/mcanouil/atelier/_extension.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
title: Atelier
22
author: Mickaël Canouil
3-
version: 0.10.2
3+
version: 0.10.3
44
quarto-required: ">=1.9.36"
55
contributes:
66
project:
@@ -84,5 +84,4 @@ contributes:
8484
- file: html/scripts/ordinal-dates.html
8585
- file: html/scripts/a11y-fixes.html
8686
- file: html/scripts/navbar-tooltips.html
87-
source: mcanouil/quarto-atelier@0.10.2
88-
source-type: registry
87+
source: mcanouil/quarto-atelier@0.10.3

docs/_extensions/mcanouil/atelier/_modules/string.lua

Lines changed: 181 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil
6-
--- @version 1.0.0
6+
--- @version 1.1.0
77

88
local M = {}
99

@@ -76,6 +76,158 @@ function M.to_string(val)
7676
return str ~= '' and str or nil
7777
end
7878

79+
--- Strip one layer of surrounding bracket or punctuation characters.
80+
--- Handles balanced pairs: () [] {} "" '' `` «»
81+
--- Handles trailing-only punctuation: , . ; : ! ?
82+
--- @param text string The input text
83+
--- @return string prefix Characters stripped from the start (may be empty)
84+
--- @return string inner The inner text after stripping
85+
--- @return string suffix Characters stripped from the end (may be empty)
86+
function M.strip_surrounding(text)
87+
if not text or #text < 2 then
88+
return '', text or '', ''
89+
end
90+
91+
local balanced = {
92+
['('] = ')', ['['] = ']', ['{'] = '}',
93+
['"'] = '"', ["'"] = "'", ['`'] = '`',
94+
}
95+
-- UTF-8 guillemets
96+
local first_two = text:sub(1, 2)
97+
local last_two = text:sub(-2)
98+
if first_two == '\xC2\xAB' and last_two == '\xC2\xBB' then
99+
return first_two, text:sub(3, -3), last_two
100+
end
101+
102+
local first = text:sub(1, 1)
103+
local last = text:sub(-1)
104+
105+
if balanced[first] and last == balanced[first] then
106+
return first, text:sub(2, -2), last
107+
end
108+
109+
local trailing = {
110+
[','] = true, ['.'] = true, [';'] = true,
111+
[':'] = true, ['!'] = true, ['?'] = true,
112+
}
113+
if trailing[last] then
114+
return '', text:sub(1, -2), last
115+
end
116+
117+
return '', text, ''
118+
end
119+
120+
--- Peel unbalanced surrounding brackets and trailing punctuation from a token.
121+
--- Unlike `strip_surrounding`, this does not require a balanced pair: it removes
122+
--- any run of leading opening-bracket characters and any run of trailing
123+
--- closing-bracket or punctuation characters. This handles bracket groups that
124+
--- Pandoc split across whitespace, e.g. "(#2," and "#3)" from "(#2, #3)".
125+
--- Leading set: ( [ { " ' ` and the 2-byte UTF-8 « (\xC2\xAB).
126+
--- Trailing set: ) ] } " ' ` , . ; : ! ? and the 2-byte UTF-8 » (\xC2\xBB).
127+
--- @param text string The input text
128+
--- @return string prefix Characters peeled from the start (may be empty)
129+
--- @return string inner The inner text after peeling
130+
--- @return string suffix Characters peeled from the end (may be empty)
131+
function M.strip_edges(text)
132+
if not text or text == '' then
133+
return '', text or '', ''
134+
end
135+
136+
local leading = {
137+
['('] = true, ['['] = true, ['{'] = true,
138+
['"'] = true, ["'"] = true, ['`'] = true,
139+
}
140+
local trailing = {
141+
[')'] = true, [']'] = true, ['}'] = true,
142+
['"'] = true, ["'"] = true, ['`'] = true,
143+
[','] = true, ['.'] = true, [';'] = true,
144+
[':'] = true, ['!'] = true, ['?'] = true,
145+
}
146+
147+
local first = 1
148+
local last = #text
149+
local prefix = ''
150+
local suffix = ''
151+
152+
while first <= last do
153+
if text:sub(first, first + 1) == '\xC2\xAB' then
154+
prefix = prefix .. '\xC2\xAB'
155+
first = first + 2
156+
elseif leading[text:sub(first, first)] then
157+
prefix = prefix .. text:sub(first, first)
158+
first = first + 1
159+
else
160+
break
161+
end
162+
end
163+
164+
while last >= first do
165+
-- The two-byte window can only match a real «/» pair: the leading loop
166+
-- never leaves \xC2 at first - 1 (openers are ASCII or the \xAB of a peeled
167+
-- «), so the guillemet check cannot straddle the already-peeled prefix.
168+
if last >= 2 and text:sub(last - 1, last) == '\xC2\xBB' then
169+
suffix = '\xC2\xBB' .. suffix
170+
last = last - 2
171+
elseif trailing[text:sub(last, last)] then
172+
suffix = text:sub(last, last) .. suffix
173+
last = last - 1
174+
else
175+
break
176+
end
177+
end
178+
179+
return prefix, text:sub(first, last), suffix
180+
end
181+
182+
--- Find a balanced bracket pair anywhere in the text and split around it.
183+
--- Walks the text from `start_pos` looking for an opening bracket whose matching
184+
--- closing bracket appears later in the string. Returns the text split into
185+
--- a prefix (up to and including the opening bracket), the inner content, and
186+
--- a suffix (closing bracket and everything after).
187+
--- Supports the same bracket pairs as `strip_surrounding`:
188+
--- () [] {} "" '' `` and the 2-byte UTF-8 guillemets «».
189+
--- @param text string The input text
190+
--- @param start_pos integer|nil Byte position to start searching from (default 1)
191+
--- @return string|nil prefix Text up to and including the opening bracket
192+
--- @return string|nil content Non-empty text between the brackets
193+
--- @return string|nil suffix Closing bracket and trailing text
194+
--- @return integer|nil open_pos Byte position of the opening bracket
195+
function M.find_bracketed_content(text, start_pos)
196+
if not text or #text < 2 then
197+
return nil, nil, nil, nil
198+
end
199+
start_pos = start_pos or 1
200+
201+
local balanced = {
202+
['('] = ')', ['['] = ']', ['{'] = '}',
203+
['"'] = '"', ["'"] = "'", ['`'] = '`',
204+
}
205+
206+
local i = start_pos
207+
while i <= #text do
208+
-- UTF-8 guillemet «…»
209+
if text:sub(i, i + 1) == '\xC2\xAB' then
210+
local close_pos = text:find('\xC2\xBB', i + 2, true)
211+
if close_pos and close_pos > i + 2 then
212+
return text:sub(1, i + 1), text:sub(i + 2, close_pos - 1), text:sub(close_pos), i
213+
end
214+
i = i + 2
215+
else
216+
local c = text:sub(i, i)
217+
local close_char = balanced[c]
218+
if close_char then
219+
local close_pos = text:find(close_char, i + 1, true)
220+
if close_pos and close_pos > i + 1 then
221+
return text:sub(1, i), text:sub(i + 1, close_pos - 1), text:sub(close_pos), i
222+
end
223+
end
224+
i = i + 1
225+
end
226+
end
227+
228+
return nil, nil, nil, nil
229+
end
230+
79231
-- ============================================================================
80232
-- ESCAPE UTILITIES
81233
-- ============================================================================
@@ -106,10 +258,37 @@ function M.escape_typst(text)
106258
end
107259

108260
--- Escape characters for Typst string literals (inside `"..."`).
261+
--- Handles backslash, double quote, newline, carriage return, and tab.
109262
--- @param text string The text to escape
110263
--- @return string The escaped text safe for Typst string literals
111264
function M.escape_typst_string(text)
112-
return text:gsub('\\', '\\\\'):gsub('"', '\\"')
265+
return (text
266+
:gsub('\\', '\\\\')
267+
:gsub('"', '\\"')
268+
:gsub('\n', '\\n')
269+
:gsub('\r', '\\r')
270+
:gsub('\t', '\\t'))
271+
end
272+
273+
--- Escape characters for JavaScript string literals (inside `"..."` or `'...'`).
274+
--- Handles backslash, both quote styles, newlines, carriage returns, tabs,
275+
--- form feeds, and the `</` sequence so payloads cannot break out of a
276+
--- surrounding inline `<script>` block.
277+
--- @param text string|nil The text to escape
278+
--- @return string The escaped text safe for JavaScript string literals
279+
--- @usage local safe = M.escape_js_string([[a "b" </script>]])
280+
function M.escape_js_string(text)
281+
if text == nil then return '' end
282+
if type(text) ~= 'string' then text = tostring(text) end
283+
return (text
284+
:gsub('\\', '\\\\')
285+
:gsub('"', '\\"')
286+
:gsub("'", "\\'")
287+
:gsub('\n', '\\n')
288+
:gsub('\r', '\\r')
289+
:gsub('\t', '\\t')
290+
:gsub('\f', '\\f')
291+
:gsub('</', '<\\/'))
113292
end
114293

115294
--- Escape special Lua pattern characters for use in string.gsub.

docs/_extensions/mcanouil/atelier/html/theme.scss

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ $border-radius-sm: 0.375rem !default;
55
$border-radius-lg: 0.75rem !default;
66
$enable-smooth-scroll: true !default;
77

8+
// `.btn-close-white` asks Bootstrap to invert the dismiss icon to force a light
9+
// one. The icon follows the text around it instead, see DISMISS BUTTON below,
10+
// so there is nothing left for the class to invert.
11+
$btn-close-white-filter: none !default;
12+
813
/*-- scss:functions --*/
914

1015
// Mix the page background and foreground. brand injects different
@@ -756,6 +761,32 @@ body .navbar .gitlink-widget-dropdown {
756761
}
757762
}
758763

764+
// ===========================================================================
765+
// DISMISS BUTTON
766+
// ===========================================================================
767+
// Bootstrap bakes black into the .btn-close icon and lightens it under
768+
// [data-bs-theme="dark"], which Quarto never sets: it marks the scheme on the
769+
// body and swaps the whole stylesheet bundle. The icon stayed black on the
770+
// dark surface of a modal, an offcanvas, or a toast.
771+
//
772+
// Painting it from the page foreground would only move the problem. A
773+
// contextual dismissible alert keeps its light tint in the dark bundle,
774+
// because Bootstrap gates the dark tints on that same attribute, and a light
775+
// icon is no more readable there than a black one is on a modal. The icon is
776+
// masked from the text colour around it instead, so it follows the surface it
777+
// sits on, whichever bundle is active.
778+
.btn-close,
779+
.btn-close:hover {
780+
color: inherit;
781+
}
782+
783+
.btn-close {
784+
background-image: none;
785+
background-color: currentcolor;
786+
mask: var(--bs-btn-close-bg) center / $btn-close-width auto no-repeat;
787+
-webkit-mask: var(--bs-btn-close-bg) center / $btn-close-width auto no-repeat;
788+
}
789+
759790
// ===========================================================================
760791
// 404 PAGE
761792
// ===========================================================================
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
title: gitlink
22
author: Mickaël Canouil
3-
version: 1.10.0
3+
version: 1.10.1
44
quarto-required: ">=1.9.38"
55
contributes:
66
filters:
77
- path: gitlink.lua
88
at: post-quarto
9-
source: mcanouil/quarto-gitlink@1.10.0
10-
source-type: registry
9+
source: mcanouil/quarto-gitlink@1.10.1

docs/_extensions/mcanouil/gitlink/_modules/bitbucket.lua

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
--- MC Bitbucket - Bitbucket-specific functionality for gitlink extension
2-
--- @module bitbucket
2+
--- @module "bitbucket"
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil
66

7-
local str = require("_modules/string")
7+
--- Load a sibling module from the same directory as this file.
8+
--- @param filename string The sibling module filename (e.g., 'string.lua')
9+
--- @return table The loaded module
10+
local function load_sibling(filename)
11+
local source = debug.getinfo(1, 'S').source:sub(2)
12+
local dir = source:match('(.*[/\\])') or ''
13+
return require((dir .. filename):gsub('%.lua$', ''))
14+
end
15+
16+
local str = load_sibling('string.lua')
817

918
local bitbucket_module = {}
1019

docs/_extensions/mcanouil/gitlink/_modules/git.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--- MC Git - Git repository utilities for Quarto Lua filters and shortcodes
2-
--- @module git
2+
--- @module "git"
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil

docs/_extensions/mcanouil/gitlink/_modules/html.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--- MC HTML - HTML generation and dependency management for Quarto Lua filters and shortcodes
2-
--- @module html
2+
--- @module "html"
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil

docs/_extensions/mcanouil/gitlink/_modules/logging.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--- MC Logging - Formatted log output for Quarto Lua filters and shortcodes
2-
--- @module logging
2+
--- @module "logging"
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil

docs/_extensions/mcanouil/gitlink/_modules/paths.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--- MC Paths - Path resolution utilities for Quarto Lua filters and shortcodes
2-
--- @module paths
2+
--- @module "paths"
33
--- @license MIT
44
--- @copyright 2026 Mickaël Canouil
55
--- @author Mickaël Canouil

0 commit comments

Comments
 (0)