@abreen/tada 1.13.4 → 1.13.7
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/build/bundle.ts +17 -2
- package/build/generate-content-assets.ts +1 -0
- package/build/types.d.ts +1 -0
- package/build/utils/derive-theme.ts +10 -0
- package/build/utils/render.ts +1 -0
- package/build/utils/trace-core.ts +75 -22
- package/build/utils/trace.ts +27 -10
- package/build/watch/assets.ts +2 -2
- package/build/watch/compiler-types.d.ts +1 -0
- package/package.json +1 -1
- package/src/_base.scss +6 -5
- package/src/slides/index.ts +34 -0
- package/src/style.scss +1 -1
- package/src/trace/index.ts +14 -7
- package/src/trace/style.scss +11 -10
- package/templates/_theme.scss +2 -0
- package/templates/default.html +1 -1
package/build/bundle.ts
CHANGED
|
@@ -9,10 +9,20 @@ import {
|
|
|
9
9
|
getDistDir,
|
|
10
10
|
toPosix,
|
|
11
11
|
} from './utils/paths';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
deriveTheme,
|
|
14
|
+
deriveLinkHue,
|
|
15
|
+
deriveTraceLineActiveHue,
|
|
16
|
+
} from './utils/derive-theme';
|
|
13
17
|
import type { PluginBuilder } from 'bun';
|
|
14
18
|
import type { SiteVariables } from './types';
|
|
15
19
|
import timezones from '../src/timezone/timezones.json' with { type: 'json' };
|
|
20
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
21
|
+
|
|
22
|
+
export function getBundleNaming(): string {
|
|
23
|
+
const version = pkg.version.replace(/[^a-zA-Z0-9.-]/g, '-');
|
|
24
|
+
return `[name].bundle.tada-${version}.[ext]`;
|
|
25
|
+
}
|
|
16
26
|
|
|
17
27
|
function formatCssNumber(value: number, precision = 4): string {
|
|
18
28
|
if (Number.isInteger(value)) {
|
|
@@ -40,6 +50,9 @@ function renderThemeScss(siteVariables: SiteVariables): string {
|
|
|
40
50
|
const linkColorHover = `hsl(${linkHue}deg 34% 60%)`;
|
|
41
51
|
const linkColorDark = `hsl(${linkHue}deg 50% 72%)`;
|
|
42
52
|
const linkColorHoverDark = `hsl(${linkHue}deg 40% 80%)`;
|
|
53
|
+
const traceLineActiveHue = formatCssNumber(deriveTraceLineActiveHue(tintHue));
|
|
54
|
+
const bgTraceLineActive = `hsl(${traceLineActiveHue}deg 100% 86%)`;
|
|
55
|
+
const bgTraceLineActiveDark = `hsl(${traceLineActiveHue}deg 90% 18%)`;
|
|
43
56
|
|
|
44
57
|
const rendered = _.template(template)({
|
|
45
58
|
...theme,
|
|
@@ -54,6 +67,8 @@ function renderThemeScss(siteVariables: SiteVariables): string {
|
|
|
54
67
|
linkColorHover,
|
|
55
68
|
linkColorDark,
|
|
56
69
|
linkColorHoverDark,
|
|
70
|
+
bgTraceLineActive,
|
|
71
|
+
bgTraceLineActiveDark,
|
|
57
72
|
});
|
|
58
73
|
|
|
59
74
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tada-'));
|
|
@@ -109,7 +124,7 @@ export async function bundle(
|
|
|
109
124
|
const result = await Bun.build({
|
|
110
125
|
entrypoints,
|
|
111
126
|
outdir: resolvedDistDir,
|
|
112
|
-
naming:
|
|
127
|
+
naming: getBundleNaming(),
|
|
113
128
|
minify: mode === 'production',
|
|
114
129
|
sourcemap: isDev ? 'inline' : 'none',
|
|
115
130
|
define: createDefine(siteVariables, isDev),
|
package/build/types.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ const DARK_TEXT_L_MAX = 0.82;
|
|
|
16
16
|
|
|
17
17
|
const LINK_ANCHOR_HUE = 212;
|
|
18
18
|
const LINK_TINT_BLEND = 0.05;
|
|
19
|
+
const TRACE_LINE_ACTIVE_ANCHOR_HUE = 52;
|
|
20
|
+
const TRACE_LINE_ACTIVE_TINT_BLEND = 0.1;
|
|
19
21
|
|
|
20
22
|
function clamp(v: number, min: number, max: number): number {
|
|
21
23
|
return Math.min(Math.max(v, min), max);
|
|
@@ -83,6 +85,14 @@ export function deriveLinkHue(tintHue: number): number {
|
|
|
83
85
|
return (LINK_ANCHOR_HUE + diff * LINK_TINT_BLEND + 360) % 360;
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
export function deriveTraceLineActiveHue(tintHue: number): number {
|
|
89
|
+
const diff = ((tintHue - TRACE_LINE_ACTIVE_ANCHOR_HUE + 540) % 360) - 180;
|
|
90
|
+
return (
|
|
91
|
+
(TRACE_LINE_ACTIVE_ANCHOR_HUE + diff * TRACE_LINE_ACTIVE_TINT_BLEND + 360) %
|
|
92
|
+
360
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
export function getTextOnColor(cssColor: string): '#fff' | '#000' {
|
|
87
97
|
const parsed = parse(cssColor);
|
|
88
98
|
if (!parsed) {
|
package/build/utils/render.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
3
4
|
import { renderCodeSegment } from './code';
|
|
4
5
|
import { splitLines } from './literate-java';
|
|
5
6
|
import { normalizeOutputPath } from './paths';
|
|
@@ -14,18 +15,46 @@ export interface ChunkTraceOutputOptions {
|
|
|
14
15
|
ignoreFields?: Record<string, string[]>;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
export interface ChunkTraceOutputResult {
|
|
19
|
+
manifest: TraceManifest;
|
|
20
|
+
artifactId: string;
|
|
21
|
+
outputPaths: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hashTraceFiles(files: { name: string; content: string }[]): string {
|
|
25
|
+
const hasher = createHash('sha256');
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
hasher.update(file.name, 'utf8');
|
|
28
|
+
hasher.update('\0', 'utf8');
|
|
29
|
+
hasher.update(file.content, 'utf8');
|
|
30
|
+
hasher.update('\0', 'utf8');
|
|
31
|
+
}
|
|
32
|
+
return `sha256-${hasher.digest('hex').slice(0, 16)}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildTraceOutputPath(
|
|
36
|
+
relDir: string,
|
|
37
|
+
traceName: string,
|
|
38
|
+
artifactId: string,
|
|
39
|
+
fileName: string,
|
|
40
|
+
): string {
|
|
41
|
+
return [relDir, '_traces', traceName, artifactId, fileName]
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join('/');
|
|
44
|
+
}
|
|
45
|
+
|
|
17
46
|
export function chunkTraceOutput(
|
|
18
47
|
output: string,
|
|
19
48
|
traceOutputDir: string,
|
|
49
|
+
relDir: string,
|
|
50
|
+
traceName: string,
|
|
20
51
|
sourceFile: string,
|
|
21
52
|
source: string,
|
|
22
53
|
options: ChunkTraceOutputOptions = {},
|
|
23
|
-
):
|
|
54
|
+
): ChunkTraceOutputResult {
|
|
24
55
|
const chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;
|
|
25
56
|
const ignoreFields = options.ignoreFields ?? {};
|
|
26
57
|
|
|
27
|
-
fs.mkdirSync(traceOutputDir, { recursive: true });
|
|
28
|
-
|
|
29
58
|
const lines = output
|
|
30
59
|
.trim()
|
|
31
60
|
.split('\n')
|
|
@@ -46,26 +75,27 @@ export function chunkTraceOutput(
|
|
|
46
75
|
|
|
47
76
|
let chunkEntries: TraceChunkEntry[] = [];
|
|
48
77
|
let chunkIndex = 0;
|
|
78
|
+
const chunkFiles: { name: string; content: string }[] = [];
|
|
49
79
|
|
|
50
80
|
for (const step of allSteps) {
|
|
51
81
|
const svg = generateStepSvg(step, layout);
|
|
52
82
|
chunkEntries.push({ line: step.line, stdout: step.stdout, svg });
|
|
53
83
|
|
|
54
84
|
if (chunkEntries.length >= chunkSize) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
JSON.stringify(chunkEntries),
|
|
58
|
-
);
|
|
85
|
+
chunkFiles.push({
|
|
86
|
+
name: `chunk-${chunkIndex}.json`,
|
|
87
|
+
content: JSON.stringify(chunkEntries),
|
|
88
|
+
});
|
|
59
89
|
chunkEntries = [];
|
|
60
90
|
chunkIndex++;
|
|
61
91
|
}
|
|
62
92
|
}
|
|
63
93
|
|
|
64
94
|
if (chunkEntries.length > 0) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
JSON.stringify(chunkEntries),
|
|
68
|
-
);
|
|
95
|
+
chunkFiles.push({
|
|
96
|
+
name: `chunk-${chunkIndex}.json`,
|
|
97
|
+
content: JSON.stringify(chunkEntries),
|
|
98
|
+
});
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
const manifest: TraceManifest = {
|
|
@@ -76,12 +106,26 @@ export function chunkTraceOutput(
|
|
|
76
106
|
lineToSteps,
|
|
77
107
|
};
|
|
78
108
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
JSON.stringify(manifest),
|
|
82
|
-
|
|
109
|
+
const manifestFile = {
|
|
110
|
+
name: 'manifest.json',
|
|
111
|
+
content: JSON.stringify(manifest),
|
|
112
|
+
};
|
|
113
|
+
const files = [manifestFile, ...chunkFiles];
|
|
114
|
+
const artifactId = hashTraceFiles(files);
|
|
115
|
+
const artifactDir = path.join(traceOutputDir, artifactId);
|
|
83
116
|
|
|
84
|
-
|
|
117
|
+
fs.mkdirSync(artifactDir, { recursive: true });
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
fs.writeFileSync(path.join(artifactDir, file.name), file.content);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
manifest,
|
|
124
|
+
artifactId,
|
|
125
|
+
outputPaths: files.map(file =>
|
|
126
|
+
buildTraceOutputPath(relDir, traceName, artifactId, file.name),
|
|
127
|
+
),
|
|
128
|
+
};
|
|
85
129
|
}
|
|
86
130
|
|
|
87
131
|
export function highlightTraceSource(
|
|
@@ -96,16 +140,25 @@ export function highlightTraceSource(
|
|
|
96
140
|
export function getTraceOutputPaths(
|
|
97
141
|
relDir: string,
|
|
98
142
|
traceName: string,
|
|
143
|
+
artifactId: string,
|
|
99
144
|
totalSteps: number,
|
|
100
145
|
): string[] {
|
|
101
|
-
const
|
|
102
|
-
|
|
146
|
+
const outputPaths = [
|
|
147
|
+
buildTraceOutputPath(relDir, traceName, artifactId, 'manifest.json'),
|
|
148
|
+
];
|
|
103
149
|
for (
|
|
104
150
|
let chunkIndex = 0;
|
|
105
151
|
chunkIndex * DEFAULT_CHUNK_SIZE < totalSteps;
|
|
106
152
|
chunkIndex++
|
|
107
153
|
) {
|
|
108
|
-
outputPaths.push(
|
|
154
|
+
outputPaths.push(
|
|
155
|
+
buildTraceOutputPath(
|
|
156
|
+
relDir,
|
|
157
|
+
traceName,
|
|
158
|
+
artifactId,
|
|
159
|
+
`chunk-${chunkIndex}.json`,
|
|
160
|
+
),
|
|
161
|
+
);
|
|
109
162
|
}
|
|
110
163
|
return outputPaths;
|
|
111
164
|
}
|
|
@@ -148,18 +201,18 @@ export function materializeTraceOutputs({
|
|
|
148
201
|
export function buildManifestUrl({
|
|
149
202
|
relDir,
|
|
150
203
|
traceName,
|
|
204
|
+
artifactId,
|
|
151
205
|
applyBasePath,
|
|
152
206
|
}: {
|
|
153
207
|
relDir: string;
|
|
154
208
|
traceName: string;
|
|
209
|
+
artifactId: string;
|
|
155
210
|
applyBasePath: (subPath: string) => string;
|
|
156
211
|
}): string {
|
|
157
212
|
return applyBasePath(
|
|
158
213
|
normalizeOutputPath(
|
|
159
214
|
'/' +
|
|
160
|
-
|
|
161
|
-
.filter(Boolean)
|
|
162
|
-
.join('/'),
|
|
215
|
+
buildTraceOutputPath(relDir, traceName, artifactId, 'manifest.json'),
|
|
163
216
|
),
|
|
164
217
|
);
|
|
165
218
|
}
|
package/build/utils/trace.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface TraceContext {
|
|
|
42
42
|
|
|
43
43
|
export interface TraceResult {
|
|
44
44
|
manifestUrl: string;
|
|
45
|
+
artifactId: string;
|
|
45
46
|
highlightedSource: string;
|
|
46
47
|
totalSteps: number;
|
|
47
48
|
mtime: number;
|
|
@@ -141,6 +142,7 @@ export function createTraceHelpers(context: TraceContext): {
|
|
|
141
142
|
const outputPaths = getTraceOutputPaths(
|
|
142
143
|
relDir,
|
|
143
144
|
traceName,
|
|
145
|
+
cached.artifactId,
|
|
144
146
|
cached.totalSteps,
|
|
145
147
|
);
|
|
146
148
|
if (
|
|
@@ -155,7 +157,12 @@ export function createTraceHelpers(context: TraceContext): {
|
|
|
155
157
|
}
|
|
156
158
|
return {
|
|
157
159
|
...cached,
|
|
158
|
-
manifestUrl: buildManifestUrl({
|
|
160
|
+
manifestUrl: buildManifestUrl({
|
|
161
|
+
relDir,
|
|
162
|
+
traceName,
|
|
163
|
+
artifactId: cached.artifactId,
|
|
164
|
+
applyBasePath,
|
|
165
|
+
}),
|
|
159
166
|
};
|
|
160
167
|
}
|
|
161
168
|
}
|
|
@@ -176,32 +183,42 @@ export function createTraceHelpers(context: TraceContext): {
|
|
|
176
183
|
);
|
|
177
184
|
const traceOutputDir = path.join(distDir, relDir, '_traces', traceName);
|
|
178
185
|
|
|
179
|
-
const
|
|
186
|
+
const traceOutput = chunkTraceOutput(
|
|
180
187
|
output,
|
|
181
188
|
traceOutputDir,
|
|
189
|
+
relDir,
|
|
190
|
+
traceName,
|
|
182
191
|
sourceFileNameForTrace(sourceFile),
|
|
183
192
|
source,
|
|
184
193
|
{ chunkSize: DEFAULT_CHUNK_SIZE, ignoreFields },
|
|
185
194
|
);
|
|
186
|
-
for (const outputPath of
|
|
187
|
-
relDir,
|
|
188
|
-
traceName,
|
|
189
|
-
manifest.totalSteps,
|
|
190
|
-
)) {
|
|
195
|
+
for (const outputPath of traceOutput.outputPaths) {
|
|
191
196
|
dependencyCollector?.generatedOutputPaths?.add(outputPath);
|
|
192
197
|
}
|
|
193
198
|
|
|
194
|
-
const manifestUrl = buildManifestUrl({
|
|
199
|
+
const manifestUrl = buildManifestUrl({
|
|
200
|
+
relDir,
|
|
201
|
+
traceName,
|
|
202
|
+
artifactId: traceOutput.artifactId,
|
|
203
|
+
applyBasePath,
|
|
204
|
+
});
|
|
195
205
|
|
|
196
|
-
const totalSteps = manifest.totalSteps;
|
|
206
|
+
const totalSteps = traceOutput.manifest.totalSteps;
|
|
197
207
|
const mtime = fs.statSync(sourceFilePath).mtimeMs;
|
|
198
208
|
cache.set(sourceFilePath, {
|
|
199
209
|
manifestUrl,
|
|
210
|
+
artifactId: traceOutput.artifactId,
|
|
200
211
|
highlightedSource,
|
|
201
212
|
totalSteps,
|
|
202
213
|
mtime,
|
|
203
214
|
});
|
|
204
|
-
return {
|
|
215
|
+
return {
|
|
216
|
+
manifestUrl,
|
|
217
|
+
artifactId: traceOutput.artifactId,
|
|
218
|
+
highlightedSource,
|
|
219
|
+
totalSteps,
|
|
220
|
+
mtime,
|
|
221
|
+
};
|
|
205
222
|
}
|
|
206
223
|
|
|
207
224
|
return {
|
package/build/watch/assets.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { bundle } from '../bundle';
|
|
3
|
+
import { bundle, getBundleNaming } from '../bundle';
|
|
4
4
|
import { copyFonts } from '../generate-fonts';
|
|
5
5
|
import { copyKatexAssets } from '../generate-katex-assets';
|
|
6
6
|
import { generateFavicons } from '../generate-favicon';
|
|
@@ -42,7 +42,7 @@ export async function bundleWatchAssets(
|
|
|
42
42
|
const reloadAssets = await Bun.build({
|
|
43
43
|
entrypoints: [RELOAD_CLIENT_PATH],
|
|
44
44
|
outdir: outputDir,
|
|
45
|
-
naming:
|
|
45
|
+
naming: getBundleNaming(),
|
|
46
46
|
sourcemap: 'inline',
|
|
47
47
|
});
|
|
48
48
|
return [
|
package/package.json
CHANGED
package/src/_base.scss
CHANGED
|
@@ -38,12 +38,13 @@ body {
|
|
|
38
38
|
* Enable Inter's features:
|
|
39
39
|
* liga - standard ligatures
|
|
40
40
|
* calt - contextual alternates
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* ss07 - square punctuation
|
|
42
|
+
* ss08 - square quotes
|
|
43
|
+
* cv01 - alternate 1
|
|
44
|
+
* cv08 - uppercase I with serif
|
|
45
|
+
* cv10 - capital G with spur
|
|
45
46
|
*/
|
|
46
|
-
font-feature-settings: 'liga', 'calt', '
|
|
47
|
+
font-feature-settings: 'liga', 'calt', 'ss07', 'ss08', 'cv01', 'cv08', 'cv10';
|
|
47
48
|
line-height: var(--line-height);
|
|
48
49
|
color: var(--fg-color);
|
|
49
50
|
background-color: var(--bg-color);
|
package/src/slides/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface SlidesPresentDetail {
|
|
|
9
9
|
slideIndex?: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
const FULLSCREEN_STORAGE_KEY = 'slidesFullscreen';
|
|
13
|
+
|
|
12
14
|
function isInteractive(
|
|
13
15
|
view: Window & typeof globalThis,
|
|
14
16
|
target: EventTarget | null,
|
|
@@ -83,6 +85,16 @@ export default function mountSlides(window: Window): void | (() => void) {
|
|
|
83
85
|
presentButton.disabled = false;
|
|
84
86
|
}
|
|
85
87
|
if (fullscreenCheckbox) {
|
|
88
|
+
try {
|
|
89
|
+
const stored = window.localStorage.getItem(FULLSCREEN_STORAGE_KEY);
|
|
90
|
+
if (stored === 'false') {
|
|
91
|
+
fullscreenCheckbox.checked = false;
|
|
92
|
+
} else if (stored === 'true') {
|
|
93
|
+
fullscreenCheckbox.checked = true;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// ignored
|
|
97
|
+
}
|
|
86
98
|
fullscreenCheckbox.disabled = false;
|
|
87
99
|
}
|
|
88
100
|
|
|
@@ -365,6 +377,20 @@ export default function mountSlides(window: Window): void | (() => void) {
|
|
|
365
377
|
exitPresentation();
|
|
366
378
|
}
|
|
367
379
|
|
|
380
|
+
function handleFullscreenPreferenceChange(): void {
|
|
381
|
+
if (!fullscreenCheckbox) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
window.localStorage.setItem(
|
|
386
|
+
FULLSCREEN_STORAGE_KEY,
|
|
387
|
+
String(fullscreenCheckbox.checked),
|
|
388
|
+
);
|
|
389
|
+
} catch {
|
|
390
|
+
// Ignore storage errors so the checkbox remains usable.
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
368
394
|
function handleFullscreenChange(): void {
|
|
369
395
|
if (
|
|
370
396
|
isPresenting &&
|
|
@@ -460,6 +486,10 @@ export default function mountSlides(window: Window): void | (() => void) {
|
|
|
460
486
|
}
|
|
461
487
|
|
|
462
488
|
presentButton?.addEventListener('click', handlePresentClick);
|
|
489
|
+
fullscreenCheckbox?.addEventListener(
|
|
490
|
+
'change',
|
|
491
|
+
handleFullscreenPreferenceChange,
|
|
492
|
+
);
|
|
463
493
|
closeButton.addEventListener('click', handleCloseClick);
|
|
464
494
|
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
|
465
495
|
window.addEventListener('keydown', handleKeydown);
|
|
@@ -470,6 +500,10 @@ export default function mountSlides(window: Window): void | (() => void) {
|
|
|
470
500
|
return () => {
|
|
471
501
|
exitPresentation();
|
|
472
502
|
presentButton?.removeEventListener('click', handlePresentClick);
|
|
503
|
+
fullscreenCheckbox?.removeEventListener(
|
|
504
|
+
'change',
|
|
505
|
+
handleFullscreenPreferenceChange,
|
|
506
|
+
);
|
|
473
507
|
closeButton.removeEventListener('click', handleCloseClick);
|
|
474
508
|
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
|
475
509
|
window.removeEventListener('keydown', handleKeydown);
|
package/src/style.scss
CHANGED
package/src/trace/index.ts
CHANGED
|
@@ -58,13 +58,20 @@ async function goToStep(
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function updateSourceHighlight(panel: HTMLElement, currentLine: number): void {
|
|
61
|
-
panel
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
panel
|
|
62
|
+
.querySelectorAll(
|
|
63
|
+
'.code-row.trace-line-active, .line-number.trace-line-active',
|
|
64
|
+
)
|
|
65
|
+
.forEach(el => {
|
|
66
|
+
el.classList.remove('trace-line-active');
|
|
67
|
+
});
|
|
68
|
+
const lineNumber = panel.querySelector(
|
|
69
|
+
`.line-number[data-line="${currentLine}"]`,
|
|
70
|
+
);
|
|
71
|
+
const row = lineNumber?.closest('.code-row') as HTMLElement | null;
|
|
72
|
+
if (row) {
|
|
73
|
+
row.classList.add('trace-line-active');
|
|
74
|
+
const elRect = row.getBoundingClientRect();
|
|
68
75
|
const panelRect = panel.getBoundingClientRect();
|
|
69
76
|
panel.scrollTop += elRect.top - panelRect.top - panel.clientHeight / 2;
|
|
70
77
|
}
|
package/src/trace/style.scss
CHANGED
|
@@ -112,16 +112,17 @@
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
115
|
+
.code-row.trace-line-active {
|
|
116
|
+
background: var(--bg-trace-line-active);
|
|
117
|
+
|
|
118
|
+
.line-number {
|
|
119
|
+
font-weight: bold;
|
|
120
|
+
color: var(--fg-color);
|
|
121
|
+
background: var(--bg-trace-line-active);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
code {
|
|
125
|
+
background: none;
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
}
|
package/templates/_theme.scss
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
--bg2-color-translucent: hsl(
|
|
33
33
|
var(--tint-hue) calc(12% * var(--tint-amount)) 92% / 66%
|
|
34
34
|
);
|
|
35
|
+
--bg-trace-line-active: <%= bgTraceLineActive %>;
|
|
35
36
|
--box-shadow: 0 2px 9px
|
|
36
37
|
hsl(var(--tint-hue) calc(85% * var(--tint-amount)) 10% / 25%);
|
|
37
38
|
--backdrop-filter: blur(10px);
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
--bg2-color-translucent: hsl(
|
|
88
89
|
var(--tint-hue) calc(12% * var(--tint-amount)) 12% / 66%
|
|
89
90
|
);
|
|
91
|
+
--bg-trace-line-active: <%= bgTraceLineActiveDark %>;
|
|
90
92
|
--box-shadow: 0 3px 8px
|
|
91
93
|
hsl(var(--tint-hue) calc(33% * var(--tint-amount)) 8% / 70%);
|
|
92
94
|
--link-active-color: #fff;
|
package/templates/default.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<div class="slides-header" data-pagefind-ignore>
|
|
8
8
|
<button type="button" data-slides-present disabled>Present</button>
|
|
9
9
|
<label>
|
|
10
|
-
<input type="checkbox" data-slides-fullscreen checked disabled>
|
|
10
|
+
<input id="slides-fullscreen" type="checkbox" data-slides-fullscreen checked disabled>
|
|
11
11
|
Full screen
|
|
12
12
|
</label>
|
|
13
13
|
</div>
|