@abreen/tada 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -232,6 +232,7 @@ Example site configuration JSON file:
232
232
  | `features.search` | Enable search UI and Pagefind index generation |
233
233
  | `features.code` | Enable generated source-code HTML pages for configured code extensions |
234
234
  | `features.favicon` | Enable automatically generated favicons |
235
+ | `features.footer` | Show the Tada footer at the bottom of every page |
235
236
  | `base` | Full base URL of the deployed site, used for metadata and URL generation |
236
237
  | `basePath` | URL prefix for deployment under a subpath (e.g., `"/cs101"`), use `"/"` at root |
237
238
  | `internalDomains` | Domain names treated as internal by link processing (not marked external) |
package/bin/tada.ts CHANGED
@@ -332,7 +332,7 @@ async function initCommand(args: string[]): Promise<void> {
332
332
  base: 'http://localhost:8080',
333
333
  basePath: '/',
334
334
  internalDomains: ['localhost'],
335
- features: { search: true, code: true, favicon: false },
335
+ features: { search: true, code: true, favicon: false, footer: true },
336
336
  });
337
337
 
338
338
  const prodConfig = createSiteConfig({
@@ -345,7 +345,7 @@ async function initCommand(args: string[]): Promise<void> {
345
345
  base: prodBase,
346
346
  basePath: prodBasePath,
347
347
  internalDomains: [prodDomain],
348
- features: { search: true, code: true, favicon: true },
348
+ features: { search: true, code: true, favicon: true, footer: true },
349
349
  });
350
350
 
351
351
  fs.writeFileSync(
@@ -10,7 +10,7 @@ const configDir = getProjectDir();
10
10
 
11
11
  const DEFAULT: Partial<SiteVariables> = {
12
12
  basePath: '/',
13
- features: { search: true, code: true, favicon: true },
13
+ features: { search: true, code: true, favicon: true, footer: true },
14
14
  };
15
15
 
16
16
  const isValid = compileJsonSchema(siteSchema);
package/build/types.d.ts CHANGED
@@ -22,6 +22,7 @@ export type FeatureConfig = {
22
22
  search: boolean;
23
23
  code: boolean;
24
24
  favicon: boolean;
25
+ footer: boolean;
25
26
  };
26
27
 
27
28
  /** A rendered content asset ready to write to dist/ */
@@ -24,6 +24,7 @@ import {
24
24
  toPosix,
25
25
  toUrlPath,
26
26
  } from './paths';
27
+ import pkg from '../../package.json' with { type: 'json' };
27
28
  import { parseFrontMatterAndContent } from './front-matter';
28
29
  import { createMarkdown } from './markdown';
29
30
  import { generateTocHtml, generateCodeTocHtml } from '../toc-plugin';
@@ -45,6 +46,8 @@ import type {
45
46
 
46
47
  const log = makeLogger(import.meta.url);
47
48
 
49
+ const tadaVersion: string = pkg.version;
50
+
48
51
  const REQUIRED_FRONT_MATTER_FIELDS = ['title'];
49
52
 
50
53
  function isWatchMode(assetFiles: string[]): boolean {
@@ -133,6 +136,7 @@ function createTemplateParameters({
133
136
  content,
134
137
  applyBasePath,
135
138
  isWatchMode,
139
+ tadaVersion,
136
140
  };
137
141
  }
138
142
 
@@ -291,7 +295,7 @@ export function renderCodePageAsset({
291
295
  const codeFilePath = applyBasePath(
292
296
  normalizeOutputPath(`/${toUrlPath(path.relative(contentDir, filePath))}`),
293
297
  );
294
- const titleHtml = `<tt>${name + ext}</tt>`;
298
+ const titleHtml = `<code>${name + ext}</code>`;
295
299
  const tocItems = lang === 'java' ? extractJavaMethodToc(sourceCode) : [];
296
300
  const tocHtml = generateCodeTocHtml(tocItems);
297
301
  const pageVariables: Record<string, unknown> = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -4,11 +4,17 @@
4
4
  "properties": {
5
5
  "features": {
6
6
  "type": "object",
7
- "default": { "search": true, "code": true, "favicon": true },
7
+ "default": {
8
+ "search": true,
9
+ "code": true,
10
+ "favicon": true,
11
+ "footer": true
12
+ },
8
13
  "properties": {
9
14
  "search": { "type": "boolean", "default": true },
10
15
  "code": { "type": "boolean", "default": true },
11
- "favicon": { "type": "boolean", "default": true }
16
+ "favicon": { "type": "boolean", "default": true },
17
+ "footer": { "type": "boolean", "default": true }
12
18
  },
13
19
  "additionalProperties": false
14
20
  },
package/src/_layout.scss CHANGED
@@ -198,3 +198,35 @@ body.literate main table {
198
198
  grid-template-columns: 1fr 1fr;
199
199
  gap: var(--gap);
200
200
  }
201
+
202
+ footer {
203
+ @include user-select(none);
204
+ text-align: center;
205
+ padding: 2rem 0 1rem;
206
+ color: var(--fg2-color);
207
+
208
+ a {
209
+ color: var(--fg2-color);
210
+ text-decoration: none;
211
+
212
+ @media (hover: hover) {
213
+ &:hover {
214
+ color: var(--link-color-hover);
215
+ }
216
+ }
217
+
218
+ &:active {
219
+ color: var(--link-active-color);
220
+ }
221
+
222
+ &::after {
223
+ content: '';
224
+ display: inline-block;
225
+ width: 1.3em;
226
+ height: 1.3em;
227
+ vertical-align: middle;
228
+ margin-left: 0.2em;
229
+ @include contained-mask(var(--icon-tada));
230
+ }
231
+ }
232
+ }
@@ -11,7 +11,12 @@ interface PagefindSubResult {
11
11
  }
12
12
 
13
13
  interface PagefindResult {
14
- meta?: { title?: string; page?: string; template?: string };
14
+ meta?: {
15
+ title?: string;
16
+ title_html?: string;
17
+ page?: string;
18
+ template?: string;
19
+ };
15
20
  url: string;
16
21
  excerpt?: string;
17
22
  score: number;
@@ -74,6 +79,7 @@ async function doSearch(state: State) {
74
79
  }));
75
80
  return {
76
81
  title,
82
+ titleHtml: d.meta?.title_html ?? null,
77
83
  url: d.url,
78
84
  excerpt: d.excerpt ?? '',
79
85
  score: d.score ?? 0,
@@ -141,9 +147,10 @@ function render(
141
147
  titleEl.id = `title-${i}`;
142
148
  titleEl.className = 'title';
143
149
  if (result.template === 'code') {
144
- const code = doc.createElement('code');
145
- code.textContent = result.title;
146
- titleEl.appendChild(code);
150
+ titleEl.classList.add('code-page');
151
+ }
152
+ if (result.titleHtml) {
153
+ titleEl.innerHTML = result.titleHtml;
147
154
  } else {
148
155
  titleEl.textContent = result.title;
149
156
  }
@@ -1,6 +1,7 @@
1
1
  export type SubResult = { title: string; url: string; excerpt: string };
2
2
  export type Result = {
3
3
  title: string;
4
+ titleHtml: string | null;
4
5
  url: string;
5
6
  excerpt: string;
6
7
  score: number;
@@ -67,6 +68,7 @@ export function groupPdfResults(results: Result[]): Result[] {
67
68
 
68
69
  grouped.push({
69
70
  title: primary.title,
71
+ titleHtml: null,
70
72
  url: baseUrl,
71
73
  excerpt: primary.excerpt,
72
74
  score: primary.score,
@@ -127,11 +127,11 @@ header {
127
127
 
128
128
  ol a .title {
129
129
  font-weight: 500;
130
+ }
130
131
 
131
- code {
132
- background: none;
133
- padding: 0;
134
- }
132
+ ol a .title.code-page code {
133
+ background: none;
134
+ padding: 0;
135
135
  }
136
136
 
137
137
  ol li .excerpt {
@@ -1,3 +1,8 @@
1
+ <% if (site.features.footer) { %>
2
+ <footer>
3
+ <p>Generated by <a href="https://github.com/abreen/Tada" target="_blank">Tada</a> <%= tadaVersion %></p>
4
+ </footer>
5
+ <% } %>
1
6
  </div>
2
7
  </body>
3
8
  </html>
@@ -2,7 +2,7 @@
2
2
  <% if (page.parent) { %>
3
3
  <a class="breadcrumb" href="<%= applyBasePath(page.parent) %>" data-pagefind-ignore><%= page.parentLabel %></a>
4
4
  <% } %>
5
- <h1 data-pagefind-weight="10"><%= page.titleHtml %></h1>
5
+ <h1 data-pagefind-weight="10" data-pagefind-meta="title_html[data-title-html]" data-title-html="<%- page.titleHtml %>"><%= page.titleHtml %></h1>
6
6
 
7
7
  <div class="info" data-pagefind-ignore>
8
8
  <% if (page.published) { %>
@@ -67,6 +67,7 @@
67
67
  --icon-search: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><circle cx='11' cy='11' r='7' stroke='<%= iconColor %>' stroke-width='2' fill='none'/><line x1='16.5' y1='16.5' x2='22' y2='22' stroke='<%= iconColor %>' stroke-width='2' stroke-linecap='round'/></svg>");
68
68
  --icon-chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><polyline points='9 18 15 12 9 6' stroke='<%= iconColor %>' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
69
69
  --icon-breadcrumb: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><path d='M3 7 H17 V22' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/><polyline points='11 16 17 22 23 16' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/></svg>");
70
+ --icon-tada: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='39' height='39' viewBox='0 0 24 24'><path fill='black' d='m14.53 1.45l-1.08 1.08l1.6 1.6q.33.375.33.87c0 .495-.11.64-.33.86L11.5 9.47l1 1.08l3.63-3.61c.53-.59.79-1.24.79-1.94s-.26-1.36-.79-1.95zm-3.98 2.02L9.47 4.55l.61.56c.22.22.33.52.33.89s-.11.67-.33.89l-.61.56l1.08 1.08l.56-.61c.53-.59.8-1.23.8-1.92c0-.72-.27-1.37-.8-1.97zM21 5.06c-.69 0-1.33.27-1.92.8l-5.63 5.64l1.08 1l5.58-5.56c.25-.25.55-.38.89-.38s.64.13.89.38l.61.61l1.03-1.08l-.56-.61c-.59-.53-1.25-.8-1.97-.8M7 8L2 22l14-5zm12 3.06c-.7 0-1.34.27-1.94.8l-1.59 1.59l1.08 1.08l1.59-1.59c.25-.25.53-.38.86-.38s.63.13.88.38l1.62 1.59l1.05-1.03l-1.6-1.64c-.59-.53-1.25-.8-1.95-.8'/></svg>");
70
71
 
71
72
  color-scheme: light dark;
72
73
  }
@@ -3,7 +3,7 @@
3
3
  <div class="main-content">
4
4
  <main class="body" data-pagefind-body>
5
5
  <div class="file-header">
6
- <h1 data-pagefind-weight="10"><%= page.titleHtml %></h1>
6
+ <h1 data-pagefind-weight="10" data-pagefind-meta="title_html[data-title-html]" data-title-html="<%- page.titleHtml %>"><%= page.titleHtml %></h1>
7
7
  <%= render('_download.html') %>
8
8
  </div>
9
9
  <div class="code-body">