Skip to content

Commit 7bce74f

Browse files
committed
web: add speechbubble post-processor
1 parent 568083d commit 7bce74f

4 files changed

Lines changed: 98 additions & 4 deletions

File tree

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
},
1212
"dependencies": {
1313
"@fontsource/jetbrains-mono": "^5.0.2",
14-
"@fontsource/roboto-slab": "^5.0.2"
14+
"@fontsource/roboto-slab": "^5.0.2",
15+
"@nzbr/parcel-transformer-postprocess-html": "github:nzbr/parcel-transformer-postprocess-html",
16+
"@types/node": "^20.10.6",
17+
"typescript": "^5.3.3",
18+
"base16-builder": "^1.3.0"
1519
},
1620
"peerDependencies": {
1721
"sharp": "^0.31.1"
18-
},
19-
"devDependencies": {
20-
"base16-builder": "^1.3.0"
2122
}
2223
}

web/html-postprocessors/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.js
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs';
3+
import type { StepInputs } from '@nzbr/parcel-transformer-postprocess-html/src';
4+
import { toPosixPath } from '@nzbr/parcel-transformer-postprocess-html/src';
5+
6+
export function generateSpeechbubbles({ $, asset, options }: StepInputs): void {
7+
const root = toPosixPath(options.projectRoot);
8+
const stickers = path.posix.join(root, 'src', 'stickers');
9+
10+
asset.invalidateOnFileCreate({
11+
glob: path.posix.join(stickers, '*'),
12+
});
13+
14+
fs.readdirSync(stickers).forEach((character) => {
15+
const characterDir = path.posix.join(stickers, character);
16+
const cssClass = character.toLowerCase().trim().replaceAll(' ', '-');
17+
$(`.${cssClass}`).each((_, el) => {
18+
asset.invalidateOnFileCreate({
19+
glob: path.posix.join(characterDir, '*'),
20+
});
21+
22+
const stickers = fs
23+
.readdirSync(characterDir)
24+
.filter((file) => !file.endsWith('.txt'))
25+
.map((file) => {
26+
const stickerName = file.split('.').slice(0, -1).join('.');
27+
28+
return {
29+
stickerName,
30+
stickerFile: path.posix.join(characterDir, file),
31+
stickerAltFile: path.posix.join(characterDir, stickerName + '.txt'),
32+
stickerCssClass: stickerName.toLowerCase().trim().replaceAll(' ', '-'),
33+
};
34+
});
35+
36+
const defaultSticker = stickers.find(
37+
(sticker) => sticker.stickerName === character,
38+
);
39+
const variants = stickers.filter((sticker) => sticker.stickerName !== character);
40+
41+
const sticker = variants
42+
.concat(defaultSticker ? [defaultSticker] : [])
43+
.find((sticker) => $(el).hasClass(sticker.stickerCssClass));
44+
45+
if (!sticker) {
46+
asset.invalidateOnFileCreate({
47+
glob: path.posix.join(characterDir, `${character}.*`),
48+
});
49+
throw new Error(`No sticker found for ${character} in ${asset.filePath}`);
50+
}
51+
52+
if (!fs.existsSync(sticker.stickerAltFile)) {
53+
asset.invalidateOnFileCreate({
54+
glob: path.posix.join(characterDir, `${sticker.stickerName}.txt`),
55+
});
56+
throw new Error(
57+
`No alt text found for ${sticker.stickerName} in ${asset.filePath}`,
58+
);
59+
}
60+
61+
asset.invalidateOnFileChange(sticker.stickerAltFile);
62+
const alt = fs.readFileSync(sticker.stickerAltFile, 'utf-8').trim();
63+
64+
const url = asset.addURLDependency(
65+
`/${path.posix.relative(root, sticker.stickerFile)}`,
66+
{},
67+
);
68+
69+
$(el).addClass('speechbubble');
70+
$(el).html(`
71+
<img src="${url}" title=${character} alt="${alt}" width='128' />
72+
<div class="text">
73+
${$(el).html() ?? ''}
74+
</div>
75+
`);
76+
// width=128 is a fallback for the reader mode, because it ignores the CSS
77+
});
78+
});
79+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"include": [
3+
"*.ts"
4+
],
5+
"exclude": [],
6+
"compilerOptions": {
7+
"module": "CommonJS",
8+
"outDir": ".",
9+
"lib": [
10+
"ES2021"
11+
]
12+
}
13+
}

0 commit comments

Comments
 (0)