Skip to content

Commit

Permalink
Merge pull request #16806 from schlawg/improved-font-handling
Browse files Browse the repository at this point in the history
improve font situation
  • Loading branch information
ornicar authored Jan 20, 2025
2 parents ddc9643 + e913d65 commit 7b0928d
Show file tree
Hide file tree
Showing 77 changed files with 337 additions and 456 deletions.
6 changes: 4 additions & 2 deletions app/views/base/embed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ object embed:
st.headTitle(title),
(ctx.bg == "system").option(page.ui.systemThemeScript(ctx.nonce.some)),
page.ui.pieceSprite(ctx.pieceSet.name),
cssTag("common.theme.embed"), // includes both light & dark colors
cssTag("common.theme.embed"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssKeys.map(cssTag),
page.ui.scriptsPreload(modules.flatMap(_.map(_.key)))
),
Expand Down Expand Up @@ -65,7 +66,8 @@ object embed:
st.headTitle(title),
(ctx.bg == "system").option(page.ui.systemThemeScript(ctx.nonce.some)),
page.ui.pieceSprite(ctx.pieceSet.name),
cssTag("common.theme.embed"), // includes both light & dark colors
cssTag("common.theme.embed"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssKeys.map(cssTag),
page.ui.sitePreload(
List[I18nModule.Selector](_.site, _.timeago) ++ i18nModules,
Expand Down
1 change: 1 addition & 0 deletions app/views/base/page.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ object page:
else s"${ctx.me.so(_.username.value + " ")} $prodTitle"
,
cssTag("common.theme.all"),
link(rel := "stylesheet", href := assetUrl("css/theme/font-face.css")),
cssTag("site"),
pref.is3d.option(cssTag("common.board-3d")),
ctx.data.inquiry.isDefined.option(cssTag("mod.inquiry")),
Expand Down
3 changes: 1 addition & 2 deletions bin/gen/licon.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def main():

gen_sources(codes)

print('Generated:\n public/font/lichess.woff\n public/font/lichess.woff2\n public/font/lichess.ttf')
print('Generated:\n public/font/lichess.woff2\n public/font/lichess.ttf\n public/oops/font.html')
print(' modules/ui/src/main/Icon.scala\n ui/common/src/licon.ts')
print(' ui/common/css/abstract/_licon.scss\n')
print("Don't forget to install lichess.ttf in your code editor\n")
Expand Down Expand Up @@ -158,7 +158,6 @@ def gen_fonts():
[f, name] = tempfile.mkstemp(suffix='.pe', dir='.')
os.write(f, textwrap.dedent(f"""
Open('lichess.sfd')
Generate('lichess.woff')
Generate('lichess.woff2')
Generate('lichess.ttf')
Quit()
Expand Down
22 changes: 12 additions & 10 deletions modules/web/src/main/ui/layout.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,22 @@ final class layout(helpers: Helpers, assetHelper: lila.web.ui.AssetFullHelper)(
def fontPreload(using ctx: Context) = frag(
preload(assetUrl("font/lichess.woff2"), "font", crossorigin = true, "font/woff2".some),
preload(
assetUrl("font/noto-sans-v14-latin-regular.woff2"),
assetUrl("font/noto-sans-latin.woff2"),
"font",
crossorigin = true,
"font/woff2".some
),
(!ctx.pref.pieceNotationIsLetter).option(
preload(assetUrl("font/lichess.chess.woff2"), "font", crossorigin = true, "font/woff2".some)
preload(assetUrl("font/lichess-chess.woff2"), "font", crossorigin = true, "font/woff2".some)
)
)

def allNotifications(challenges: Int, notifs: Int)(using Translate) =
val challengeTitle = trans.challenge.challengesX.txt(challenges)
val notifTitle = trans.site.notificationsX.txt(notifs)
spaceless:
s"""<div>
s"""
<div>
<button id="challenge-toggle" class="toggle link">
<span title="$challengeTitle" role="status" aria-label="$challengeTitle" class="data-count" data-count="$challenges" data-icon="${Icon.Swords}"></span>
</button>
Expand Down Expand Up @@ -220,13 +221,14 @@ final class layout(helpers: Helpers, assetHelper: lila.web.ui.AssetFullHelper)(
def spaceless(html: String) = raw(spaceRegex.replaceAllIn(html.replace("\\n", ""), ""))

def lichessFontFaceCss = spaceless:
s"""<style>@font-face {
font-family: 'lichess';
font-display: block;
src:
url('${assetUrl("font/lichess.woff2")}') format('woff2'),
url('${assetUrl("font/lichess.woff")}') format('woff');
}</style>"""
s"""
<style>
@font-face {
font-family: 'lichess';
font-display: block;
src: url('${assetUrl("font/lichess.woff2")}') format('woff2')
}
</style>"""

def bottomHtml(using ctx: Context) = frag(
ctx.me
Expand Down
Binary file removed public/font/Segment7.woff
Binary file not shown.
Binary file removed public/font/alarmclock.woff2
Binary file not shown.
File renamed without changes.
File renamed without changes.
Binary file removed public/font/lichess.chess.woff
Binary file not shown.
Binary file removed public/font/lichess.woff
Binary file not shown.
Binary file removed public/font/noto-sans-bold-cyrillic-ext.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-bold-cyrillic.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-bold-devanagari.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-bold-greek-ext.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-bold-greek.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-bold-vietnamese.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-cyrillic-ext.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-cyrillic.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-devanagari.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-greek-ext.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-greek.woff2
Binary file not shown.
Binary file added public/font/noto-sans-latin-ext.woff2
Binary file not shown.
Binary file added public/font/noto-sans-latin.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-v14-latin-700.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-v14-latin-ext-700.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-v14-latin-ext-regular.woff2
Binary file not shown.
Binary file removed public/font/noto-sans-v14-latin-regular.woff2
Binary file not shown.
Binary file modified public/font/noto-sans-vietnamese.woff2
Binary file not shown.
File renamed without changes.
Binary file added public/font/roboto-cyrillic-ext.woff2
Binary file not shown.
Binary file added public/font/roboto-cyrillic.woff2
Binary file not shown.
Binary file added public/font/roboto-greek-ext.woff2
Binary file not shown.
Binary file added public/font/roboto-greek.woff2
Binary file not shown.
Binary file added public/font/roboto-latin-ext.woff2
Binary file not shown.
Binary file added public/font/roboto-latin.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-cyrillic-ext.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-cyrillic.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-greek-ext.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-greek.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-latin-ext.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-latin.woff2
Binary file not shown.
Binary file removed public/font/roboto-light-vietnamese.woff2
Binary file not shown.
Binary file added public/font/roboto-vietnamese.woff2
Binary file not shown.
File renamed without changes.
File renamed without changes.
5 changes: 3 additions & 2 deletions ui/.build/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import { tsc, stopTscWatch } from './tsc.ts';
import { sass, stopSass } from './sass.ts';
import { esbuild, stopEsbuildWatch } from './esbuild.ts';
import { sync, stopSync } from './sync.ts';
import { hash } from './hash.ts';
import { stopManifest } from './manifest.ts';
import { env, errorMark, colors as c } from './env.ts';
import { env, errorMark, c } from './env.ts';
import { i18n, stopI18nWatch } from './i18n.ts';
import { unique } from './algo.ts';
import { clean } from './clean.ts';
Expand Down Expand Up @@ -37,7 +38,7 @@ export async function build(pkgs: string[]): Promise<void> {
fs.promises.mkdir(env.buildTempDir),
]);

await Promise.all([sass(), sync(), i18n()]);
await Promise.all([sass(), sync().then(hash), i18n()]);
await Promise.all([tsc(), esbuild(), monitor(pkgs)]);
}

Expand Down
2 changes: 1 addition & 1 deletion ui/.build/src/clean.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { promises as fs } from 'fs';
import fg from 'fast-glob';
import { env, colors as c } from './env.ts';
import { env, c } from './env.ts';

const globOpts: fg.Options = {
absolute: true,
Expand Down
2 changes: 1 addition & 1 deletion ui/.build/src/console.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createServer, IncomingMessage, ServerResponse } from 'node:http';
import { env, errorMark, warnMark, colors as c } from './env.ts';
import { env, errorMark, warnMark, c } from './env.ts';

export async function startConsole() {
if (!env.remoteLog || !env.watch) return;
Expand Down
65 changes: 32 additions & 33 deletions ui/.build/src/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'node:path';
import type { Package } from './parse.ts';
import { unique, isEquivalent } from './algo.ts';
import { updateManifest } from './manifest.ts';
import { type Manifest, updateManifest } from './manifest.ts';

// state, logging, and exit code logic

Expand All @@ -25,10 +25,6 @@ export const env = new (class {
readonly i18nDestDir = path.join(this.rootDir, 'translation', 'dest');
readonly i18nJsDir = path.join(this.rootDir, 'translation', 'js');

packages: Map<string, Package> = new Map();
workspaceDeps: Map<string, string[]> = new Map();
building: Package[] = [];

watch = false;
clean = false;
prod = false;
Expand All @@ -42,12 +38,18 @@ export const env = new (class {
exitCode: Map<Builder, number | false> = new Map();
startTime: number | undefined = Date.now();
logTime = true;
logContext = true;
color: any = {
build: 'green',
sass: 'magenta',
tsc: 'yellow',
esbuild: 'blue',
logCtx = true;
logColor = true;

packages: Map<string, Package> = new Map();
workspaceDeps: Map<string, string[]> = new Map();
building: Package[] = [];
manifest: { js: Manifest; i18n: Manifest; css: Manifest; hashed: Manifest; dirty: boolean } = {
i18n: {},
js: {},
css: {},
hashed: {},
dirty: false,
};

get sass(): boolean {
Expand Down Expand Up @@ -97,7 +99,7 @@ export const env = new (class {
}

good(ctx = 'build'): void {
this.log(colors.good('No errors') + this.watch ? ` - ${colors.grey('Watching')}...` : '', { ctx: ctx });
this.log(c.good('No errors') + this.watch ? ` - ${c.grey('Watching')}...` : '', { ctx: ctx });
}

log(d: any, { ctx = 'build', error = false, warn = false }: any = {}): void {
Expand All @@ -108,20 +110,13 @@ export const env = new (class {
? d.join('\n')
: JSON.stringify(d);

const esc = this.color ? escape : (text: string, _: any) => text;

if (!this.color) text = stripColorEscapes(text);

const prefix = (
(this.logTime === false ? '' : prettyTime()) +
(!ctx || !this.logContext ? '' : `[${esc(ctx, colorForCtx(ctx, this.color))}] `)
(this.logTime ? prettyTime() : '') + (ctx && this.logCtx ? `[${escape(ctx, colorForCtx(ctx))}]` : '')
).trim();

lines(text).forEach(line =>
lines(this.logColor ? text : stripColorEscapes(text)).forEach(line =>
console.log(
`${prefix ? prefix + ' - ' : ''}${
error ? esc(line, codes.error) : warn ? esc(line, codes.warn) : line
}`,
`${prefix ? prefix + ' - ' : ''}${escape(line, error ? codes.error : warn ? codes.warn : undefined)}`,
),
);
}
Expand All @@ -132,13 +127,11 @@ export const env = new (class {
const allDone = this.exitCode.size === 3;
if (ctx !== 'tsc' || code === 0)
this.log(
`${code === 0 ? 'Done' : colors.red('Failed')}` +
(this.watch ? ` - ${colors.grey('Watching')}...` : ''),
`${code === 0 ? 'Done' : c.red('Failed')}` + (this.watch ? ` - ${c.grey('Watching')}...` : ''),
{ ctx },
);
if (allDone) {
if (this.startTime && !err)
this.log(`Done in ${colors.green((Date.now() - this.startTime) / 1000 + '')}s`);
if (this.startTime && !err) this.log(`Done in ${c.green((Date.now() - this.startTime) / 1000 + '')}s`);
this.startTime = undefined; // it's pointless to time subsequent builds, they are too fast
}
if (!this.watch && err) process.exitCode = err;
Expand All @@ -148,11 +141,12 @@ export const env = new (class {

export const lines = (s: string): string[] => s.split(/[\n\r\f]+/).filter(x => x.trim());

const escape = (text: string, code: string): string => `\x1b[${code}m${stripColorEscapes(text)}\x1b[0m`;
const escape = (text: string, code?: string): string =>
env.logColor && code ? `\x1b[${code}m${stripColorEscapes(text)}\x1b[0m` : text;

const colorLines = (text: string, code: string) =>
lines(text)
.map(t => (env.color ? escape(t, code) : t))
.map(t => escape(t, code))
.join('\n');

const codes: Record<string, string> = {
Expand All @@ -168,7 +162,7 @@ const codes: Record<string, string> = {
warn: '33',
};

export const colors: Record<string, (text: string) => string> = {
export const c: Record<string, (text: string) => string> = {
red: (text: string): string => colorLines(text, codes.red),
green: (text: string): string => colorLines(text, codes.green),
yellow: (text: string): string => colorLines(text, codes.yellow),
Expand All @@ -183,11 +177,16 @@ export const colors: Record<string, (text: string) => string> = {
cyanBold: (text: string): string => colorLines(text, codes.cyan + ';1'),
};

export const errorMark: string = colors.red('✘ ') + colors.error('[ERROR]');
export const warnMark: string = colors.yellow('⚠ ') + colors.warn('[WARNING]');
export const errorMark: string = c.red('✘ ') + c.error('[ERROR]');
export const warnMark: string = c.yellow('⚠ ') + c.warn('[WARNING]');

const colorForCtx = (ctx: string, color: any): string =>
color && ctx in color && color[ctx] in codes ? codes[color[ctx]] : codes.grey;
const colorForCtx = (ctx: string): string =>
({
build: codes.green,
sass: codes.magenta,
tsc: codes.yellow,
esbuild: codes.blue,
})[ctx] ?? codes.grey;

const pad2 = (n: number) => (n < 10 ? `0${n}` : `${n}`);

Expand Down
2 changes: 1 addition & 1 deletion ui/.build/src/esbuild.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'node:path';
import es from 'esbuild';
import fs from 'node:fs';
import { env, errorMark, colors as c } from './env.ts';
import { env, errorMark, c } from './env.ts';
import { type Manifest, updateManifest } from './manifest.ts';
import { trimAndConsolidateWhitespace, readable } from './parse.ts';

Expand Down
88 changes: 88 additions & 0 deletions ui/.build/src/hash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { globArray } from './parse.ts';
import { updateManifest } from './manifest.ts';
import { env } from './env.ts';

export async function hash(): Promise<void> {
const newHashLinks = new Map<string, number>();
const alreadyHashed = new Map<string, string>();
const hashed = (
await Promise.all(
env.building.flatMap(pkg =>
pkg.hash.map(async hash =>
(await globArray(hash.glob, { cwd: env.outDir })).map(path => ({
path,
replace: hash.replace,
root: pkg.root,
})),
),
),
)
).flat();

const sourceStats = await Promise.all(hashed.map(hash => fs.promises.stat(hash.path)));

for (const [i, stat] of sourceStats.entries()) {
const name = hashed[i].path.slice(env.outDir.length + 1);
if (stat.mtimeMs === env.manifest.hashed[name]?.mtime)
alreadyHashed.set(name, env.manifest.hashed[name].hash!);
else newHashLinks.set(name, stat.mtimeMs);
}
await Promise.allSettled([...alreadyHashed].map(([name, hash]) => link(name, hash)));

for await (const { name, hash } of [...newHashLinks.keys()].map(hashLink)) {
env.manifest.hashed[name] = Object.defineProperty({ hash }, 'mtime', { value: newHashLinks.get(name) });
}
if (newHashLinks.size === 0 && alreadyHashed.size === Object.keys(env.manifest.hashed).length) return;

for (const key of Object.keys(env.manifest.hashed)) {
if (!hashed.some(x => x.path.endsWith(key))) delete env.manifest.hashed[key];
}
// TODO find a better home for all of this
const replaceMany: Map<string, { root: string; mapping: Record<string, string> }> = new Map();
for (const { root, path, replace } of hashed) {
if (!replace) continue;
const replaceInOne = replaceMany.get(replace) ?? { root, mapping: {} };
const from = path.slice(env.outDir.length + 1);
replaceInOne.mapping[from] = asHashed(from, env.manifest.hashed[from].hash!);
replaceMany.set(replace, replaceInOne);
}
for await (const { name, hash } of [...replaceMany].map(([n, r]) => replaceAllIn(n, r.root, r.mapping))) {
env.manifest.hashed[name] = { hash };
}
updateManifest({ dirty: true });
}

async function replaceAllIn(name: string, root: string, files: Record<string, string>) {
const result = Object.entries(files).reduce(
(data, [from, to]) => data.replaceAll(from, to),
await fs.promises.readFile(path.join(root, name), 'utf8'),
);
const hash = crypto.createHash('sha256').update(result).digest('hex').slice(0, 8);
await fs.promises.writeFile(path.join(env.hashOutDir, asHashed(name, hash)), result);
return { name, hash };
}

async function hashLink(name: string) {
const src = path.join(env.outDir, name);
const hash = crypto
.createHash('sha256')
.update(await fs.promises.readFile(src))
.digest('hex')
.slice(0, 8);
await link(name, hash);
return { name, hash };
}

async function link(name: string, hash: string) {
const link = path.join(env.hashOutDir, asHashed(name, hash));
return fs.promises.symlink(path.join('..', name), link).catch(() => {});
}

function asHashed(path: string, hash: string) {
const name = path.slice(path.lastIndexOf('/') + 1);
const extPos = name.indexOf('.');
return extPos < 0 ? `${name}.${hash}` : `${name.slice(0, extPos)}.${hash}${name.slice(extPos)}`;
}
2 changes: 1 addition & 1 deletion ui/.build/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'node:path';
import crypto from 'node:crypto';
import fs from 'node:fs';
import { XMLParser } from 'fast-xml-parser';
import { env, colors as c } from './env.ts';
import { env, c } from './env.ts';
import { globArray, readable } from './parse.ts';
import { type Manifest, updateManifest } from './manifest.ts';
import { quantize, zip } from './algo.ts';
Expand Down
4 changes: 2 additions & 2 deletions ui/.build/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ if (['--tsc', '--sass', '--esbuild', '--sync', '--i18n'].filter(x => argv.includ
env.i18n = argv.includes('--i18n');
env.sync = argv.includes('--sync');
}
if (argv.includes('--no-color')) env.color = undefined;

env.logTime = !argv.includes('--no-time');
env.logContext = !argv.includes('--no-context');
env.logCtx = !argv.includes('--no-context');
env.logColor = !argv.includes('--no-color');
env.watch = argv.includes('--watch') || oneDashArgs.includes('w');
env.prod = argv.includes('--prod') || oneDashArgs.includes('p');
env.debug = argv.includes('--debug') || oneDashArgs.includes('d');
Expand Down
Loading

0 comments on commit 7b0928d

Please sign in to comment.