Skip to content

Commit 42b24d3

Browse files
add leading slash to logo paths when input is not in project directory
fixes #11982 navbar and sidebar always prepended a slash and still do (the input document location is not known when they are initialized) the other formats must call logoAddLeadingSlashes after resolveLogo this compares the directory of the input and the project directory if they are different and the path is not an URL, prepend a slash
1 parent f9eb459 commit 42b24d3

31 files changed

+300
-78
lines changed

src/core/brand/brand.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
} from "../../resources/types/zod/schema-types.ts";
3030
import { InternalError } from "../lib/error.ts";
3131

32-
import { join, relative } from "../../deno_ral/path.ts";
32+
import { dirname, join, relative, resolve } from "../../deno_ral/path.ts";
3333
import { warnOnce } from "../log.ts";
3434
import { isCssColorName } from "../css/color-names.ts";
3535
import {
@@ -38,6 +38,7 @@ import {
3838
LogoSpecifier,
3939
LogoSpecifierPathOptional,
4040
} from "../../resources/types/schema-types.ts";
41+
import { ensureLeadingSlash } from "../path.ts";
4142

4243
type ProcessedBrandData = {
4344
color: Record<string, string>;
@@ -390,6 +391,35 @@ export function resolveLogo(
390391
};
391392
}
392393

394+
const ensureLeadingSlashIfNotExternal = (path: string) =>
395+
isExternalPath(path) ? path : ensureLeadingSlash(path);
396+
397+
export function logoAddLeadingSlashes(
398+
spec: NormalizedLogoLightDarkSpecifier | undefined,
399+
brand: LightDarkBrand | undefined,
400+
input: string | undefined,
401+
): NormalizedLogoLightDarkSpecifier | undefined {
402+
if (!spec) {
403+
return spec;
404+
}
405+
if (input) {
406+
const inputDir = dirname(resolve(input));
407+
if (!brand || inputDir === brand.light?.projectDir) {
408+
return spec;
409+
}
410+
}
411+
return {
412+
light: spec.light && {
413+
...spec.light,
414+
path: ensureLeadingSlashIfNotExternal(spec.light.path),
415+
},
416+
dark: spec.dark && {
417+
...spec.dark,
418+
path: ensureLeadingSlashIfNotExternal(spec.dark.path),
419+
},
420+
};
421+
}
422+
393423
// this a typst workaround but might as well write it as a proper function
394424
export function fillLogoPaths(
395425
brand: LightDarkBrand | undefined,

src/core/path.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ export function removeTrailingSlash(path: string) {
200200
}
201201
}
202202

203+
export function ensureLeadingSlash(path: string) {
204+
if (path && !path.startsWith("/")) {
205+
return "/" + path;
206+
} else {
207+
return path;
208+
}
209+
}
210+
203211
export function resolveGlobs(
204212
root: string,
205213
globs: string[],

src/format/dashboard/format-dashboard.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import { processToolbars } from "./format-dashboard-toolbar.ts";
6666
import { processDatatables } from "./format-dashboard-tables.ts";
6767
import { assert } from "testing/asserts";
6868
import { brandBootstrapSassBundles } from "../../core/sass/brand.ts";
69-
import { resolveLogo } from "../../core/brand/brand.ts";
69+
import { logoAddLeadingSlashes, resolveLogo } from "../../core/brand/brand.ts";
7070

7171
const kDashboardClz = "quarto-dashboard";
7272

@@ -130,12 +130,14 @@ export function dashboardFormat() {
130130
alt: format.metadata[kLogoAlt] as string,
131131
};
132132
}
133-
format.metadata[kLogo] = resolveLogo(brand, logoSpec, [
133+
let logo = resolveLogo(brand, logoSpec, [
134134
"small",
135135
"medium",
136136
"large",
137137
]);
138+
logo = logoAddLeadingSlashes(logo, brand, input);
138139

140+
format.metadata[kLogo] = logo;
139141
const extras: FormatExtras = await baseHtmlFormat.formatExtras(
140142
input,
141143
markdown,

src/format/reveal/format-reveal.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ import { ProjectContext } from "../../project/types.ts";
7979
import { titleSlidePartial } from "./format-reveal-title.ts";
8080
import { registerWriterFormatHandler } from "../format-handlers.ts";
8181
import { pandocNativeStr } from "../../core/pandoc/codegen.ts";
82-
import { resolveLogo } from "../../core/brand/brand.ts";
82+
import { logoAddLeadingSlashes, resolveLogo } from "../../core/brand/brand.ts";
8383

8484
export function revealResolveFormat(format: Format) {
8585
format.metadata = revealMetadataFilter(format.metadata);
@@ -299,7 +299,7 @@ export function revealjsFormat() {
299299
theme["text-highlighting-mode"],
300300
),
301301
],
302-
[kMarkdownAfterBody]: [revealMarkdownAfterBody(format)],
302+
[kMarkdownAfterBody]: [revealMarkdownAfterBody(format, input)],
303303
},
304304
},
305305
);
@@ -378,7 +378,7 @@ export function revealjsFormat() {
378378
);
379379
}
380380

