mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +00:00
refactor changes
This commit is contained in:
1 parent
df1a447a79
commit
d4318554f6
36 files changed
+1251
-291
No files matched your search
+4
-1
@@ -8,7 +8,9 @@
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"dmg": "tauri build --bundles dmg"
|
||||
"dmg": "tauri build --bundles dmg",
|
||||
"fonts:sync": "node node_modules/margin-shared/bin/sync-fonts.mjs .",
|
||||
"fonts:check": "node node_modules/margin-shared/bin/sync-fonts.mjs . --check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
@@ -21,6 +23,7 @@
|
||||
"@tiptap/pm": "^3.27.1",
|
||||
"@tiptap/react": "^3.27.1",
|
||||
"@tiptap/starter-kit": "^3.27.1",
|
||||
"margin-shared": "file:./shared",
|
||||
"pdfjs-dist": "^6.0.227",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
|
||||
Generated
+9
@@ -38,6 +38,9 @@ importers:
|
||||
'@tiptap/starter-kit':
|
||||
specifier: ^3.27.1
|
||||
version: 3.27.1
|
||||
margin-shared:
|
||||
specifier: file:./shared
|
||||
version: file:shared
|
||||
pdfjs-dist:
|
||||
specifier: ^6.0.227
|
||||
version: 6.0.227
|
||||
@@ -887,6 +890,10 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
margin-shared@file:shared:
|
||||
resolution: {directory: shared, type: directory}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -1784,6 +1791,8 @@ snapshots:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
margin-shared@file:shared: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# margin-shared
|
||||
|
||||
The faces, palette and glyphs [Margin](../) and Margin Docs both draw from. It is the one copy of
|
||||
the decisions the two apps have to agree on: which six fonts they offer, what their named pairings
|
||||
are, the 35 design tokens they share, and the title bar icon paths.
|
||||
|
||||
No build step and no dependencies. Both apps resolve the TypeScript source directly through their
|
||||
bundler, so an edit here is live in both on the next dev server restart.
|
||||
|
||||
Margin consumes it as `"margin-shared": "file:./shared"`. Margin Docs, which lives in a separate
|
||||
repository, uses a relative path to this directory; see its `package.json`.
|
||||
|
||||
The font binaries in `fonts/` are the source of truth, and each app keeps a vendored copy under
|
||||
`public/fonts` because its Rust PDF exporter reads them with `include_bytes!` before any npm install
|
||||
has run. Run `pnpm fonts:sync` in an app to refresh that copy and `pnpm fonts:check` to verify it.
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env node
|
||||
// Copies this package's font files into an app's public/fonts, or checks that they already match.
|
||||
//
|
||||
// The copies exist because both apps' `src-tauri/src/pdf.rs` reads the same files with
|
||||
// `include_bytes!`, so the bytes have to be on disk at a path cargo can see before any npm install
|
||||
// has run. Serving them out of node_modules would mean a cargo build that fails on a fresh clone
|
||||
// until the front end was installed, which is a worse trade than a vendored copy with a named
|
||||
// upstream and a check that fails loudly when the two drift.
|
||||
//
|
||||
// node bin/sync-fonts.mjs <app-dir> copy, reporting what changed
|
||||
// node bin/sync-fonts.mjs <app-dir> --check compare only, exit 1 on any difference
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const source = resolve(here, "..", "fonts");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const check = args.includes("--check");
|
||||
const target = args.find((a) => !a.startsWith("--"));
|
||||
|
||||
if (!target) {
|
||||
console.error("usage: sync-fonts.mjs <app-dir> [--check]");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const dest = resolve(process.cwd(), target, "public", "fonts");
|
||||
const files = readdirSync(source).filter((f) => f.endsWith(".ttf") || f.endsWith(".txt")).sort();
|
||||
|
||||
if (!check) mkdirSync(dest, { recursive: true });
|
||||
|
||||
const wrong = [];
|
||||
let copied = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const from = join(source, file);
|
||||
const to = join(dest, file);
|
||||
const want = readFileSync(from);
|
||||
const have = existsSync(to) ? readFileSync(to) : null;
|
||||
|
||||
if (have !== null && have.equals(want)) continue;
|
||||
|
||||
if (check) {
|
||||
wrong.push(`${have === null ? "missing" : "differs"}: ${file}`);
|
||||
continue;
|
||||
}
|
||||
writeFileSync(to, want);
|
||||
copied += 1;
|
||||
}
|
||||
|
||||
// A file the app has and the package does not is reported rather than deleted. It is far more
|
||||
// likely to be a face somebody is in the middle of adding than something to throw away, and this
|
||||
// script does not get to be the reason an asset disappears.
|
||||
if (existsSync(dest)) {
|
||||
for (const file of readdirSync(dest)) {
|
||||
if (!files.includes(file)) wrong.push(`not in the package, left alone: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
if (wrong.length === 0) {
|
||||
console.log(`fonts match margin-shared (${files.length} files)`);
|
||||
process.exit(0);
|
||||
}
|
||||
console.error("public/fonts is out of step with margin-shared:");
|
||||
for (const line of wrong) console.error(` ${line}`);
|
||||
console.error("\nrun: pnpm fonts:sync");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(copied === 0 ? `fonts already current (${files.length} files)` : `synced ${copied} file(s)`);
|
||||
for (const line of wrong) console.log(` note: ${line}`);
|
||||
@@ -0,0 +1,111 @@
|
||||
/* The six families src/fonts.ts offers, bound to the variable files in fonts/.
|
||||
*
|
||||
* The URLs are absolute site paths, so each app serves these out of its own `public/fonts`. The
|
||||
* copies there are vendored from this package and kept honest by `bin/sync-fonts.mjs`; they are not
|
||||
* imported from node_modules, because both apps' `src-tauri/src/pdf.rs` reads the same files with
|
||||
* `include_bytes!` and a cargo build must not depend on an npm install having run first.
|
||||
*
|
||||
* Every one is a variable file with an upright and an italic cut, so a weight anywhere in the
|
||||
* declared range is a real instance rather than a synthesised one, and italic is the designer's
|
||||
* italic rather than a slant applied by the browser.
|
||||
*
|
||||
* `font-display: block` throughout, and that is the unusual choice. The alternative is a first
|
||||
* paint in Georgia that reflows into Literata a moment later, and a page of prose that resets its
|
||||
* line breaks under the caret is worse than a page that arrives a beat late. The files are local to
|
||||
* the bundle, so the beat is a disk read. */
|
||||
|
||||
@font-face {
|
||||
font-family: "Hanken Grotesk";
|
||||
src: url("/fonts/HankenGrotesk-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Hanken Grotesk";
|
||||
src: url("/fonts/HankenGrotesk-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Literata";
|
||||
src: url("/fonts/Literata-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Literata";
|
||||
src: url("/fonts/Literata-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "EB Garamond";
|
||||
src: url("/fonts/EBGaramond-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 800;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "EB Garamond";
|
||||
src: url("/fonts/EBGaramond-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 800;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/fonts/Lora-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 700;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/fonts/Lora-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 700;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Serif 4";
|
||||
src: url("/fonts/SourceSerif4-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Serif 4";
|
||||
src: url("/fonts/SourceSerif4-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Fraunces";
|
||||
src: url("/fonts/Fraunces-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Fraunces";
|
||||
src: url("/fonts/Fraunces-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/* The palette, the type scale and the shape constants both apps are built from.
|
||||
*
|
||||
* These 35 tokens already held identical values in the two repos before this package existed, which
|
||||
* is the argument for moving them: they were identical by somebody remembering to copy a hex across,
|
||||
* and that is not a mechanism. A colour that only exists in one app is not here. Each app layers its
|
||||
* own sheet on top of this one and adds what only it needs, which is a preview pane width in Margin
|
||||
* and a document typography scale in Margin Docs.
|
||||
*
|
||||
* --font-book and --font-heading are the two a document overrides at runtime, per book or per
|
||||
* document. What is set here is the default pair, and the only place that decides what an untouched
|
||||
* page looks like. */
|
||||
|
||||
:root {
|
||||
--font-ui: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
--font-book: "Literata", Georgia, "Times New Roman", serif;
|
||||
--font-heading: "Literata", Georgia, "Times New Roman", serif;
|
||||
|
||||
--r-sm: 5px;
|
||||
--r-md: 8px;
|
||||
--r-lg: 12px;
|
||||
|
||||
--pane-sidebar: 248px;
|
||||
--measure: 46em;
|
||||
--titlebar-h: 46px;
|
||||
|
||||
--t-1: 11px;
|
||||
--t-2: 12px;
|
||||
--t-3: 13px;
|
||||
--t-4: 15px;
|
||||
|
||||
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||
}
|
||||
|
||||
:root,
|
||||
:root[data-theme="light"] {
|
||||
--paper: #fcfbf7;
|
||||
--shell: #f1ece2;
|
||||
--sidebar: #ece6da;
|
||||
--raised: #fbfaf6;
|
||||
|
||||
--ink: #23201b;
|
||||
--ink-soft: #6b6458;
|
||||
--ink-faint: #9b9484;
|
||||
|
||||
--line: #e3ddce;
|
||||
--line-strong: #d6cfbd;
|
||||
|
||||
--accent: #2a2622;
|
||||
--accent-ink: #100e0b;
|
||||
--accent-wash: rgba(35, 32, 27, 0.08);
|
||||
--accent-contrast: #faf7f0;
|
||||
--selection: rgba(35, 32, 27, 0.14);
|
||||
--glass: rgba(252, 251, 247, 0.86);
|
||||
|
||||
--danger: #b4453a;
|
||||
--danger-ink: #963327;
|
||||
--danger-wash: rgba(180, 69, 58, 0.1);
|
||||
--danger-contrast: #faf7f0;
|
||||
|
||||
--shadow-page: 0 1px 2px rgba(35, 32, 27, 0.06), 0 14px 30px rgba(35, 32, 27, 0.09);
|
||||
--shadow-pop: 0 8px 24px rgba(35, 32, 27, 0.14);
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--paper: #1d1a16;
|
||||
--shell: #151310;
|
||||
--sidebar: #1a1713;
|
||||
--raised: #232019;
|
||||
|
||||
--ink: #ece6da;
|
||||
--ink-soft: #a89f8e;
|
||||
--ink-faint: #756d5e;
|
||||
|
||||
--line: #2c2823;
|
||||
--line-strong: #3a352d;
|
||||
|
||||
--accent: #efe9dc;
|
||||
--accent-ink: #ffffff;
|
||||
--accent-wash: rgba(239, 233, 220, 0.12);
|
||||
--accent-contrast: #1a1714;
|
||||
--selection: rgba(239, 233, 220, 0.18);
|
||||
--glass: rgba(33, 30, 25, 0.88);
|
||||
|
||||
--danger: #d97a6f;
|
||||
--danger-ink: #e58e84;
|
||||
--danger-wash: rgba(217, 122, 111, 0.16);
|
||||
--danger-contrast: #1a1714;
|
||||
|
||||
--shadow-page: 0 1px 2px rgba(0, 0, 0, 0.4), 0 14px 34px rgba(0, 0, 0, 0.5);
|
||||
--shadow-pop: 0 8px 24px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2017 The EB Garamond Project Authors (https://github.com/octaviopardo/EBGaramond12)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2018 The Fraunces Project Authors (https://github.com/undercasetype/Fraunces)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,94 @@
|
||||
Copyright 2021 The Hanken Grotesk Project Authors (https://github.com/marcologous/hanken-grotesk)
|
||||
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2017 The Literata Project Authors (https://github.com/googlefonts/literata)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2011 The Lora Project Authors (https://github.com/cyrealtype/Lora-Cyrillic), with Reserved Font Name "Lora".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2014 The Source Serif 4 Project Authors (https://github.com/adobe-fonts/source-serif)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "margin-shared",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"description": "The faces, palette and glyphs Margin and Margin Docs both draw from.",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./fonts": "./src/fonts.ts",
|
||||
"./icons": "./src/icons.ts",
|
||||
"./css/fonts.css": "./css/fonts.css",
|
||||
"./css/tokens.css": "./css/tokens.css"
|
||||
},
|
||||
"bin": {
|
||||
"margin-shared-fonts": "./bin/sync-fonts.mjs"
|
||||
},
|
||||
"files": ["src", "css", "fonts", "bin"]
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// The faces both apps offer, and the two slots they set them into.
|
||||
//
|
||||
// This lives in one place because the two apps have to agree about it. A face named here is a
|
||||
// `@font-face` in css/fonts.css, a file in fonts/, and a family a Typst preamble names on the way
|
||||
// to a PDF, and those four lists going out of step with each other is a document that renders in
|
||||
// one app and falls back to Georgia in the other. There is no way to keep four lists in two repos
|
||||
// honest by hand, so there is one list.
|
||||
//
|
||||
// Two slots and not one. Body and heading are the only typographic decision worth a control:
|
||||
// a document that lets its author pick a face per paragraph is a word processor, and neither of
|
||||
// these is one. The scale, the leading and the measure belong to each app's own stylesheet, which
|
||||
// decided them once for every document.
|
||||
//
|
||||
// A `FontRef` is stored, not a family name. "Literata" as a string cannot say whether it means the
|
||||
// file in fonts/ or a copy the user installed themselves, and those are two different faces the
|
||||
// moment one of them is updated.
|
||||
|
||||
export type FontCategory = "serif" | "sans" | "display";
|
||||
|
||||
/** One face that ships in fonts/, in the variable file both editors render from. */
|
||||
export interface BundledFont {
|
||||
id: string;
|
||||
label: string;
|
||||
/** The CSS family name, which is also what a Typst preamble names it by. */
|
||||
family: string;
|
||||
category: FontCategory;
|
||||
regular: string;
|
||||
italic: string;
|
||||
/** The weight range the variable file covers, for the `@font-face` in css/fonts.css. */
|
||||
weight: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The six, and the order a picker lists them in: the serifs a page of prose is set in, the one
|
||||
* display face, then the sans both apps' own chrome already uses.
|
||||
*
|
||||
* Literata and Hanken Grotesk are first-class here and also special: they are the two that have
|
||||
* static instances cut for PDF export, so they are the only pair whose bold really is bold on the
|
||||
* page. The other four export as their variable file at one weight, which each app's pdf.rs
|
||||
* explains.
|
||||
*/
|
||||
export const BUNDLED_FONTS: readonly BundledFont[] = [
|
||||
{
|
||||
id: "literata",
|
||||
label: "Literata",
|
||||
family: "Literata",
|
||||
category: "serif",
|
||||
regular: "Literata-VF.ttf",
|
||||
italic: "Literata-Italic-VF.ttf",
|
||||
weight: "200 900",
|
||||
},
|
||||
{
|
||||
id: "eb-garamond",
|
||||
label: "EB Garamond",
|
||||
family: "EB Garamond",
|
||||
category: "serif",
|
||||
regular: "EBGaramond-VF.ttf",
|
||||
italic: "EBGaramond-Italic-VF.ttf",
|
||||
weight: "400 800",
|
||||
},
|
||||
{
|
||||
id: "lora",
|
||||
label: "Lora",
|
||||
family: "Lora",
|
||||
category: "serif",
|
||||
regular: "Lora-VF.ttf",
|
||||
italic: "Lora-Italic-VF.ttf",
|
||||
weight: "400 700",
|
||||
},
|
||||
{
|
||||
id: "source-serif",
|
||||
label: "Source Serif 4",
|
||||
family: "Source Serif 4",
|
||||
category: "serif",
|
||||
regular: "SourceSerif4-VF.ttf",
|
||||
italic: "SourceSerif4-Italic-VF.ttf",
|
||||
weight: "200 900",
|
||||
},
|
||||
{
|
||||
id: "fraunces",
|
||||
label: "Fraunces",
|
||||
family: "Fraunces",
|
||||
category: "display",
|
||||
regular: "Fraunces-VF.ttf",
|
||||
italic: "Fraunces-Italic-VF.ttf",
|
||||
weight: "100 900",
|
||||
},
|
||||
{
|
||||
id: "hanken",
|
||||
label: "Hanken Grotesk",
|
||||
family: "Hanken Grotesk",
|
||||
category: "sans",
|
||||
regular: "HankenGrotesk-VF.ttf",
|
||||
italic: "HankenGrotesk-Italic-VF.ttf",
|
||||
weight: "100 900",
|
||||
},
|
||||
];
|
||||
|
||||
/** One of the six, or a family off the machine. */
|
||||
export type FontRef = { kind: "bundled"; id: string } | { kind: "system"; family: string };
|
||||
|
||||
/**
|
||||
* What one book or one document is set in.
|
||||
*
|
||||
* Named for the pair rather than for either app's noun, because the two call the thing it belongs
|
||||
* to different names. Each app aliases it: `BookFonts` in Margin, `DocumentFonts` in Margin Docs.
|
||||
*/
|
||||
export interface FontPair {
|
||||
body: FontRef;
|
||||
heading: FontRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* A named pair, which is what a picker offers first.
|
||||
*
|
||||
* Pairing two faces is the part of this that takes an eye, and a list of twelve families with no
|
||||
* opinion attached is how a document ends up in Fraunces body text. The presets are the answer to
|
||||
* "make this look like something"; the two selects underneath are for somebody who already knows.
|
||||
*/
|
||||
export interface FontPairing {
|
||||
id: string;
|
||||
label: string;
|
||||
body: FontRef;
|
||||
heading: FontRef;
|
||||
}
|
||||
|
||||
const bundled = (id: string): FontRef => ({ kind: "bundled", id });
|
||||
|
||||
export const FONT_PAIRINGS: readonly FontPairing[] = [
|
||||
{ id: "quiet-press", label: "Quiet Press", body: bundled("literata"), heading: bundled("literata") },
|
||||
{ id: "classic", label: "Classic", body: bundled("eb-garamond"), heading: bundled("eb-garamond") },
|
||||
{ id: "editorial", label: "Editorial", body: bundled("source-serif"), heading: bundled("fraunces") },
|
||||
{ id: "modern", label: "Modern", body: bundled("lora"), heading: bundled("hanken") },
|
||||
{ id: "contrast", label: "Contrast", body: bundled("literata"), heading: bundled("hanken") },
|
||||
{ id: "plain", label: "Plain", body: bundled("hanken"), heading: bundled("hanken") },
|
||||
];
|
||||
|
||||
/** What a page is set in until somebody says otherwise: the css/tokens.css pair, in FontRef form. */
|
||||
export const DEFAULT_FONTS: FontPair = { body: bundled("literata"), heading: bundled("literata") };
|
||||
|
||||
// The two tails from css/tokens.css, so a face that fails to load falls back to what the app would
|
||||
// have used anyway rather than to the webview's default.
|
||||
const SERIF_FALLBACK = `Georgia, "Times New Roman", serif`;
|
||||
const SANS_FALLBACK = `ui-sans-serif, system-ui, -apple-system, sans-serif`;
|
||||
|
||||
export function bundledFont(id: string): BundledFont | undefined {
|
||||
return BUNDLED_FONTS.find((f) => f.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A `FontRef` as one string, which is what a `<select>` value and a stored preference both need.
|
||||
*
|
||||
* The two-character tag is the whole point: a system family can be called "Literata" and must not
|
||||
* come back as the bundled one.
|
||||
*/
|
||||
export function encodeRef(ref: FontRef): string {
|
||||
return ref.kind === "bundled" ? `b:${ref.id}` : `s:${ref.family}`;
|
||||
}
|
||||
|
||||
export function decodeRef(value: string): FontRef {
|
||||
return value.startsWith("s:")
|
||||
? { kind: "system", family: value.slice(2) }
|
||||
: { kind: "bundled", id: value.slice(2) };
|
||||
}
|
||||
|
||||
/** The bare family name, which is what a Typst preamble names a face by. */
|
||||
export function fontFamilyName(ref: FontRef): string {
|
||||
if (ref.kind === "system") return ref.family;
|
||||
return bundledFont(ref.id)?.family ?? "Literata";
|
||||
}
|
||||
|
||||
/** What a picker calls it. Identical to the family for a system face, which has no other name. */
|
||||
export function fontLabel(ref: FontRef): string {
|
||||
if (ref.kind === "system") return ref.family;
|
||||
return bundledFont(ref.id)?.label ?? ref.id;
|
||||
}
|
||||
|
||||
/** A CSS font stack, which is what goes into `--font-book` and `--font-heading`. */
|
||||
export function fontStack(ref: FontRef): string {
|
||||
if (ref.kind === "bundled") {
|
||||
const font = bundledFont(ref.id);
|
||||
if (!font) return `"Literata", ${SERIF_FALLBACK}`;
|
||||
return `"${font.family}", ${font.category === "sans" ? SANS_FALLBACK : SERIF_FALLBACK}`;
|
||||
}
|
||||
return `"${ref.family}", ${SERIF_FALLBACK}`;
|
||||
}
|
||||
|
||||
export function refsEqual(a: FontRef, b: FontRef): boolean {
|
||||
if (a.kind === "bundled" && b.kind === "bundled") return a.id === b.id;
|
||||
if (a.kind === "system" && b.kind === "system") return a.family === b.family;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function fontsEqual(a: FontPair, b: FontPair): boolean {
|
||||
return refsEqual(a.body, b.body) && refsEqual(a.heading, b.heading);
|
||||
}
|
||||
|
||||
/** Which preset this pair is, or null for a combination somebody built themselves. */
|
||||
export function pairingFor(fonts: FontPair): string | null {
|
||||
const match = FONT_PAIRINGS.find(
|
||||
(p) => refsEqual(p.body, fonts.body) && refsEqual(p.heading, fonts.heading),
|
||||
);
|
||||
return match?.id ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The faces an export has to be handed, split by where their bytes come from.
|
||||
*
|
||||
* Bundled faces come back as ids rather than as records, because the id is what crosses the IPC
|
||||
* boundary: the backend already has the bytes compiled in and looks them up by id. Deduplicated,
|
||||
* because the common case is one family in both slots and asking the backend to load a two megabyte
|
||||
* variable file twice is two megabytes of IPC for nothing.
|
||||
*/
|
||||
export function fontsUsed(fonts: FontPair): { bundled: string[]; system: string[] } {
|
||||
const usedBundled = new Set<string>();
|
||||
const usedSystem = new Set<string>();
|
||||
for (const ref of [fonts.body, fonts.heading]) {
|
||||
if (ref.kind === "bundled") {
|
||||
if (bundledFont(ref.id)) usedBundled.add(ref.id);
|
||||
} else {
|
||||
usedSystem.add(ref.family);
|
||||
}
|
||||
}
|
||||
return { bundled: [...usedBundled], system: [...usedSystem] };
|
||||
}
|
||||
|
||||
/** Anything that is not a `FontRef`, from a stored preference written by another version. */
|
||||
export function isFontRef(value: unknown): value is FontRef {
|
||||
if (typeof value !== "object" || value === null) return false;
|
||||
const ref = value as { kind?: unknown; id?: unknown; family?: unknown };
|
||||
if (ref.kind === "bundled") return typeof ref.id === "string" && bundledFont(ref.id) !== undefined;
|
||||
return ref.kind === "system" && typeof ref.family === "string" && ref.family !== "";
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// The title bar glyphs, drawn on a 24 unit grid for a 1.6 stroke.
|
||||
//
|
||||
// Here because the two apps kept drifting. Each had its own idea of what a search or a moon looked
|
||||
// like, they were adjusted independently, and the result was two products from the same hand that
|
||||
// did not look related. A path is a design decision, not a detail, and the fix for two copies of a
|
||||
// decision is one copy.
|
||||
//
|
||||
// Paths and not an icon dependency: a set is six hundred kilobytes for the handful of shapes a
|
||||
// title bar needs, and every one of these is a few dozen bytes.
|
||||
//
|
||||
// Each app renders these through its own `Icon` component. The two components are identical today
|
||||
// and are deliberately not shared: one is React, which would make this package depend on React for
|
||||
// twenty four lines, and a component is where an app is entitled to differ.
|
||||
|
||||
/** A pane and its divider. Also the preview dock in Margin, mirrored. */
|
||||
export const SIDEBAR = "M3 4.5h18v15H3zM9 4.5v15";
|
||||
|
||||
/** Margin's preview dock: the same pane with the divider on the other side. */
|
||||
export const DOCK = "M3 4.5h18v15H3zM14 4.5v15";
|
||||
|
||||
export const SEARCH = "M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM20 20l-4-4";
|
||||
|
||||
/** A capital A with a tick beside it: the letter a checker is looking at, checked. */
|
||||
export const SPELLING = "M4 17l4-10 4 10M5.4 13.4h5.2M15 17l2.5 2.5L22 14";
|
||||
|
||||
/** Three lines of a paragraph and a squiggle under the last, which is the mark grammar leaves. */
|
||||
export const GRAMMAR = "M4 7h16M4 12h12M4 17h7M13.5 18.5c1-1.2 2-1.2 3 0s2 1.2 3 0";
|
||||
|
||||
/**
|
||||
* A capital A beside a lowercase a, which is what a font panel has been called since there were
|
||||
* font panels.
|
||||
*
|
||||
* Two letterforms and not one on a rule: a letter over a full width line is the underline button in
|
||||
* every editor anybody has used. The other constraint is SPELLING above, which is also built on a
|
||||
* capital A; what separates them is the shape to its right, a round bowl here and an angular tick
|
||||
* there, and that difference survives 16px in a way a crossbar's height would not.
|
||||
*/
|
||||
export const FONT =
|
||||
"M2.5 18L6.5 6l4 12M4.3 14.2h4.4M17 11.3a3.2 3.2 0 1 0 0 6.4a3.2 3.2 0 0 0 0-6.4M20.2 11.3v6.7";
|
||||
|
||||
/** The page's two edges with a double headed arrow between them. */
|
||||
export const WIDTH = "M3 5v14M21 5v14M7 12h10M7 12l3-3M7 12l3 3M17 12l-3-3M17 12l-3 3";
|
||||
|
||||
export const EXPORT = "M5 13v6h14v-6M12 16V3M8 7l4-4 4 4";
|
||||
|
||||
export const MOON = "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z";
|
||||
|
||||
/**
|
||||
* The sun, which is the one glyph here that is not a single path: the disc has to be a circle so
|
||||
* that it stays round at every size, and the rays have to be a path so that they keep their caps.
|
||||
* Rendered as two children rather than one `d`.
|
||||
*/
|
||||
export const SUN_RAYS =
|
||||
"M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4";
|
||||
export const SUN_DISC = { cx: 12, cy: 12, r: 4 } as const;
|
||||
|
||||
export const MORE = "M5 12h.01M12 12h.01M19 12h.01";
|
||||
|
||||
export const CLOSE = "M6 6l12 12M18 6L6 18";
|
||||
|
||||
export const CHECK = "M20 6L9 17l-5-5";
|
||||
@@ -0,0 +1,5 @@
|
||||
// Everything both apps draw from. Two entry points as well as this one, `margin-shared/fonts` and
|
||||
// `margin-shared/icons`, so a module that only wants the glyphs does not pull in the catalogue.
|
||||
|
||||
export * from "./fonts";
|
||||
export * as icons from "./icons";
|
||||
Generated
+1
-1
@@ -4713,7 +4713,7 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "margin-app"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"fontdb",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import type { Editor as TiptapEditor } from "@tiptap/react";
|
||||
import { icons } from "margin-shared";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { Dock } from "./Dock";
|
||||
import { ResizeHandle } from "./ResizeHandle";
|
||||
@@ -281,7 +282,7 @@ export function EditorView() {
|
||||
<header className="titlebar" data-tauri-drag-region>
|
||||
<div className="lead">
|
||||
<button className="icon-btn" data-on={sidebarOpen} onClick={toggleSidebar} title="Toggle chapters" aria-label="Toggle chapters">
|
||||
<Icon d="M3 4.5h18v15H3zM9 4.5v15" />
|
||||
<Icon d={icons.SIDEBAR} />
|
||||
</button>
|
||||
</div>
|
||||
<button className="doc-title" onClick={() => setSettingsOpen(true)} title="Book setup">
|
||||
@@ -291,22 +292,22 @@ export function EditorView() {
|
||||
<div className="actions">
|
||||
{isDesktop && !isCompact && <BackupButton />}
|
||||
<button className="icon-btn" data-on={findOpen} onClick={() => (findOpen ? setFindOpen(false) : openFind(false))} title="Find (⌘F)">
|
||||
<Icon d="M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM20 20l-4-4" />
|
||||
<Icon d={icons.SEARCH} />
|
||||
</button>
|
||||
{!isCompact && proofingAvailable && !coverActive && (
|
||||
<>
|
||||
<button className="icon-btn" data-on={spelling} onClick={toggleSpelling} title="Check spelling">
|
||||
<Icon d="M4 17l4-10 4 10M5.4 13.4h5.2M15 17l2.5 2.5L22 14" />
|
||||
<Icon d={icons.SPELLING} />
|
||||
</button>
|
||||
<button className="icon-btn" data-on={grammar} onClick={toggleGrammar} title="Check grammar">
|
||||
<Icon d="M4 7h16M4 12h12M4 17h7M13.5 18.5c1-1.2 2-1.2 3 0s2 1.2 3 0" />
|
||||
<Icon d={icons.GRAMMAR} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{!isCompact && (
|
||||
<div className="menu-wrap">
|
||||
<button className="icon-btn" data-on={widthOpen} onClick={() => setWidthOpen((v) => !v)} title="Editor width">
|
||||
<Icon d="M3 5v14M21 5v14M7 12h10M7 12l3-3M7 12l3 3M17 12l-3-3M17 12l-3 3" />
|
||||
<Icon d={icons.WIDTH} />
|
||||
</button>
|
||||
<Menu open={widthOpen} onClose={() => setWidthOpen(false)}>
|
||||
{WIDTH_OPTIONS.map((w) => (
|
||||
@@ -327,7 +328,7 @@ export function EditorView() {
|
||||
{!isCompact && (
|
||||
<div className="menu-wrap">
|
||||
<button className="icon-btn" data-on={exportOpen} onClick={() => setExportOpen((v) => !v)} title="Export">
|
||||
<Icon d="M5 13v6h14v-6M12 16V3M8 7l4-4 4 4" />
|
||||
<Icon d={icons.EXPORT} />
|
||||
</button>
|
||||
<Menu open={exportOpen} onClose={() => setExportOpen(false)}>
|
||||
<button onClick={() => handleExport("pdf")}>Export PDF…</button>
|
||||
@@ -339,21 +340,21 @@ export function EditorView() {
|
||||
<button className="icon-btn" onClick={toggleTheme} title={theme === "dark" ? "Light mode" : "Dark mode"}>
|
||||
{theme === "dark" ? (
|
||||
<Icon>
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<path d="M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4" />
|
||||
<circle cx={icons.SUN_DISC.cx} cy={icons.SUN_DISC.cy} r={icons.SUN_DISC.r} />
|
||||
<path d={icons.SUN_RAYS} />
|
||||
</Icon>
|
||||
) : (
|
||||
<Icon d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
|
||||
<Icon d={icons.MOON} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<button className="icon-btn" data-on={dock} onClick={toggleDock} title="Toggle preview">
|
||||
<Icon d="M3 4.5h18v15H3zM14 4.5v15" />
|
||||
<Icon d={icons.DOCK} />
|
||||
</button>
|
||||
{isCompact && (
|
||||
<div className="menu-wrap">
|
||||
<button className="icon-btn" data-on={moreOpen} onClick={() => setMoreOpen((v) => !v)} title="More" aria-label="More options">
|
||||
<Icon d="M5 12h.01M12 12h.01M19 12h.01" />
|
||||
<Icon d={icons.MORE} />
|
||||
</button>
|
||||
<Menu open={moreOpen} onClose={() => setMoreOpen(false)}>
|
||||
<div className="menu-label">Editor width</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
fontStack,
|
||||
pairingFor,
|
||||
} from "../model/fonts";
|
||||
import { icons } from "margin-shared";
|
||||
import { useEscapeLayer } from "../escape";
|
||||
import { useFocusTrap } from "../focus";
|
||||
import { Icon } from "./Icon";
|
||||
@@ -128,7 +129,7 @@ export function Settings({ onClose, onSave }: { onClose: () => void; onSave: ()
|
||||
<div className="panel-head">
|
||||
<h2>Book setup</h2>
|
||||
<button className="icon-btn" onClick={onClose} title="Close">
|
||||
<Icon d="M6 6l12 12M18 6L6 18" />
|
||||
<Icon d={icons.CLOSE} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
|
||||
+6
-2
@@ -1,7 +1,7 @@
|
||||
import type { JSONContent } from "@tiptap/core";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { type Book, type FigurePlacement, TRIM_DIMS, FIGURE_WIDTH, bodyNumber, chapterKind, isResizablePlacement, partNumber, partRoman } from "../model/book";
|
||||
import { fontStack, fontsUsed } from "../model/fonts";
|
||||
import { bundledFont, fontStack, fontsUsed } from "../model/fonts";
|
||||
|
||||
export interface EpubFile {
|
||||
path: string;
|
||||
@@ -66,7 +66,11 @@ async function fetchFontBase64(file: string): Promise<string | null> {
|
||||
}
|
||||
|
||||
async function loadFontAssets(book: Book): Promise<FontAssets> {
|
||||
const used = fontsUsed(book.settings.fonts).bundled;
|
||||
// Ids from the shared helper, resolved back to records: an EPUB embeds the font files, so this
|
||||
// is the one caller that needs a face's filenames rather than its name.
|
||||
const used = fontsUsed(book.settings.fonts)
|
||||
.bundled.map((id) => bundledFont(id))
|
||||
.filter((font) => font !== undefined);
|
||||
const files: EpubFile[] = [];
|
||||
const faces: string[] = [];
|
||||
const manifest: string[] = [];
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ export interface PdfFonts {
|
||||
|
||||
function pdfFonts(book: Book): PdfFonts {
|
||||
const used = fontsUsed(book.settings.fonts);
|
||||
return { bundled: used.bundled.map((f) => f.id), system: used.system };
|
||||
return { bundled: used.bundled, system: used.system };
|
||||
}
|
||||
|
||||
const TRIM: Record<TrimSize, { w: string; h: string }> = {
|
||||
|
||||
+37
-102
@@ -1,106 +1,41 @@
|
||||
export type FontCategory = "serif" | "sans" | "display";
|
||||
// The faces a book can be set in.
|
||||
//
|
||||
// The catalogue itself is margin-shared's, because Margin and Margin Docs have to offer the same
|
||||
// six families under the same ids: a book and a document set in "EB Garamond" must mean the same
|
||||
// file, and the two apps drifting apart on which faces exist is how one of them ends up exporting a
|
||||
// PDF in a fallback. What is left here is the one thing that is this app's own, which is the noun.
|
||||
|
||||
export interface BundledFont {
|
||||
id: string;
|
||||
label: string;
|
||||
family: string;
|
||||
category: FontCategory;
|
||||
regular: string;
|
||||
italic?: string;
|
||||
weight: string;
|
||||
}
|
||||
export type {
|
||||
BundledFont,
|
||||
FontCategory,
|
||||
FontPairing,
|
||||
FontRef,
|
||||
} from "margin-shared/fonts";
|
||||
|
||||
export const BUNDLED_FONTS: BundledFont[] = [
|
||||
{ id: "literata", label: "Literata", family: "Literata", category: "serif", regular: "Literata-VF.ttf", italic: "Literata-Italic-VF.ttf", weight: "200 900" },
|
||||
{ id: "eb-garamond", label: "EB Garamond", family: "EB Garamond", category: "serif", regular: "EBGaramond-VF.ttf", italic: "EBGaramond-Italic-VF.ttf", weight: "400 800" },
|
||||
{ id: "lora", label: "Lora", family: "Lora", category: "serif", regular: "Lora-VF.ttf", italic: "Lora-Italic-VF.ttf", weight: "400 700" },
|
||||
{ id: "source-serif", label: "Source Serif 4", family: "Source Serif 4", category: "serif", regular: "SourceSerif4-VF.ttf", italic: "SourceSerif4-Italic-VF.ttf", weight: "200 900" },
|
||||
{ id: "fraunces", label: "Fraunces", family: "Fraunces", category: "display", regular: "Fraunces-VF.ttf", italic: "Fraunces-Italic-VF.ttf", weight: "100 900" },
|
||||
{ id: "hanken", label: "Hanken Grotesk", family: "Hanken Grotesk", category: "sans", regular: "HankenGrotesk-VF.ttf", italic: "HankenGrotesk-Italic-VF.ttf", weight: "100 900" },
|
||||
];
|
||||
export {
|
||||
BUNDLED_FONTS,
|
||||
DEFAULT_FONTS,
|
||||
FONT_PAIRINGS,
|
||||
bundledFont,
|
||||
decodeRef,
|
||||
encodeRef,
|
||||
fontFamilyName,
|
||||
fontLabel,
|
||||
fontStack,
|
||||
fontsEqual,
|
||||
fontsUsed,
|
||||
isFontRef,
|
||||
pairingFor,
|
||||
refsEqual,
|
||||
} from "margin-shared/fonts";
|
||||
|
||||
export type FontRef =
|
||||
| { kind: "bundled"; id: string }
|
||||
| { kind: "system"; family: string };
|
||||
import type { FontPair } from "margin-shared/fonts";
|
||||
|
||||
export interface BookFonts {
|
||||
body: FontRef;
|
||||
heading: FontRef;
|
||||
}
|
||||
|
||||
export interface FontPairing {
|
||||
id: string;
|
||||
label: string;
|
||||
body: FontRef;
|
||||
heading: FontRef;
|
||||
}
|
||||
|
||||
const bundled = (id: string): FontRef => ({ kind: "bundled", id });
|
||||
|
||||
export const FONT_PAIRINGS: FontPairing[] = [
|
||||
{ id: "quiet-press", label: "Quiet Press", body: bundled("literata"), heading: bundled("literata") },
|
||||
{ id: "classic", label: "Classic", body: bundled("eb-garamond"), heading: bundled("eb-garamond") },
|
||||
{ id: "editorial", label: "Editorial", body: bundled("source-serif"), heading: bundled("fraunces") },
|
||||
{ id: "modern", label: "Modern", body: bundled("lora"), heading: bundled("hanken") },
|
||||
{ id: "contrast", label: "Contrast", body: bundled("literata"), heading: bundled("hanken") },
|
||||
];
|
||||
|
||||
export const DEFAULT_FONTS: BookFonts = { body: bundled("literata"), heading: bundled("literata") };
|
||||
|
||||
const SERIF_FALLBACK = `Georgia, "Times New Roman", serif`;
|
||||
const SANS_FALLBACK = `ui-sans-serif, system-ui, -apple-system, sans-serif`;
|
||||
|
||||
export function bundledFont(id: string): BundledFont | undefined {
|
||||
return BUNDLED_FONTS.find((f) => f.id === id);
|
||||
}
|
||||
|
||||
export function encodeRef(ref: FontRef): string {
|
||||
return ref.kind === "bundled" ? `b:${ref.id}` : `s:${ref.family}`;
|
||||
}
|
||||
|
||||
export function decodeRef(value: string): FontRef {
|
||||
return value.startsWith("s:") ? { kind: "system", family: value.slice(2) } : { kind: "bundled", id: value.slice(2) };
|
||||
}
|
||||
|
||||
export function fontFamilyName(ref: FontRef): string {
|
||||
if (ref.kind === "system") return ref.family;
|
||||
return bundledFont(ref.id)?.family ?? "Literata";
|
||||
}
|
||||
|
||||
export function fontLabel(ref: FontRef): string {
|
||||
if (ref.kind === "system") return ref.family;
|
||||
return bundledFont(ref.id)?.label ?? ref.id;
|
||||
}
|
||||
|
||||
export function fontStack(ref: FontRef): string {
|
||||
if (ref.kind === "bundled") {
|
||||
const font = bundledFont(ref.id);
|
||||
if (!font) return SERIF_FALLBACK;
|
||||
return `"${font.family}", ${font.category === "sans" ? SANS_FALLBACK : SERIF_FALLBACK}`;
|
||||
}
|
||||
return `"${ref.family}", ${SERIF_FALLBACK}`;
|
||||
}
|
||||
|
||||
export function refsEqual(a: FontRef, b: FontRef): boolean {
|
||||
if (a.kind !== b.kind) return false;
|
||||
return a.kind === "bundled" ? a.id === (b as { id: string }).id : a.family === (b as { family: string }).family;
|
||||
}
|
||||
|
||||
export function pairingFor(fonts: BookFonts): string | null {
|
||||
const match = FONT_PAIRINGS.find((p) => refsEqual(p.body, fonts.body) && refsEqual(p.heading, fonts.heading));
|
||||
return match?.id ?? null;
|
||||
}
|
||||
|
||||
export function fontsUsed(fonts: BookFonts): { bundled: BundledFont[]; system: string[] } {
|
||||
const usedBundled = new Map<string, BundledFont>();
|
||||
const usedSystem = new Set<string>();
|
||||
for (const ref of [fonts.body, fonts.heading]) {
|
||||
if (ref.kind === "bundled") {
|
||||
const font = bundledFont(ref.id);
|
||||
if (font) usedBundled.set(font.id, font);
|
||||
} else {
|
||||
usedSystem.add(ref.family);
|
||||
}
|
||||
}
|
||||
return { bundled: [...usedBundled.values()], system: [...usedSystem] };
|
||||
}
|
||||
/**
|
||||
* What one book is set in.
|
||||
*
|
||||
* An alias rather than a second interface, so every call site in this app keeps reading in the
|
||||
* app's own noun while the shape stays the shared one. Margin Docs calls the same pair
|
||||
* `DocumentFonts`.
|
||||
*/
|
||||
export type BookFonts = FontPair;
|
||||
+4
-95
@@ -1,95 +1,4 @@
|
||||
@font-face {
|
||||
font-family: "Literata";
|
||||
src: url("/fonts/Literata-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Literata";
|
||||
src: url("/fonts/Literata-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Hanken Grotesk";
|
||||
src: url("/fonts/HankenGrotesk-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Hanken Grotesk";
|
||||
src: url("/fonts/HankenGrotesk-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "EB Garamond";
|
||||
src: url("/fonts/EBGaramond-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 800;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "EB Garamond";
|
||||
src: url("/fonts/EBGaramond-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 800;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/fonts/Lora-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 700;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Lora";
|
||||
src: url("/fonts/Lora-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 400 700;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Serif 4";
|
||||
src: url("/fonts/SourceSerif4-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Serif 4";
|
||||
src: url("/fonts/SourceSerif4-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 200 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Fraunces";
|
||||
src: url("/fonts/Fraunces-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Fraunces";
|
||||
src: url("/fonts/Fraunces-Italic-VF.ttf") format("truetype-variations");
|
||||
font-weight: 100 900;
|
||||
font-style: italic;
|
||||
font-display: block;
|
||||
}
|
||||
/* The six families, declared once in margin-shared so that Margin and Margin Docs bind the same
|
||||
files to the same family names. The faces themselves are served from this app's public/fonts,
|
||||
which `pnpm fonts:sync` fills from the package and `pnpm fonts:check` verifies. */
|
||||
@import "margin-shared/css/fonts.css";
|
||||
+6
-77
@@ -1,80 +1,9 @@
|
||||
/* The palette, the type scale and the shape constants come from margin-shared, which is the one
|
||||
copy of the values both apps have to agree on. */
|
||||
@import "margin-shared/css/tokens.css";
|
||||
|
||||
/* The one token only this app has: the width of the preview dock, which Margin Docs has no
|
||||
equivalent of. */
|
||||
:root {
|
||||
--font-ui: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
--font-book: "Literata", Georgia, "Times New Roman", serif;
|
||||
--font-heading: "Literata", Georgia, "Times New Roman", serif;
|
||||
|
||||
--r-sm: 5px;
|
||||
--r-md: 8px;
|
||||
--r-lg: 12px;
|
||||
|
||||
--pane-sidebar: 248px;
|
||||
--pane-dock: 384px;
|
||||
--measure: 46em;
|
||||
--titlebar-h: 46px;
|
||||
|
||||
--t-1: 11px;
|
||||
--t-2: 12px;
|
||||
--t-3: 13px;
|
||||
--t-4: 15px;
|
||||
|
||||
--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||
}
|
||||
|
||||
:root,
|
||||
:root[data-theme="light"] {
|
||||
--paper: #fcfbf7;
|
||||
--shell: #f1ece2;
|
||||
--sidebar: #ece6da;
|
||||
--raised: #fbfaf6;
|
||||
|
||||
--ink: #23201b;
|
||||
--ink-soft: #6b6458;
|
||||
--ink-faint: #9b9484;
|
||||
|
||||
--line: #e3ddce;
|
||||
--line-strong: #d6cfbd;
|
||||
|
||||
--accent: #2a2622;
|
||||
--accent-ink: #100e0b;
|
||||
--accent-wash: rgba(35, 32, 27, 0.08);
|
||||
--accent-contrast: #faf7f0;
|
||||
--selection: rgba(35, 32, 27, 0.14);
|
||||
--glass: rgba(252, 251, 247, 0.86);
|
||||
|
||||
--danger: #b4453a;
|
||||
--danger-ink: #963327;
|
||||
--danger-wash: rgba(180, 69, 58, 0.1);
|
||||
--danger-contrast: #faf7f0;
|
||||
|
||||
--shadow-page: 0 1px 2px rgba(35, 32, 27, 0.06), 0 14px 30px rgba(35, 32, 27, 0.09);
|
||||
--shadow-pop: 0 8px 24px rgba(35, 32, 27, 0.14);
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--paper: #1d1a16;
|
||||
--shell: #151310;
|
||||
--sidebar: #1a1713;
|
||||
--raised: #232019;
|
||||
|
||||
--ink: #ece6da;
|
||||
--ink-soft: #a89f8e;
|
||||
--ink-faint: #756d5e;
|
||||
|
||||
--line: #2c2823;
|
||||
--line-strong: #3a352d;
|
||||
|
||||
--accent: #efe9dc;
|
||||
--accent-ink: #ffffff;
|
||||
--accent-wash: rgba(239, 233, 220, 0.12);
|
||||
--accent-contrast: #1a1714;
|
||||
--selection: rgba(239, 233, 220, 0.18);
|
||||
--glass: rgba(33, 30, 25, 0.88);
|
||||
|
||||
--danger: #d97a6f;
|
||||
--danger-ink: #e58e84;
|
||||
--danger-wash: rgba(217, 122, 111, 0.16);
|
||||
--danger-contrast: #1a1714;
|
||||
|
||||
--shadow-page: 0 1px 2px rgba(0, 0, 0, 0.4), 0 14px 34px rgba(0, 0, 0, 0.5);
|
||||
--shadow-pop: 0 8px 24px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
Reference in new issue
Block a user