Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve font situation #16806

Merged
merged 5 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading