@abreen/tada 1.7.1 → 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.1",
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/_base.scss CHANGED
@@ -99,6 +99,7 @@ code {
99
99
  pre {
100
100
  background: var(--bg2-color);
101
101
  border-radius: var(--mono-border-radius);
102
+ font-size: var(--mono-font-size);
102
103
  line-height: var(--mono-line-height);
103
104
  overflow-x: auto;
104
105
  overflow-y: hidden;
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
+ }
@@ -34,7 +34,6 @@ body.literate .literate-code-output {
34
34
  display: grid;
35
35
  grid-template-columns: 1fr;
36
36
  gap: var(--gap);
37
- font-size: var(--mono-font-size);
38
37
 
39
38
  > pre {
40
39
  margin-top: 0;
@@ -11,7 +11,12 @@ interface PagefindSubResult {
11
11
  }
12
12
 
13
13
  interface PagefindResult {
14
- meta?: { title?: string; page?: 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,11 +79,13 @@ 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,
80
86
  subResults,
81
87
  pageNumber: getPdfPageNumber(d.url, d.meta?.page),
88
+ template: d.meta?.template ?? null,
82
89
  };
83
90
  });
84
91
 
@@ -139,14 +146,16 @@ function render(
139
146
  const titleEl = doc.createElement('div');
140
147
  titleEl.id = `title-${i}`;
141
148
  titleEl.className = 'title';
142
- titleEl.textContent = result.title;
149
+ if (result.template === 'code') {
150
+ titleEl.classList.add('code-page');
151
+ }
152
+ if (result.titleHtml) {
153
+ titleEl.innerHTML = result.titleHtml;
154
+ } else {
155
+ titleEl.textContent = result.title;
156
+ }
143
157
  a.appendChild(titleEl);
144
158
 
145
- const subtitle = doc.createElement('div');
146
- subtitle.className = 'subtitle';
147
- subtitle.innerText = result.url;
148
- a.appendChild(subtitle);
149
-
150
159
  const excerpt = doc.createElement('div');
151
160
  excerpt.className = 'excerpt';
152
161
  excerpt.innerHTML = result.excerpt;
@@ -1,11 +1,13 @@
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;
7
8
  subResults: SubResult[];
8
9
  pageNumber: number | null;
10
+ template: string | null;
9
11
  };
10
12
 
11
13
  export function getPdfPageNumber(
@@ -66,11 +68,13 @@ export function groupPdfResults(results: Result[]): Result[] {
66
68
 
67
69
  grouped.push({
68
70
  title: primary.title,
71
+ titleHtml: null,
69
72
  url: baseUrl,
70
73
  excerpt: primary.excerpt,
71
74
  score: primary.score,
72
75
  subResults,
73
76
  pageNumber: null,
77
+ template: null,
74
78
  });
75
79
  });
76
80
 
@@ -129,13 +129,9 @@ header {
129
129
  font-weight: 500;
130
130
  }
131
131
 
132
- ol li .subtitle {
133
- font-family: var(--mono-font);
134
- font-size: var(--font-size-small);
135
- color: var(--fg2-color);
136
- white-space: nowrap;
137
- overflow: hidden;
138
- text-overflow: ellipsis;
132
+ ol a .title.code-page code {
133
+ background: none;
134
+ padding: 0;
139
135
  }
140
136
 
141
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
  }
@@ -54,7 +54,7 @@
54
54
  </style></noscript>
55
55
  </head>
56
56
 
57
- <body class="<%= page.template %><% if (page.tocHtml) { %> toc-is-active<% } %>">
57
+ <body class="<%= page.template %><% if (page.tocHtml) { %> toc-is-active<% } %>" data-pagefind-meta="template:<%= page.template %>">
58
58
  <header data-pagefind-ignore>
59
59
  <div class="header-controls">
60
60
  <div id="to-top-container" class="to-top-container"></div>
@@ -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">