diff --git a/src/command/render/filters.ts b/src/command/render/filters.ts index 3c2a5ef838f..127d3feaddd 100644 --- a/src/command/render/filters.ts +++ b/src/command/render/filters.ts @@ -457,14 +457,6 @@ function languageFilterParams(format: Format) { [kCodeSummary]: format.metadata[kCodeSummary] || language[kCodeSummary], [kTocTitleDocument]: language[kTocTitleDocument], }; - Object.keys(language).forEach((key) => { - if ( - key.startsWith("callout-") || key.startsWith("crossref-") || - key.startsWith("environment-") - ) { - params[key] = language[key]; - } - }); // default prefixes based on titles [ "fig", @@ -481,6 +473,14 @@ function languageFilterParams(format: Format) { ].forEach((type) => { params[`crossref-${type}-prefix`] = language[`crossref-${type}-title`]; }); + Object.keys(language).forEach((key) => { + if ( + key.startsWith("callout-") || key.startsWith("crossref-") || + key.startsWith("environment-") + ) { + params[key] = language[key]; + } + }); return params; } diff --git a/src/config/constants.ts b/src/config/constants.ts index 7777c268c33..82495797d5e 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -248,6 +248,11 @@ export const kCalloutNoteCaption = "callout-note-title"; export const kCalloutWarningCaption = "callout-warning-title"; export const kCalloutImportantCaption = "callout-important-title"; export const kCalloutCautionCaption = "callout-caution-title"; +export const kCalloutTipPrefix = "callout-tip-prefix"; +export const kCalloutNotePrefix = "callout-note-prefix"; +export const kCalloutWarningPrefix = "callout-warning-prefix"; +export const kCalloutImportantPrefix = "callout-important-prefix"; +export const kCalloutCautionPrefix = "callout-caution-prefix"; export const kSectionTitleAbstract = "section-title-abstract"; export const kSectionTitleFootnotes = "section-title-footnotes"; export const kSectionTitleReferences = "section-title-references"; @@ -376,6 +381,11 @@ export const kLanguageDefaultsKeys = [ kCalloutWarningCaption, kCalloutImportantCaption, kCalloutCautionCaption, + kCalloutTipPrefix, + kCalloutNotePrefix, + kCalloutWarningPrefix, + kCalloutImportantPrefix, + kCalloutCautionPrefix, kSectionTitleAbstract, kSectionTitleFootnotes, kSectionTitleReferences, diff --git a/src/core/language.ts b/src/core/language.ts index 97755ff5ca8..e7b24379011 100644 --- a/src/core/language.ts +++ b/src/core/language.ts @@ -162,7 +162,12 @@ export function translationsForLang(language: FormatLanguage, lang: string) { // start with the defaults let translations = {} as FormatLanguage; Object.keys(language).forEach((key) => { - if (kLanguageDefaultsKeys.includes(key)) { + // crossrefs can be custom, so be more lenient + if ( + kLanguageDefaultsKeys.includes(key) || + key.match(/^crossref-.*-title$/) || + key.match(/^crossref-.*-prefix$/) + ) { translations[key] = language[key]; } }); @@ -193,13 +198,13 @@ export async function formatLanguage( metadata[kLang] || "en" ) as string; - const defaultLanguge = translationsForLang( + const defaultLanguage = translationsForLang( (await readDefaultLanguageTranslations(langCode)).language, langCode, ); // merge any user provided language w/ the defaults - language = mergeConfigs(defaultLanguge, language); + language = mergeConfigs(defaultLanguage, language); // now select the correct variations based on the lang code and translations return translationsForLang(language, langCode); diff --git a/src/resources/filters/crossref/refs.lua b/src/resources/filters/crossref/refs.lua index 200d3fac074..b5150fe1a65 100644 --- a/src/resources/filters/crossref/refs.lua +++ b/src/resources/filters/crossref/refs.lua @@ -22,7 +22,7 @@ function resolveRefs() local refTypes = valid_ref_types() -- scan citations for refs - local refs = pandoc.List() + local refs = pandoc.Inlines({}) for i, cite in ipairs (citeEl.citations) do -- get the label and type, and note if the label is uppercase local label = cite.id diff --git a/src/resources/filters/customnodes/callout.lua b/src/resources/filters/customnodes/callout.lua index 110608cf46d..8a9f4c17dee 100644 --- a/src/resources/filters/customnodes/callout.lua +++ b/src/resources/filters/customnodes/callout.lua @@ -139,6 +139,7 @@ function _callout_main() _quarto.ast.add_renderer("Callout", function(_) return _quarto.format.isEpubOutput() or _quarto.format.isRevealJsOutput() end, function (node) + node = _quarto.modules.callouts.decorate_callout_title_with_crossref(node) local title = quarto.utils.as_inlines(node.title) local type = node.type local calloutAppearance = node.appearance @@ -259,30 +260,34 @@ function _callout_main() icon_color = "brand-color." .. callout_theme_color_map[callout.type] end end - local title = callout.title - if title == nil then - title = pandoc.Plain(_quarto.modules.callouts.displayName(callout.type)) + if callout.attr.identifier == "" then + return _quarto.format.typst.function_call("callout", { + { "body", _quarto.format.typst.as_typst_content(callout.content) }, + { "title", _quarto.format.typst.as_typst_content( + callout.title or pandoc.Plain(_quarto.modules.callouts.displayName(callout.type)) + )}, + { "background_color", pandoc.RawInline("typst", background_color) }, + { "icon_color", pandoc.RawInline("typst", icon_color) }, + { "icon", pandoc.RawInline("typst", "" .. icon .. "()")} + }) end local typst_callout = _quarto.format.typst.function_call("callout", { { "body", _quarto.format.typst.as_typst_content(callout.content) }, - { "title", _quarto.format.typst.as_typst_content(title) }, + { "title", _quarto.format.typst.as_typst_content(callout.title, "inlines") + }, { "background_color", pandoc.RawInline("typst", background_color) }, { "icon_color", pandoc.RawInline("typst", icon_color) }, { "icon", pandoc.RawInline("typst", "" .. icon .. "()")} }) - if callout.attr.identifier == "" then - return typst_callout - end - local category = crossref.categories.by_ref_type[refType(callout.attr.identifier)] return make_typst_figure { content = typst_callout, caption_location = "top", caption = pandoc.Plain(pandoc.Str("")), - kind = "quarto-callout-" .. callout.type, - supplement = category.name, + kind = "quarto-callout-" .. _quarto.modules.callouts.displayName(callout.type), + supplement = param("crossref-" .. callout.type .. "-prefix") or category.name, numbering = "1", identifier = callout.attr.identifier } diff --git a/src/resources/filters/modules/callouts.lua b/src/resources/filters/modules/callouts.lua index e63123681e8..4f3ac25a947 100644 --- a/src/resources/filters/modules/callouts.lua +++ b/src/resources/filters/modules/callouts.lua @@ -10,7 +10,11 @@ local function callout_title_prefix(callout, withDelimiter) return end - return titlePrefix(category.ref_type, category.name, callout.order, withDelimiter) + -- https://github.com/quarto-dev/quarto-cli/issues/10894 + -- honor custom callout title if it exists + local default = param("callout-" .. callout.type .. "-title", category.name) + + return titlePrefix(category.ref_type, default, callout.order, withDelimiter) end local function decorate_callout_title_with_crossref(callout) diff --git a/src/resources/filters/modules/typst.lua b/src/resources/filters/modules/typst.lua index de7c7c16d4a..f0fd945b948 100644 --- a/src/resources/filters/modules/typst.lua +++ b/src/resources/filters/modules/typst.lua @@ -59,12 +59,21 @@ local function _main() end end - local function as_typst_content(content) - local result = pandoc.Blocks({}) - result:insert(pandoc.RawInline("typst", "[\n")) - result:extend(quarto.utils.as_blocks(content) or {}) - result:insert(pandoc.RawInline("typst", "]\n")) - return result + local function as_typst_content(content, blocks_or_inlines) + blocks_or_inlines = blocks_or_inlines or "blocks" + if blocks_or_inlines == "blocks" then + local result = pandoc.Blocks({}) + result:insert(pandoc.RawInline("typst", "[")) + result:extend(quarto.utils.as_blocks(content) or {}) + result:insert(pandoc.RawInline("typst", "]\n")) + return result + else + local result = pandoc.Inlines({}) + result:insert(pandoc.RawInline("typst", "[")) + result:extend(quarto.utils.as_inlines(content) or {}) + result:insert(pandoc.RawInline("typst", "]")) + return result + end end return { diff --git a/src/resources/filters/quarto-post/latex.lua b/src/resources/filters/quarto-post/latex.lua index 42a04ddc533..8797ff59ead 100644 --- a/src/resources/filters/quarto-post/latex.lua +++ b/src/resources/filters/quarto-post/latex.lua @@ -23,7 +23,8 @@ local function ensure_callout_counter(ref) quarto.doc.include_text('in-header', newcommand) end -function latexCalloutBoxDefault(title, callout_type, icon, callout) +function latexCalloutBoxDefault(title, callout_type, icon, callout) + title = title or "" -- callout dimensions local leftBorderWidth = '.75mm' @@ -31,11 +32,12 @@ function latexCalloutBoxDefault(title, callout_type, icon, callout) local borderRadius = '.35mm' local leftPad = '2mm' local color = _quarto.modules.callouts.latexColorForType(callout_type) + local display_title = _quarto.modules.callouts.displayName(callout_type) local frameColor = _quarto.modules.callouts.latexFrameColorForType(callout_type) local iconForType = _quarto.modules.callouts.iconForType(callout_type) - local calloutContents = pandoc.List({}); + local calloutContents = pandoc.List({}) if is_valid_ref_type(refType(callout.attr.identifier)) then local ref = refType(callout.attr.identifier) @@ -43,12 +45,18 @@ function latexCalloutBoxDefault(title, callout_type, icon, callout) -- ensure that front matter includes the correct new counter types ensure_callout_counter(ref) - local delim = "" + local suffix = "" if title:len() > 0 then - delim = pandoc.utils.stringify(titleDelim()) + suffix = pandoc.utils.stringify(titleDelim()) .. " " .. title end - title = crossref_info.prefix .. " \\ref*{" .. callout.attr.identifier .. "}" .. delim .. " " .. title + title = display_title .. " \\ref*{" .. callout.attr.identifier .. "}" .. suffix calloutContents:insert(pandoc.RawInline('latex', '\\quartocallout' .. crossref_info.ref_type .. '{' .. callout.attr.identifier .. '} ')) + else + if title:len() > 0 then + title = title + else + title = display_title + end end -- generate options @@ -411,9 +419,7 @@ function render_latex() -- generate the callout box local callout if calloutAppearance == _quarto.modules.constants.kCalloutAppearanceDefault then - if title == nil then - title = _quarto.modules.callouts.displayName(type) - else + if title ~= nil then title = pandoc.write(pandoc.Pandoc(title), 'latex') end callout = latexCalloutBoxDefault(title, type, icon, node) @@ -467,7 +473,6 @@ function render_latex() quarto.doc.use_latex_package('fancyvrb') quarto.doc.include_text('in-header', '\\VerbatimFootnotes') end - return pandoc.Div(calloutContents) end, Note = function(n) diff --git a/src/resources/formats/typst/pandoc/quarto/definitions.typ b/src/resources/formats/typst/pandoc/quarto/definitions.typ index 6e0bfeb8f39..ec679e46006 100644 --- a/src/resources/formats/typst/pandoc/quarto/definitions.typ +++ b/src/resources/formats/typst/pandoc/quarto/definitions.typ @@ -142,7 +142,7 @@ new_title)) block_with_new_content(old_callout, - new_title_block + + block(below: 0pt, new_title_block) + old_callout.body.children.at(1)) } diff --git a/tests/docs/smoke-all/2024/09/26/issue-10894-1.qmd b/tests/docs/smoke-all/2024/09/26/issue-10894-1.qmd new file mode 100644 index 00000000000..cae305499bb --- /dev/null +++ b/tests/docs/smoke-all/2024/09/26/issue-10894-1.qmd @@ -0,0 +1,28 @@ +--- +format: html +language: + callout-tip-title: "5d2d2a9de01d5c3c1ab622721d0d2704" + crossref-tip-prefix: "Note" +_quarto: + tests: + html: + ensureFileRegexMatches: + - + - 'Note.*1' + - '5d2d2a9de01d5c3c1ab622721d0d2704' + - [] +--- + +::: {#tip-123 .callout-tip} + +Text + +::: + +::: {.callout-tip} + +Text + +::: + +See @tip-123. \ No newline at end of file diff --git a/tests/docs/smoke-all/2024/09/26/issue-10894-2.qmd b/tests/docs/smoke-all/2024/09/26/issue-10894-2.qmd new file mode 100644 index 00000000000..a7ecea7b77f --- /dev/null +++ b/tests/docs/smoke-all/2024/09/26/issue-10894-2.qmd @@ -0,0 +1,22 @@ +--- +format: revealjs +language: + callout-tip-title: "98ce773e8b340c34e879b359b774b802" + crossref-tip-prefix: "Note" +_quarto: + tests: + revealjs: + ensureFileRegexMatches: + - + - '98ce773e8b340c34e879b359b774b802.*1' + - 'Note.*1' + - [] +--- + +::: {#tip-123 .callout-tip} + +Text + +::: + +See @tip-123. \ No newline at end of file diff --git a/tests/docs/smoke-all/2024/09/26/issue-10894-3.qmd b/tests/docs/smoke-all/2024/09/26/issue-10894-3.qmd new file mode 100644 index 00000000000..c0e8b426e8f --- /dev/null +++ b/tests/docs/smoke-all/2024/09/26/issue-10894-3.qmd @@ -0,0 +1,47 @@ +--- +format: latex +language: + callout-tip-title: "98ce773e8b340c34e879b359b774b802" + crossref-tip-prefix: "Note" +_quarto: + tests: + latex: + ensureFileRegexMatches: + - + - '98ce773e8b340c34e879b359b774b802' + - 'Note\~\\ref\{tip-1\}' + - + - ': 98ce773e8b340c34e879b359b774b802' # should never be a title +--- + +::: {#tip-1 .callout-tip} + +Text + +::: + +::: {#tip-2 .callout-tip} + +## A title + +Text + +::: + + +::: {.callout-tip} + +Text + +::: + +::: {.callout-tip} + +## A title + +Text + +::: + + +See @tip-1, @tip-2. \ No newline at end of file