diff --git a/src/command/render/filters.ts b/src/command/render/filters.ts index bae4f4d1a10..1081b5fc6ec 100644 --- a/src/command/render/filters.ts +++ b/src/command/render/filters.ts @@ -9,6 +9,7 @@ import { existsSync } from "../../deno_ral/fs.ts"; import { kBibliography, kBrand, + kBrandMode, kCitationLocation, kCiteMethod, kClearCellOptions, @@ -909,6 +910,7 @@ const extractTypstFilterParams = (format: Format) => { [kTocIndent]: format.metadata[kTocIndent], [kLogo]: format.metadata[kLogo], [kCssPropertyProcessing]: format.metadata[kCssPropertyProcessing], + [kBrandMode]: format.metadata[kBrandMode], [kHtmlPreTagProcessing]: format.metadata[kHtmlPreTagProcessing], }; }; diff --git a/src/config/constants.ts b/src/config/constants.ts index d322ad6fd28..988e34f1ce4 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -141,6 +141,7 @@ export const kFontPaths = "font-paths"; export const kHtmlTableProcessing = "html-table-processing"; export const kHtmlPreTagProcessing = "html-pre-tag-processing"; export const kCssPropertyProcessing = "css-property-processing"; +export const kBrandMode = "brand-mode"; export const kUseRsvgConvert = "use-rsvg-convert"; export const kValidateYaml = "validate-yaml"; diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index 3652d57fe17..af71dc09979 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -15621,6 +15621,22 @@ var require_yaml_intelligence_resources = __commonJS({ }, description: "The paper size for the document.\n" }, + { + name: "brand-mode", + schema: { + enum: [ + "light", + "dark" + ] + }, + default: "light", + tags: { + formats: [ + "typst" + ] + }, + description: "The brand mode to use for rendering the Typst document, `light` or `dark`.\n" + }, { name: "layout", schema: { @@ -20935,6 +20951,7 @@ var require_yaml_intelligence_resources = __commonJS({ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -21095,6 +21112,7 @@ var require_yaml_intelligence_resources = __commonJS({ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -23419,6 +23437,7 @@ var require_yaml_intelligence_resources = __commonJS({ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -23772,6 +23791,7 @@ var require_yaml_intelligence_resources = __commonJS({ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -24022,7 +24042,8 @@ var require_yaml_intelligence_resources = __commonJS({ "Disambiguating year suffix in author-date styles (e.g. \u201Ca\u201D in \u201CDoe,\n1999a\u201D).", "Manuscript configuration", "internal-schema-hack", - "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019." + "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.", + "The brand mode to use for rendering the Typst document,\nlight or dark." ], "schema/external-schemas.yml": [ { @@ -24251,12 +24272,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 194252, + _internalId: 194259, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 194244, + _internalId: 194251, type: "enum", enum: [ "png", @@ -24272,7 +24293,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 194251, + _internalId: 194258, type: "anyOf", anyOf: [ { @@ -31402,6 +31423,51 @@ function guessChunkOptionsFormat(options) { } // ../yaml-validation/validator.ts +function createNiceError(obj) { + const { + violatingObject, + source, + message + } = obj; + const locF = mappedIndexToLineCol(source); + let location; + try { + location = { + start: locF(violatingObject.start), + end: locF(violatingObject.end) + }; + } catch (_e) { + location = { + start: { line: 0, column: 0 }, + end: { line: 0, column: 0 } + }; + } + const mapResult = source.map(violatingObject.start); + const fileName = mapResult ? mapResult.originalString.fileName : void 0; + return { + heading: message, + error: [], + info: {}, + fileName, + location, + sourceContext: createSourceContext(violatingObject.source, { + start: violatingObject.start, + end: violatingObject.end + }) + }; +} +var NoExprTag = class extends Error { + constructor(violatingObject, source) { + super(`Unexpected !expr tag`); + this.name = "NoExprTag"; + this.niceError = createNiceError({ + violatingObject, + source, + message: "!expr tags are not allowed in Quarto outside of knitr code cells." + }); + } + niceError; +}; var ValidationContext = class { instancePath; root; @@ -31793,6 +31859,9 @@ function validateObject(value, schema2, context) { } } } + if (value.result && typeof value.result === "object" && !Array.isArray(value.result) && value.result.tag === "!expr") { + throw new NoExprTag(value, value.source); + } throw new InternalError(`Couldn't locate key ${key}`); }; const inspectedProps = /* @__PURE__ */ new Set(); diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index 8eb91e50487..7bc0b7f3045 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -15622,6 +15622,22 @@ try { }, description: "The paper size for the document.\n" }, + { + name: "brand-mode", + schema: { + enum: [ + "light", + "dark" + ] + }, + default: "light", + tags: { + formats: [ + "typst" + ] + }, + description: "The brand mode to use for rendering the Typst document, `light` or `dark`.\n" + }, { name: "layout", schema: { @@ -20936,6 +20952,7 @@ try { "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -21096,6 +21113,7 @@ try { "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -23420,6 +23438,7 @@ try { "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -23773,6 +23792,7 @@ try { "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -24023,7 +24043,8 @@ try { "Disambiguating year suffix in author-date styles (e.g. \u201Ca\u201D in \u201CDoe,\n1999a\u201D).", "Manuscript configuration", "internal-schema-hack", - "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019." + "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.", + "The brand mode to use for rendering the Typst document,\nlight or dark." ], "schema/external-schemas.yml": [ { @@ -24252,12 +24273,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 194252, + _internalId: 194259, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 194244, + _internalId: 194251, type: "enum", enum: [ "png", @@ -24273,7 +24294,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 194251, + _internalId: 194258, type: "anyOf", anyOf: [ { @@ -31416,6 +31437,51 @@ ${tidyverseInfo( } // ../yaml-validation/validator.ts + function createNiceError(obj) { + const { + violatingObject, + source, + message + } = obj; + const locF = mappedIndexToLineCol(source); + let location; + try { + location = { + start: locF(violatingObject.start), + end: locF(violatingObject.end) + }; + } catch (_e) { + location = { + start: { line: 0, column: 0 }, + end: { line: 0, column: 0 } + }; + } + const mapResult = source.map(violatingObject.start); + const fileName = mapResult ? mapResult.originalString.fileName : void 0; + return { + heading: message, + error: [], + info: {}, + fileName, + location, + sourceContext: createSourceContext(violatingObject.source, { + start: violatingObject.start, + end: violatingObject.end + }) + }; + } + var NoExprTag = class extends Error { + constructor(violatingObject, source) { + super(`Unexpected !expr tag`); + this.name = "NoExprTag"; + this.niceError = createNiceError({ + violatingObject, + source, + message: "!expr tags are not allowed in Quarto outside of knitr code cells." + }); + } + niceError; + }; var ValidationContext = class { instancePath; root; @@ -31807,6 +31873,9 @@ ${tidyverseInfo( } } } + if (value.result && typeof value.result === "object" && !Array.isArray(value.result) && value.result.tag === "!expr") { + throw new NoExprTag(value, value.source); + } throw new InternalError(`Couldn't locate key ${key}`); }; const inspectedProps = /* @__PURE__ */ new Set(); diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index 9ef575dc130..5c1e641f1fb 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -8593,6 +8593,22 @@ }, "description": "The paper size for the document.\n" }, + { + "name": "brand-mode", + "schema": { + "enum": [ + "light", + "dark" + ] + }, + "default": "light", + "tags": { + "formats": [ + "typst" + ] + }, + "description": "The brand mode to use for rendering the Typst document, `light` or `dark`.\n" + }, { "name": "layout", "schema": { @@ -13907,6 +13923,7 @@ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -14067,6 +14084,7 @@ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -16391,6 +16409,7 @@ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -16744,6 +16763,7 @@ "Number of matches to display (defaults to 20)", "Matches after which to collapse additional results", "Provide button for copying search link", + "When false, do not merge navbar crumbs into the crumbs in\nsearch.json.", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "One or more keys that will act as a shortcut to launch search (single\ncharacters)", "Whether to include search result parents when displaying items in\nsearch results (when possible).", @@ -16994,7 +17014,8 @@ "Disambiguating year suffix in author-date styles (e.g. “a” in “Doe,\n1999a”).", "Manuscript configuration", "internal-schema-hack", - "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto’s default order\nis ‘knitr’, ‘jupyter’, ‘markdown’, ‘julia’." + "List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto’s default order\nis ‘knitr’, ‘jupyter’, ‘markdown’, ‘julia’.", + "The brand mode to use for rendering the Typst document,\nlight or dark." ], "schema/external-schemas.yml": [ { @@ -17223,12 +17244,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 194252, + "_internalId": 194259, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 194244, + "_internalId": 194251, "type": "enum", "enum": [ "png", @@ -17244,7 +17265,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 194251, + "_internalId": 194258, "type": "anyOf", "anyOf": [ { diff --git a/src/resources/filters/modules/typst_css.lua b/src/resources/filters/modules/typst_css.lua index 45725d5ad65..97a05daeaf2 100644 --- a/src/resources/filters/modules/typst_css.lua +++ b/src/resources/filters/modules/typst_css.lua @@ -176,7 +176,7 @@ local typst_named_colors = { lime = '#01ff70', } -local brandMode = 'light' --- ugh +local brandMode = param('brand-mode') or 'light' -- css can have fraction or percent -- typst can have int or percent @@ -748,6 +748,7 @@ local function expand_side_shorthand(items, context, warnings) end return { + set_brand_mode = set_brand_mode, parse_color = parse_color, parse_opacity = parse_opacity, output_color = output_color, diff --git a/src/resources/filters/quarto-post/cell-renderings.lua b/src/resources/filters/quarto-post/cell-renderings.lua index b3486b8326f..4829a3920cf 100644 --- a/src/resources/filters/quarto-post/cell-renderings.lua +++ b/src/resources/filters/quarto-post/cell-renderings.lua @@ -47,6 +47,13 @@ function choose_cell_renderings() if quarto.format.isHtmlOutput() and lightDiv and darkDiv then blocks:insert(pandoc.Div(lightDiv.content, pandoc.Attr("", {'light-content'}, {}))) blocks:insert(pandoc.Div(darkDiv.content, pandoc.Attr("", {'dark-content'}, {}))) + elseif quarto.format.isTypstOutput() and lightDiv and darkDiv then + local brandMode = param('brand-mode') or 'light' + if brandMode == 'light' then + blocks:insert(lightDiv) + elseif brandMode == 'dark' then + blocks:insert(darkDiv) + end else blocks:insert(lightDiv or darkDiv) end diff --git a/src/resources/filters/quarto-post/typst-brand-yaml.lua b/src/resources/filters/quarto-post/typst-brand-yaml.lua index 81a6f782474..6650459c7e7 100644 --- a/src/resources/filters/quarto-post/typst-brand-yaml.lua +++ b/src/resources/filters/quarto-post/typst-brand-yaml.lua @@ -59,7 +59,7 @@ function render_typst_brand_yaml() return { Pandoc = function(pandoc0) local brand = param('brand') - local brandMode = 'light' + local brandMode = param('brand-mode') or 'light' brand = brand and brand[brandMode] if brand and brand.processedData then -- color @@ -300,7 +300,7 @@ function render_typst_brand_yaml() end end, Meta = function(meta) - local brandMode = 'light' + local brandMode = param('brand-mode') or 'light' -- it can contain the path but we want to store an object here if not meta.brand or pandoc.utils.type(meta.brand) == 'Inlines' then meta.brand = {} diff --git a/src/resources/schema/document-layout.yml b/src/resources/schema/document-layout.yml index dd8795d35ff..ee0beac8f69 100644 --- a/src/resources/schema/document-layout.yml +++ b/src/resources/schema/document-layout.yml @@ -48,6 +48,15 @@ description: | The paper size for the document. +- name: brand-mode + schema: + enum: [light, dark] + default: light + tags: + formats: [typst] + description: | + The brand mode to use for rendering the Typst document, `light` or `dark`. + - name: layout schema: maybeArrayOf: string diff --git a/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color-dark.qmd b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color-dark.qmd new file mode 100644 index 00000000000..161215052f8 --- /dev/null +++ b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color-dark.qmd @@ -0,0 +1,39 @@ +--- +title: Translate brand named color references from CSS to Typst +format: + typst: + keep-typ: true + brand-mode: dark +brand: + light: + color: + palette: + dark-grey: "#444" + blue: "#82aeef" + dark: + color: + palette: + dark-grey: "#222" + blue: "#415777" +_quarto: + tests: + typst: + ensureTypstFileRegexMatches: + - + - '#block\(fill: brand-color\.dark-grey\)\[\s*#set text\(fill: brand-color\.blue\);' + - 'blue: rgb\("#415777"\)' + - 'dark-grey: rgb\("#222"\)' + - [] +--- + + +```{=typst} +// stopgap to make this look ok +#set block(inset: 6pt) +``` + +:::{style="background-color: var(--brand-dark-grey); color: var(--brand-blue)"} +This div is blue on dark grey. +::: + +{{< lipsum 2 >}} diff --git a/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color.qmd b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color.qmd new file mode 100644 index 00000000000..63b6167b72d --- /dev/null +++ b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-named-color.qmd @@ -0,0 +1,38 @@ +--- +title: Translate brand named color references from CSS to Typst +format: + typst: + keep-typ: true +brand: + light: + color: + palette: + dark-grey: "#444" + blue: "#82aeef" + dark: + color: + palette: + dark-grey: "#222" + blue: "#415777" +_quarto: + tests: + typst: + ensureTypstFileRegexMatches: + - + - '#block\(fill: brand-color\.dark-grey\)\[\s*#set text\(fill: brand-color\.blue\);' + - 'blue: rgb\("#82aeef"\)' + - 'dark-grey: rgb\("#444"\)' + - [] +--- + + +```{=typst} +// stopgap to make this look ok +#set block(inset: 6pt) +``` + +:::{style="background-color: var(--brand-dark-grey); color: var(--brand-blue)"} +This div is blue on dark grey. +::: + +{{< lipsum 2 >}} diff --git a/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-dark.qmd b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-dark.qmd new file mode 100644 index 00000000000..b416c2bf7bf --- /dev/null +++ b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-dark.qmd @@ -0,0 +1,42 @@ +--- +title: Translate brand named color references from CSS to Typst +format: + typst: + keep-typ: true + brand-mode: dark +brand: + light: + color: + palette: + light-grey: "#444" + blue: "#82aeef" + dark: + color: + palette: + dark-grey: "#222" + blue: "#415777" +_quarto: + tests: + typst: + ensureTypstFileRegexMatches: + - + - 'blue: rgb\("#415777"\)' + - 'dark-grey: rgb\("#222"\)' + - '#set text\(fill: brand-color\.blue\);' + - + printsMessage: + level: INFO + regex: 'unknown brand color light-grey' +--- + + +```{=typst} +// stopgap to make this look ok +#set block(inset: 6pt) +``` + +:::{style="background-color: var(--brand-light-grey); color: var(--brand-blue)"} +This div is blue on dark grey. +::: + +{{< lipsum 2 >}} diff --git a/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-light.qmd b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-light.qmd new file mode 100644 index 00000000000..46a248c50db --- /dev/null +++ b/tests/docs/smoke-all/typst/brand-yaml/color/typst-css-duobrand-wrong-named-color-light.qmd @@ -0,0 +1,42 @@ +--- +title: Translate brand named color references from CSS to Typst +format: + typst: + keep-typ: true + brand-mode: light +brand: + light: + color: + palette: + light-grey: "#444" + blue: "#82aeef" + dark: + color: + palette: + dark-grey: "#222" + blue: "#415777" +_quarto: + tests: + typst: + ensureTypstFileRegexMatches: + - + - 'blue: rgb\("#82aeef"\)' + - 'light-grey: rgb\("#444"\)' + - '#set text\(fill: brand-color\.blue\);' + - + printsMessage: + level: INFO + regex: 'unknown brand color dark-grey' +--- + + +```{=typst} +// stopgap to make this look ok +#set block(inset: 6pt) +``` + +:::{style="background-color: var(--brand-dark-grey); color: var(--brand-blue)"} +This div is blue on dark grey. +::: + +{{< lipsum 2 >}}