Skip to content

Commit 2da64ae

Browse files
authored
feat: make the logo link configurable (#68)
1 parent 9bc112a commit 2da64ae

File tree

18 files changed

+106
-65
lines changed

18 files changed

+106
-65
lines changed

.prettierrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"useTabs": false,
55
"tabWidth": 2,
66
"semi": true,
7-
"bracketSpacing": true
7+
"bracketSpacing": true,
8+
"plugins": ["prettier-plugin-astro"]
89
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"husky": "^9.0.11",
2121
"is-ci": "^3.0.1",
2222
"prettier": "^3.3.1",
23+
"prettier-plugin-astro": "^0.13.0",
2324
"tempfile": "^5.0.0"
2425
},
2526
"lint-staged": {},

packages/astro/src/default/components/Header.astro

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,21 @@
11
---
2-
import fs from 'node:fs';
3-
import path from 'node:path';
2+
import Logo from './Logo.astro';
43
import { LoginButton } from './LoginButton';
54
import { ThemeSwitch } from './ThemeSwitch';
65
import { useAuth } from './setup';
76
8-
const LOGO_EXTENSIONS = [
9-
'svg',
10-
'png',
11-
'jpeg',
12-
'jpg',
13-
];
14-
15-
let logo;
16-
17-
for (const logoExt of LOGO_EXTENSIONS) {
18-
const logoFilename = `logo.${logoExt}`;
19-
const exists = fs.existsSync(path.join('public', logoFilename));
20-
21-
if (exists) {
22-
logo = `/${logoFilename}`;
23-
break;
24-
}
7+
interface Props {
8+
logoLink: string;
259
}
2610
27-
if (!logo) {
28-
console.warn(`No logo found in public/. Supported filenames are: logo.(${LOGO_EXTENSIONS.join('|')})`);
29-
}
11+
const { logoLink } = Astro.props;
3012
---
3113

3214
<nav
3315
class="bg-tk-elements-topBar-backgroundColor border-b border-tk-elements-app-borderColor flex max-w-full items-center p-3 px-4 min-h-[56px]"
3416
>
3517
<div class="flex flex-1">
36-
<a
37-
href="/"
38-
class="flex items-center text-tk-elements-topBar-logo-color hover:text-tk-elements-topBar-logo-colorHover"
39-
>
40-
{logo && <img class="h-5 w-auto" src={logo} />}
41-
</a>
18+
<Logo logoLink={logoLink} />
4219
</div>
4320
<div>
4421
<ThemeSwitch client:load transition:persist />
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
5+
const LOGO_EXTENSIONS = ['svg', 'png', 'jpeg', 'jpg'];
6+
7+
interface Props {
8+
logoLink: string;
9+
}
10+
11+
function readLogoFile(logoPrefix: string) {
12+
let logo;
13+
14+
for (const logoExt of LOGO_EXTENSIONS) {
15+
const logoFilename = `${logoPrefix}.${logoExt}`;
16+
const exists = fs.existsSync(path.join('public', logoFilename));
17+
18+
if (exists) {
19+
logo = `/${logoFilename}`;
20+
break;
21+
}
22+
}
23+
24+
return logo;
25+
}
26+
27+
const { logoLink } = Astro.props;
28+
29+
const logo = readLogoFile('logo');
30+
const logoDark = readLogoFile('logo-dark') ?? logo;
31+
32+
if (!logo) {
33+
console.warn(
34+
[
35+
`No logo found in public/. Supported filenames are: logo.(${LOGO_EXTENSIONS.join('|')}). `,
36+
`You can overwrite the logo for dark mode by providing a logo-dark.(${LOGO_EXTENSIONS.join('|')}).`,
37+
].join(''),
38+
);
39+
}
40+
---
41+
42+
<a
43+
href={logoLink}
44+
class="flex items-center text-tk-elements-topBar-logo-color hover:text-tk-elements-topBar-logo-colorHover"
45+
>
46+
{logo && <img class="h-5 w-auto dark:hidden" src={logo} />}
47+
{logo && <img class="h-5 w-auto hidden dark:inline-block" src={logoDark} />}
48+
</a>

packages/astro/src/default/components/ResizablePanel.astro

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ if (!hasSidePanel) {
5555
<slot name="a" />
5656
</div>
5757
{
58-
hasSidePanel &&
59-
(
58+
hasSidePanel && (
59+
<>
6060
<div class="panel">
6161
<slot name="b" />
6262
</div>
63-
<div class="divider"></div>
64-
)
63+
<div class="divider" />
64+
</>
65+
)
6566
}
6667
</div>
6768
</resizable-panel>

packages/astro/src/default/pages/[...slug].astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ export async function getStaticPaths() {
1818
1919
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
2020
21-
const { lesson, navList, title } = Astro.props as Props;
21+
const { lesson, logoLink, navList, title } = Astro.props as Props;
2222
2323
const showWorkspacePanel = hasWorkspace(lesson);
2424
---
2525

2626
<Layout title={title}>
2727
<div id="previews-container"></div>
2828
<main class="max-w-full flex flex-col h-screen overflow-hidden" data-swap-root>
29-
<Header />
29+
<Header logoLink={logoLink ?? '/'} />
3030
<ResizablePanel
3131
classList="h-full overflow-hidden"
3232
id={RESIZABLE_PANELS.Main}

packages/astro/src/default/pages/index.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getTutorial } from '../utils/content';
33
44
const tutorial = await getTutorial();
55
6-
const parts = Object.values(tutorial);
6+
const parts = Object.values(tutorial.parts);
77
const part = parts[0];
88
const chapter = part.chapters[1];
99
const lesson = chapter.lessons[1];

packages/astro/src/default/utils/content.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ const CONTENT_DIR = path.join(process.cwd(), 'src/content/tutorial');
1717
export async function getTutorial(): Promise<Tutorial> {
1818
const collection = sortCollection(await getCollection('tutorial'));
1919

20-
const _tutorial: Tutorial = {};
20+
const _tutorial: Tutorial = {
21+
parts: {},
22+
};
2123

2224
let tutorialMetaData: TutorialSchema | undefined;
2325

@@ -34,30 +36,32 @@ export async function getTutorial(): Promise<Tutorial> {
3436

3537
// default template if not specified
3638
tutorialMetaData.template ??= 'default';
39+
40+
_tutorial.logoLink = data.logoLink;
3741
} else if (type === 'part') {
38-
_tutorial[partId] = {
42+
_tutorial.parts[partId] = {
3943
id: partId,
4044
data,
4145
slug: getSlug(entry),
4246
chapters: {},
4347
};
4448
} else if (type === 'chapter') {
45-
if (!_tutorial[partId]) {
49+
if (!_tutorial.parts[partId]) {
4650
throw new Error(`Could not find part '${partId}'`);
4751
}
4852

49-
_tutorial[partId].chapters[chapterId] = {
53+
_tutorial.parts[partId].chapters[chapterId] = {
5054
id: chapterId,
5155
data,
5256
slug: getSlug(entry),
5357
lessons: {},
5458
};
5559
} else if (type === 'lesson') {
56-
if (!_tutorial[partId]) {
60+
if (!_tutorial.parts[partId]) {
5761
throw new Error(`Could not find part '${partId}'`);
5862
}
5963

60-
if (!_tutorial[partId].chapters[chapterId]) {
64+
if (!_tutorial.parts[partId].chapters[chapterId]) {
6165
throw new Error(`Could not find chapter '${partId}'`);
6266
}
6367

@@ -75,11 +79,11 @@ export async function getTutorial(): Promise<Tutorial> {
7579
id: lessonId,
7680
part: {
7781
id: partId,
78-
title: _tutorial[partId].data.title,
82+
title: _tutorial.parts[partId].data.title,
7983
},
8084
chapter: {
8185
id: chapterId,
82-
title: _tutorial[partId].chapters[chapterId].data.title,
86+
title: _tutorial.parts[partId].chapters[chapterId].data.title,
8387
},
8488
Markdown: Content,
8589
slug: getSlug(entry),
@@ -89,7 +93,7 @@ export async function getTutorial(): Promise<Tutorial> {
8993

9094
lessons.push(lesson);
9195

92-
_tutorial[partId].chapters[chapterId].lessons[lessonId] = lesson;
96+
_tutorial.parts[partId].chapters[chapterId].lessons[lessonId] = lesson;
9397
}
9498
}
9599

@@ -111,8 +115,8 @@ export async function getTutorial(): Promise<Tutorial> {
111115
const prevLesson = i > 0 ? lessons.at(i - 1) : undefined;
112116
const nextLesson = lessons.at(i + 1);
113117

114-
const partMetadata = _tutorial[lesson.part.id].data;
115-
const chapterMetadata = _tutorial[lesson.part.id].chapters[lesson.chapter.id].data;
118+
const partMetadata = _tutorial.parts[lesson.part.id].data;
119+
const chapterMetadata = _tutorial.parts[lesson.part.id].chapters[lesson.chapter.id].data;
116120

117121
lesson.data = {
118122
...pick(
@@ -123,8 +127,8 @@ export async function getTutorial(): Promise<Tutorial> {
123127
};
124128

125129
if (prevLesson) {
126-
const partSlug = _tutorial[prevLesson.part.id].slug;
127-
const chapterSlug = _tutorial[prevLesson.part.id].chapters[prevLesson.chapter.id].slug;
130+
const partSlug = _tutorial.parts[prevLesson.part.id].slug;
131+
const chapterSlug = _tutorial.parts[prevLesson.part.id].chapters[prevLesson.chapter.id].slug;
128132

129133
lesson.prev = {
130134
title: prevLesson.data.title,
@@ -133,8 +137,8 @@ export async function getTutorial(): Promise<Tutorial> {
133137
}
134138

135139
if (nextLesson) {
136-
const partSlug = _tutorial[nextLesson.part.id].slug;
137-
const chapterSlug = _tutorial[nextLesson.part.id].chapters[nextLesson.chapter.id].slug;
140+
const partSlug = _tutorial.parts[nextLesson.part.id].slug;
141+
const chapterSlug = _tutorial.parts[nextLesson.part.id].chapters[nextLesson.chapter.id].slug;
138142

139143
lesson.next = {
140144
title: nextLesson.data.title,

packages/astro/src/default/utils/nav.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Tutorial, NavList } from '@tutorialkit/types';
22

33
export function generateNavigationList(tutorial: Tutorial): NavList {
4-
return objectToSortedArray(tutorial).map((part) => {
4+
return objectToSortedArray(tutorial.parts).map((part) => {
55
return {
66
id: part.id,
77
title: part.data.title,

packages/astro/src/default/utils/routes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export async function generateStaticRoutes() {
99

1010
const routes = [];
1111

12-
const parts = Object.values(tutorial);
12+
const parts = Object.values(tutorial.parts);
1313

1414
for (const part of parts) {
1515
const chapters = Object.values(part.chapters);
@@ -23,6 +23,7 @@ export async function generateStaticRoutes() {
2323
slug: `/${part.slug}/${chapter.slug}/${lesson.slug}`,
2424
},
2525
props: {
26+
logoLink: tutorial.logoLink,
2627
navList: generateNavigationList(tutorial),
2728
title: `${part.data.title} / ${chapter.data.title} / ${lesson.data.title}`,
2829
lesson: lesson as Lesson<AstroComponentFactory>,

packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ exports[`create a project 1`] = `
2020
"package.json",
2121
"public",
2222
"public/favicon.svg",
23+
"public/logo-dark.svg",
2324
"public/logo.svg",
2425
"src",
2526
"src/content",
@@ -110,6 +111,7 @@ exports[`create and build a project 1`] = `
110111
"2-advanced/1-unicorn/1-welcome/index.html",
111112
"favicon.svg",
112113
"index.html",
114+
"logo-dark.svg",
113115
"logo.svg",
114116
"template-default.json",
115117
"template-vite-app.json",
Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)