381-
function revealMarkdownAfterBody(format: Format) {
381+
function revealMarkdownAfterBody(format: Format, input: string) {
382382
let brandMode: "light" | "dark" = "light";
383383
if (format.metadata[kBrandMode] === "dark") {
384384
brandMode = "dark";
@@ -387,13 +387,14 @@ function revealMarkdownAfterBody(format: Format) {
387387
lines.push("::: {.quarto-auto-generated-content style='display: none;'}\n");
388388
const revealLogo = format
389389
.metadata[kSlideLogo] as (string | { path: string } | undefined);
390-
const logo = resolveLogo(format.render.brand, revealLogo, [
390+
let logo = resolveLogo(format.render.brand, revealLogo, [
391391
"small",
392392
"medium",
393393
"large",
394394
]);
395395
if (logo && logo[brandMode]) {
396-
const modeLogo = logo[brandMode]!;
396+
logo = logoAddLeadingSlashes(logo, format.render.brand, input);
397+
const modeLogo = logo![brandMode]!;
397398
const altText = modeLogo.alt ? `alt="${modeLogo.alt}" ` : "";
398399
lines.push(
399400
`<img src="${modeLogo.path}" ${altText}class="slide-logo" />`,

src/project/types/website/website-navigation.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,16 +1015,6 @@ async function sidebarEjsData(project: ProjectContext, sidebar: Sidebar) {
10151015

10161016
// ensure title and search are present
10171017
sidebar.title = await sidebarTitle(sidebar, project) as string | undefined;
1018-
if (sidebar.logo) {
1019-
// sidebar logo has been normalized
1020-
const sidebarLogo = sidebar.logo as NormalizedLogoLightDarkSpecifier;
1021-
if (sidebarLogo.light) {
1022-
sidebarLogo.light.path = resolveLogo(sidebarLogo.light.path)!;
1023-
}
1024-
if (sidebarLogo.dark) {
1025-
sidebarLogo.dark.path = resolveLogo(sidebarLogo.dark.path)!;
1026-
}
1027-
}
10281018
const searchOpts = await searchOptions(project);
10291019
sidebar.search = sidebar.search !== undefined
10301020
? sidebar.search
@@ -1258,16 +1248,6 @@ async function navbarEjsData(
12581248
: ("-" + (navbar[kCollapseBelow] || "lg")) as LayoutBreak,
12591249
pinned: navbar.pinned !== undefined ? !!navbar.pinned : false,
12601250
};
1261-
if (data.logo) {
1262-
// navbar logo has been normalized
1263-
const navbarLogo = data.logo as NormalizedLogoLightDarkSpecifier;
1264-
if (navbarLogo.light) {
1265-
navbarLogo.light.path = resolveLogo(navbarLogo.light.path)!;
1266-
}
1267-
if (navbarLogo.dark) {
1268-
navbarLogo.dark.path = resolveLogo(navbarLogo.dark.path)!;
1269-
}
1270-
}
12711251
// if there is no navbar title and it hasn't been set to 'false'
12721252
// then use the site title
12731253
if (!data.title && data.title !== false) {
@@ -1515,14 +1495,6 @@ async function sidebarTitle(sidebar: Sidebar, project: ProjectContext) {
15151495
}
15161496
}
15171497

1518-
function resolveLogo(logo?: string) {
1519-
if (logo && !isExternalPath(logo) && !logo.startsWith("/")) {
1520-
return "/" + logo;
1521-
} else {
1522-
return logo;
1523-
}
1524-
}
1525-
15261498
async function websiteHeadroom(project: ProjectContext) {
15271499
const { navbar, sidebars } = await websiteNavigationConfig(project);
15281500
if (navbar || sidebars?.length) {

src/project/types/website/website-shared.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ import { Format, FormatExtras } from "../../../config/types.ts";
4747
import { kPageTitle, kTitle, kTitlePrefix } from "../../../config/constants.ts";
4848
import { md5HashAsync } from "../../../core/hash.ts";
4949
export { type NavigationFooter } from "../../types.ts";
50-
import { resolveLogo } from "../../../core/brand/brand.ts";
50+
import {
51+
logoAddLeadingSlashes,
52+
resolveLogo,
53+
} from "../../../core/brand/brand.ts";
5154

5255
export interface Navigation {
5356
navbar?: Navbar;
@@ -137,11 +140,13 @@ export async function websiteNavigationConfig(project: ProjectContext) {
137140
navLogo = { path: navLogo, alt: navbar[kLogoAlt] };
138141
}
139142
}
140-
navbar.logo = resolveLogo(projectBrand, navLogo, [
143+
let logo = resolveLogo(projectBrand, navLogo, [
141144
"small",
142145
"medium",
143146
"large",
144147
]);
148+
logo = logoAddLeadingSlashes(logo, projectBrand, undefined);
149+
navbar.logo = logo;
145150
}
146151
// read sidebar
147152
const sidebar = websiteConfig(kSiteSidebar, project.config);
@@ -194,11 +199,13 @@ export async function websiteNavigationConfig(project: ProjectContext) {
194199
// }
195200
}
196201
}
197-
sidebars[0].logo = resolveLogo(projectBrand, sideLogo, [
202+
let logo = resolveLogo(projectBrand, sideLogo, [
198203
"medium",
199204
"small",
200205
"large",
201206
]);
207+
logo = logoAddLeadingSlashes(logo, projectBrand, undefined);
208+
sidebars[0].logo = logo;
202209

203210
// convert contents: auto into items
204211
for (const sb of sidebars) {

src/resources/filters/quarto-pre/shortcodes-handlers.lua

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ function initShortcodeHandlers()
112112
return quarto.shortcode.error_output("brand", args, context)
113113
end
114114

115+
local add_leading_slash = function(path)
116+
if path:match '^https?:' or path[1] == "/" then
117+
return path
118+
end
119+
return "/" .. path
120+
end
121+
115122
if brandCommand == "color" then
116123
local brandMode = 'light'
117124
if #args > 2 then
@@ -167,11 +174,11 @@ function initShortcodeHandlers()
167174
end
168175
local images = {}
169176
if lightLogo then
170-
table.insert(images, pandoc.Image(pandoc.Inlines {}, lightLogo.path, "",
177+
table.insert(images, pandoc.Image(pandoc.Inlines {}, add_leading_slash(lightLogo.path), "",
171178
pandoc.Attr("", {"light-content"}, {alt = lightLogo.alt})))
172179
end
173180
if darkLogo then
174-
table.insert(images, pandoc.Image(pandoc.Inlines {}, darkLogo.path, "",
181+
table.insert(images, pandoc.Image(pandoc.Inlines {}, add_leading_slash(darkLogo.path), "",
175182
pandoc.Attr("", {"dark-content"}, {alt = darkLogo.alt})))
176183
end
177184
if context == "block" then
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
website:
2+
navbar:
3+
left:
4+
- href: index.qmd
5+
text: Home
6+
- about.qmd
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
website:
2+
sidebar:
3+
style: "docked"
4+
search: false
5+
contents:
6+
- href: index.qmd
7+
text: Home
8+
- about.qmd
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
project:
2+
type: website
3+
4+
website:
5+
title: "tmpsite"
6+
7+
format:
8+
html:
9+
theme:
10+
- cosmo
11+
- brand
12+
13+
profile:
14+
group:
15+
- [navbar, sidebar]
16+
17+
brand: brand/brand.yml

0 commit comments

Comments
 (0)