diff --git a/.changeset/poor-dodos-lick.md b/.changeset/poor-dodos-lick.md new file mode 100644 index 000000000..ca02d7382 --- /dev/null +++ b/.changeset/poor-dodos-lick.md @@ -0,0 +1,5 @@ +--- +"@gitbook/fonts": minor +--- + +Initial version of the package diff --git a/.changeset/warm-roses-sleep.md b/.changeset/warm-roses-sleep.md new file mode 100644 index 000000000..e14b06cdc --- /dev/null +++ b/.changeset/warm-roses-sleep.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Fix ogimage using incorrect Google Font depending on language. diff --git a/bun.lock b/bun.lock index e3d406e11..3ff6d61c4 100644 --- a/bun.lock +++ b/bun.lock @@ -26,7 +26,7 @@ "name": "@gitbook/cache-tags", "version": "0.3.1", "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "assert-never": "^1.2.1", }, "devDependencies": { @@ -47,15 +47,27 @@ "emoji-assets": "^8.0.0", }, }, + "packages/fonts": { + "name": "@gitbook/fonts", + "version": "0.0.0", + "dependencies": { + "@gitbook/api": "catalog:", + }, + "devDependencies": { + "google-font-metadata": "^6.0.3", + "typescript": "^5.5.3", + }, + }, "packages/gitbook": { "name": "gitbook", "version": "0.12.0", "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/cache-do": "workspace:*", "@gitbook/cache-tags": "workspace:*", "@gitbook/colors": "workspace:*", "@gitbook/emoji-codepoints": "workspace:*", + "@gitbook/fonts": "workspace:*", "@gitbook/icons": "workspace:*", "@gitbook/openapi-parser": "workspace:*", "@gitbook/react-contentkit": "workspace:*", @@ -150,7 +162,7 @@ "name": "gitbook-v2", "version": "0.3.0", "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/cache-tags": "workspace:*", "@opennextjs/cloudflare": "1.2.1", "@sindresorhus/fnv1a": "^3.1.0", @@ -209,7 +221,7 @@ "name": "@gitbook/react-contentkit", "version": "0.7.0", "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/icons": "workspace:*", "classnames": "^2.5.1", }, @@ -267,10 +279,12 @@ }, "overrides": { "@codemirror/state": "6.4.1", - "@gitbook/api": "^0.120.0", "react": "^19.0.0", "react-dom": "^19.0.0", }, + "catalog": { + "@gitbook/api": "^0.120.0", + }, "packages": { "@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="], @@ -604,6 +618,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="], + "@evan/concurrency": ["@evan/concurrency@0.0.3", "", {}, "sha512-vjhkm2nrXoM39G4aP/U4CC5vFv/ZlMRSjbTII0N65J9R0EpgjdGswnHKS1KSjfGAp/9zKSNaojBgi0SxKnGapw=="], + "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], "@floating-ui/core": ["@floating-ui/core@1.6.8", "", { "dependencies": { "@floating-ui/utils": "^0.2.8" } }, "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA=="], @@ -644,6 +660,8 @@ "@gitbook/fontawesome-pro": ["@gitbook/fontawesome-pro@1.0.8", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "^6.6.0" } }, "sha512-i4PgiuGyUb52Muhc52kK3aMJIMfMkA2RbPW30tre8a6M8T6mWTfYo6gafSgjNvF1vH29zcuB8oBYnF0gO4XcHA=="], + "@gitbook/fonts": ["@gitbook/fonts@workspace:packages/fonts"], + "@gitbook/icons": ["@gitbook/icons@workspace:packages/icons"], "@gitbook/openapi-parser": ["@gitbook/openapi-parser@workspace:packages/openapi-parser"], @@ -798,6 +816,22 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@octokit/auth-token": ["@octokit/auth-token@5.1.2", "", {}, "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw=="], + + "@octokit/core": ["@octokit/core@6.1.5", "", { "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", "@octokit/request": "^9.2.3", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" } }, "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg=="], + + "@octokit/endpoint": ["@octokit/endpoint@10.1.4", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA=="], + + "@octokit/graphql": ["@octokit/graphql@8.2.2", "", { "dependencies": { "@octokit/request": "^9.2.3", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA=="], + + "@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], + + "@octokit/request": ["@octokit/request@9.2.3", "", { "dependencies": { "@octokit/endpoint": "^10.1.4", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w=="], + + "@octokit/request-error": ["@octokit/request-error@6.1.8", "", { "dependencies": { "@octokit/types": "^14.0.0" } }, "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ=="], + + "@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="], + "@opennextjs/aws": ["@opennextjs/aws@3.6.5", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "cookie": "^1.0.2", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-wni+CWlRCyWfhNfekQBBPPkrDDnaGdZLN9hMybKI0wKOKTO+zhPOqR65Eh3V0pzWAi84Sureb5mdMuLwCxAAcw=="], "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.2.1", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.6.5", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.19.1" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-cOco+nHwlo/PLB1bThF8IIvaS8PgAT9MEI5ZttFO/qt6spgvr2lUaPkpjgSIQmI3sBIEG2cLUykvQ2nbbZEcVw=="], @@ -1470,6 +1504,8 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "before-after-hook": ["before-after-hook@3.0.2", "", {}, "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="], + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], "bignumber.js": ["bignumber.js@9.1.2", "", {}, "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="], @@ -1482,6 +1518,8 @@ "body-parser": ["body-parser@2.0.2", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "3.1.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.5.2", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "^3.0.0", "type-is": "~1.6.18" } }, "sha512-SNMk0OONlQ01uk8EPeiBvTW7W4ovpL5b1O3t1sjpPgfxOQ6BqQJ6XjxinDPR79Z6HdcD5zBBwr5ssiTlgdNztQ=="], + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], "boxen": ["boxen@4.2.0", "", { "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^5.3.1", "chalk": "^3.0.0", "cli-boxes": "^2.2.0", "string-width": "^4.1.0", "term-size": "^2.1.0", "type-fest": "^0.8.1", "widest-line": "^3.1.0" } }, "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ=="], @@ -1506,6 +1544,8 @@ "bytes": ["bytes@3.1.0", "", {}, "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + "cacheable": ["cacheable@1.8.9", "", { "dependencies": { "hookified": "^1.7.1", "keyv": "^5.3.1" } }, "sha512-FicwAUyWnrtnd4QqYAoRlNs44/a1jTL7XDKqm5gJ90wz1DQPlC7U2Rd1Tydpv+E7WAr4sQHuw8Q8M3nZMAyecQ=="], "cacheable-request": ["cacheable-request@6.1.0", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^3.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^4.1.0", "responselike": "^1.0.2" } }, "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg=="], @@ -1602,10 +1642,16 @@ "css-functions-list": ["css-functions-list@3.2.3", "", {}, "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA=="], + "css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="], + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], + "css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + "cssom": ["cssom@0.5.0", "", {}, "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "cva": ["cva@1.0.0-beta.2", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "typescript": ">= 4.5.5 < 6" }, "optionalPeers": ["typescript"] }, "sha512-dqcOFe247I5pKxfuzqfq3seLL5iMYsTgo40Uw7+pKZAntPgFtR7Tmy59P5IVIq/XgB0NQWoIvYDt9TwHkuK8Cg=="], @@ -1662,6 +1708,14 @@ "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], "dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], @@ -1694,7 +1748,7 @@ "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "env-cmd": ["env-cmd@10.1.0", "", { "dependencies": { "commander": "^4.0.0", "cross-spawn": "^7.0.0" }, "bin": { "env-cmd": "bin/env-cmd.js" } }, "sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA=="], @@ -1800,6 +1854,8 @@ "external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="], + "fast-content-type-parse": ["fast-content-type-parse@2.0.1", "", {}, "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], @@ -1904,6 +1960,8 @@ "google-auth-library": ["google-auth-library@5.10.1", "", { "dependencies": { "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "fast-text-encoding": "^1.0.0", "gaxios": "^2.1.0", "gcp-metadata": "^3.4.0", "gtoken": "^4.1.0", "jws": "^4.0.0", "lru-cache": "^5.0.0" } }, "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg=="], + "google-font-metadata": ["google-font-metadata@6.0.3", "", { "dependencies": { "@evan/concurrency": "^0.0.3", "@octokit/core": "^6.1.2", "cac": "^6.7.14", "consola": "^3.3.3", "deepmerge": "^4.3.1", "json-stringify-pretty-compact": "^4.0.0", "linkedom": "^0.18.6", "pathe": "^1.1.2", "picocolors": "^1.1.1", "playwright": "^1.49.1", "stylis": "^4.3.4", "zod": "^3.24.1" }, "bin": { "gfm": "dist/cli.mjs" } }, "sha512-62+QB+nmBKppHrxwvILZkV/EvKctEAdzkKBIJY26TtwZkUjeEqmAQnE5uHvtMTdB8odqGpQu5LAKPqqUYHI9wA=="], + "google-p12-pem": ["google-p12-pem@2.0.5", "", { "dependencies": { "node-forge": "^0.10.0" }, "bin": { "gp12-pem": "build/src/bin/gp12-pem.js" } }, "sha512-7RLkxwSsMsYh9wQ5Vb2zRtkAHvqPvfoMGag+nugl1noYO7gf0844Yr9TIFA5NEBMAeVt2Z+Imu7CQMp3oNatzQ=="], "googleapis": ["googleapis@47.0.0", "", { "dependencies": { "google-auth-library": "^5.6.1", "googleapis-common": "^3.2.0" } }, "sha512-+Fnjgcc3Na/rk57dwxqW1V0HJXJFjnt3aqFlckULqAqsPkmex/AyJJe6MSlXHC37ZmlXEb9ZtPGUp5ZzRDXpHg=="], @@ -1980,12 +2038,16 @@ "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], + "html-tags": ["html-tags@3.3.1", "", {}, "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ=="], "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], "html-whitespace-sensitive-tag-names": ["html-whitespace-sensitive-tag-names@3.0.1", "", {}, "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA=="], + "htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="], + "http-cache-semantics": ["http-cache-semantics@4.1.1", "", {}, "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="], "http-errors": ["http-errors@1.4.0", "", { "dependencies": { "inherits": "2.0.1", "statuses": ">= 1.2.1 < 2" } }, "sha512-oLjPqve1tuOl5aRhv8GK5eHpqP1C9fb+Ol+XTLjKfLltE44zdDbEdjPSbU7Ch5rSNsVFqZn97SrMmZLdu1/YMw=="], @@ -2096,6 +2158,8 @@ "json-stringify-deterministic": ["json-stringify-deterministic@1.0.12", "", {}, "sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g=="], + "json-stringify-pretty-compact": ["json-stringify-pretty-compact@4.0.0", "", {}, "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="], + "json-xml-parse": ["json-xml-parse@1.3.0", "", {}, "sha512-MVosauc/3W2wL4dd4yaJzH5oXw+HOUfptn0+d4+bFghMiJFop7MaqIwFXJNLiRnNYJNQ6L4o7B+53n5wcvoLFw=="], "jsondiffpatch": ["jsondiffpatch@0.6.0", "", { "dependencies": { "@types/diff-match-patch": "^1.0.36", "chalk": "^5.3.0", "diff-match-patch": "^1.0.5" }, "bin": { "jsondiffpatch": "bin/jsondiffpatch.js" } }, "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ=="], @@ -2134,6 +2198,8 @@ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "linkedom": ["linkedom@0.18.11", "", { "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "uhyphen": "^0.2.0" } }, "sha512-K03GU3FUlnhBAP0jPb7tN7YJl7LbjZx30Z8h6wgLXusnKF7+BEZvfEbdkN/lO9LfFzxN3S0ZAriDuJ/13dIsLA=="], + "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], "lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="], @@ -2364,6 +2430,8 @@ "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "nuqs": ["nuqs@2.2.3", "", { "dependencies": { "mitt": "^3.0.1" }, "peerDependencies": { "@remix-run/react": ">=2", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router-dom": ">=6" }, "optionalPeers": ["@remix-run/react", "next", "react-router-dom"] }, "sha512-nMCcUW06KSqEXA0xp+LiRqDpIE59BVYbjZLe0HUisJAlswfihHYSsAjYTzV0lcE1thfh8uh+LqUHGdQ8qq8rfA=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2454,7 +2522,7 @@ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "pcre-to-regexp": ["pcre-to-regexp@1.1.0", "", {}, "sha512-KF9XxmUQJ2DIlMj3TqNqY1AWvyvTuIuq11CuuekxyaYMiFuMKGgQrePYMX5bXKLhLG3sDI4CsGAYHPaT7VV7+g=="], @@ -2722,6 +2790,8 @@ "stylelint": ["stylelint@16.16.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "@csstools/media-query-list-parser": "^4.0.2", "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", "css-tree": "^3.1.0", "debug": "^4.3.7", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^10.0.7", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.3", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "supports-hyperlinks": "^3.2.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^5.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-40X5UOb/0CEFnZVEHyN260HlSSUxPES+arrUphOumGWgXERHfwCD0kNBVILgQSij8iliYVwlc0V7M5bcLP9vPg=="], + "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="], + "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -2826,6 +2896,8 @@ "ufo": ["ufo@1.5.4", "", {}, "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="], + "uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="], + "uid-promise": ["uid-promise@1.0.0", "", {}, "sha512-R8375j0qwXyIu/7R0tjdF06/sElHqbmdmWC9M2qQHpEVbvE4I5+38KJI7LUUmQMp7NVq4tKHiBMkT0NFM453Ig=="], "undici": ["undici@5.28.4", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g=="], @@ -2852,6 +2924,8 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], @@ -4014,6 +4088,8 @@ "@vercel/static-config/ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="], + "@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "@vue/compiler-sfc/postcss": ["postcss@8.4.47", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" } }, "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ=="], "@vueuse/integrations/@vueuse/core": ["@vueuse/core@11.2.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "11.2.0", "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" } }, "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA=="], @@ -4062,6 +4138,8 @@ "decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "edge-runtime/async-listen": ["async-listen@3.0.1", "", {}, "sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA=="], "edge-runtime/picocolors": ["picocolors@1.0.0", "", {}, "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="], @@ -4110,6 +4188,8 @@ "google-auth-library/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "google-font-metadata/picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "googleapis-common/uuid": ["uuid@7.0.3", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="], "got/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="], @@ -4178,6 +4258,8 @@ "package-json/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "parse5/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "path-match/path-to-regexp": ["path-to-regexp@1.9.0", "", { "dependencies": { "isarray": "0.0.1" } }, "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g=="], "path-scurry/lru-cache": ["lru-cache@11.0.2", "", {}, "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA=="], @@ -4270,6 +4352,8 @@ "ts-node/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="], + "unenv/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "update-notifier/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], "url-parse-lax/prepend-http": ["prepend-http@2.0.0", "", {}, "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="], diff --git a/package.json b/package.json index dbb94b88e..1faa750ba 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,9 @@ "turbo": "^2.5.0", "vercel": "^39.3.0" }, - "packageManager": "bun@1.2.11", + "packageManager": "bun@1.2.15", "overrides": { "@codemirror/state": "6.4.1", - "@gitbook/api": "^0.120.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -34,7 +33,12 @@ "download:env": "op read op://gitbook-x-dev/gitbook-open/.env.local >> .env.local", "clean": "turbo run clean" }, - "workspaces": ["packages/*"], + "workspaces": { + "packages": ["packages/*"], + "catalog": { + "@gitbook/api": "^0.120.0" + } + }, "patchedDependencies": { "decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch", "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch" diff --git a/packages/cache-tags/package.json b/packages/cache-tags/package.json index eafeabc44..a57048f2f 100644 --- a/packages/cache-tags/package.json +++ b/packages/cache-tags/package.json @@ -10,7 +10,7 @@ }, "version": "0.3.1", "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "assert-never": "^1.2.1" }, "devDependencies": { diff --git a/packages/fonts/.gitignore b/packages/fonts/.gitignore new file mode 100644 index 000000000..253839862 --- /dev/null +++ b/packages/fonts/.gitignore @@ -0,0 +1,2 @@ +dist/ +src/data/*.json diff --git a/packages/fonts/README.md b/packages/fonts/README.md new file mode 100644 index 000000000..fef253f27 --- /dev/null +++ b/packages/fonts/README.md @@ -0,0 +1,3 @@ +# `@gitbook/fonts` + +Utilities to lookup default fonts supported by GitBook. diff --git a/packages/fonts/bin/generate.ts b/packages/fonts/bin/generate.ts new file mode 100644 index 000000000..d4326d615 --- /dev/null +++ b/packages/fonts/bin/generate.ts @@ -0,0 +1,91 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import { APIv2 } from 'google-font-metadata'; + +import { CustomizationDefaultFont } from '@gitbook/api'; + +import type { FontDefinitions } from '../src/types'; + +const googleFontsMap: { [fontName in CustomizationDefaultFont]: string } = { + [CustomizationDefaultFont.Inter]: 'inter', + [CustomizationDefaultFont.FiraSans]: 'fira-sans-extra-condensed', + [CustomizationDefaultFont.IBMPlexSerif]: 'ibm-plex-serif', + [CustomizationDefaultFont.Lato]: 'lato', + [CustomizationDefaultFont.Merriweather]: 'merriweather', + [CustomizationDefaultFont.NotoSans]: 'noto-sans', + [CustomizationDefaultFont.OpenSans]: 'open-sans', + [CustomizationDefaultFont.Overpass]: 'overpass', + [CustomizationDefaultFont.Poppins]: 'poppins', + [CustomizationDefaultFont.Raleway]: 'raleway', + [CustomizationDefaultFont.Roboto]: 'roboto', + [CustomizationDefaultFont.RobotoSlab]: 'roboto-slab', + [CustomizationDefaultFont.SourceSansPro]: 'source-sans-3', + [CustomizationDefaultFont.Ubuntu]: 'ubuntu', + [CustomizationDefaultFont.ABCFavorit]: 'inter', +}; + +/** + * Scripts to generate the list of all icons. + */ +async function main() { + // @ts-expect-error - we build the object + const output: FontDefinitions = {}; + + for (const font of Object.values(CustomizationDefaultFont)) { + const googleFontName = googleFontsMap[font]; + const fontMetadata = APIv2[googleFontName.toLowerCase()]; + if (!fontMetadata) { + throw new Error(`Font ${googleFontName} not found`); + } + + output[font] = { + font: googleFontName, + unicodeRange: fontMetadata.unicodeRange, + variants: { + '400': {}, + '700': {}, + }, + }; + + Object.keys(output[font].variants).forEach((weight) => { + const variants = fontMetadata.variants[weight]; + const normalVariant = variants.normal; + if (!normalVariant) { + throw new Error(`Font ${googleFontName} has no normal variant`); + } + + output[font].variants[weight] = {}; + Object.entries(normalVariant).forEach(([script, url]) => { + output[font].variants[weight][script] = url.url.woff; + }); + }); + } + + await writeDataFile('fonts', JSON.stringify(output, null, 2)); +} + +/** + * We write both in dist and src as the build process might have happen already + * and tsc doesn't copy the files. + */ +async function writeDataFile(name, content) { + const srcData = path.resolve(__dirname, '../src/data'); + const distData = path.resolve(__dirname, '../dist/data'); + + // Ensure the directories exists + await Promise.all([ + fs.mkdir(srcData, { recursive: true }), + fs.mkdir(distData, { recursive: true }), + ]); + + await Promise.all([ + fs.writeFile(path.resolve(srcData, `${name}.json`), content), + fs.writeFile(path.resolve(distData, `${name}.json`), content), + ]); +} + +main().catch((error) => { + console.error(`Error generating icons list: ${error}`); + process.exit(1); +}); diff --git a/packages/fonts/package.json b/packages/fonts/package.json new file mode 100644 index 000000000..dbfb2d7ca --- /dev/null +++ b/packages/fonts/package.json @@ -0,0 +1,31 @@ +{ + "name": "@gitbook/fonts", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "development": "./src/index.ts", + "default": "./dist/index.js" + } + }, + "version": "0.0.0", + "dependencies": { + "@gitbook/api": "catalog:" + }, + "devDependencies": { + "google-font-metadata": "^6.0.3", + "typescript": "^5.5.3" + }, + "scripts": { + "generate": "bun ./bin/generate.js", + "build": "tsc --project tsconfig.build.json", + "typecheck": "tsc --noEmit", + "dev": "tsc -w", + "clean": "rm -rf ./dist && rm -rf ./src/data", + "unit": "bun test" + }, + "files": ["dist", "src", "bin", "README.md", "CHANGELOG.md"], + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/fonts/src/__snapshots__/getDefaultFont.test.ts.snap b/packages/fonts/src/__snapshots__/getDefaultFont.test.ts.snap new file mode 100644 index 000000000..89184be22 --- /dev/null +++ b/packages/fonts/src/__snapshots__/getDefaultFont.test.ts.snap @@ -0,0 +1,57 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`getDefaultFont should return correct object for Latin text 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff", +} +`; + +exports[`getDefaultFont should return correct object for Cyrillic text 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff", +} +`; + +exports[`getDefaultFont should return correct object for Greek text 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZxhjp-Ek-_0ewmM.woff", +} +`; + +exports[`getDefaultFont should handle mixed script text 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZthjp-Ek-_0ewmM.woff", +} +`; + +exports[`getDefaultFont should handle different font weights: regular 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff", +} +`; + +exports[`getDefaultFont should handle different font weights: bold 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuFuYAZ9hjp-Ek-_0ew.woff", +} +`; + +exports[`getDefaultFont should handle different fonts: inter 1`] = ` +{ + "font": "Inter", + "url": "https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hjp-Ek-_0ew.woff", +} +`; + +exports[`getDefaultFont should handle different fonts: roboto 1`] = ` +{ + "font": "Roboto", + "url": "https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxMKTU1Kg.woff", +} +`; diff --git a/packages/fonts/src/fonts.ts b/packages/fonts/src/fonts.ts new file mode 100644 index 000000000..c9544efb1 --- /dev/null +++ b/packages/fonts/src/fonts.ts @@ -0,0 +1,5 @@ +import type { FontDefinitions } from './types'; + +import rawFonts from './data/fonts.json' with { type: 'json' }; + +export const fonts: FontDefinitions = rawFonts; diff --git a/packages/fonts/src/getDefaultFont.test.ts b/packages/fonts/src/getDefaultFont.test.ts new file mode 100644 index 000000000..58afbeac9 --- /dev/null +++ b/packages/fonts/src/getDefaultFont.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from 'bun:test'; +import { CustomizationDefaultFont } from '@gitbook/api'; +import { getDefaultFont } from './getDefaultFont'; + +describe('getDefaultFont', () => { + it('should return null for invalid font', () => { + const result = getDefaultFont({ + font: 'invalid-font' as CustomizationDefaultFont, + text: 'Hello', + weight: 400, + }); + expect(result).toBeNull(); + }); + + it('should return null for invalid weight', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello', + weight: 999 as any, + }); + expect(result).toBeNull(); + }); + + it('should return null for text not supported by any script', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: '😀', // Emoji not supported by Inter + weight: 400, + }); + expect(result).toBeNull(); + }); + + it('should return correct object for Latin text', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello World', + weight: 400, + }); + expect(result).not.toBeNull(); + expect(result?.font).toBe(CustomizationDefaultFont.Inter); + expect(result).toMatchSnapshot(); + }); + + it('should return correct object for Cyrillic text', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Привет мир', + weight: 400, + }); + expect(result).not.toBeNull(); + expect(result?.font).toBe(CustomizationDefaultFont.Inter); + expect(result).toMatchSnapshot(); + }); + + it('should return correct object for Greek text', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Γεια σας', + weight: 400, + }); + expect(result).not.toBeNull(); + expect(result?.font).toBe(CustomizationDefaultFont.Inter); + expect(result).toMatchSnapshot(); + }); + + it('should handle mixed script text', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello Привет', + weight: 400, + }); + expect(result).not.toBeNull(); + expect(result?.font).toBe(CustomizationDefaultFont.Inter); + expect(result).toMatchSnapshot(); + }); + + it('should handle different font weights', () => { + const regular = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello', + weight: 400, + }); + const bold = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello', + weight: 700, + }); + expect(regular).not.toBeNull(); + expect(bold).not.toBeNull(); + expect(regular).toMatchSnapshot('regular'); + expect(bold).toMatchSnapshot('bold'); + }); + + it('should handle empty string', () => { + const result = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: '', + weight: 400, + }); + expect(result).toBeNull(); + }); + + it('should handle different fonts', () => { + const inter = getDefaultFont({ + font: CustomizationDefaultFont.Inter, + text: 'Hello', + weight: 400, + }); + const roboto = getDefaultFont({ + font: CustomizationDefaultFont.Roboto, + text: 'Hello', + weight: 400, + }); + expect(inter).not.toBeNull(); + expect(roboto).not.toBeNull(); + expect(inter).toMatchSnapshot('inter'); + expect(roboto).toMatchSnapshot('roboto'); + }); +}); diff --git a/packages/fonts/src/getDefaultFont.ts b/packages/fonts/src/getDefaultFont.ts new file mode 100644 index 000000000..ea18e8bba --- /dev/null +++ b/packages/fonts/src/getDefaultFont.ts @@ -0,0 +1,112 @@ +import type { CustomizationDefaultFont } from '@gitbook/api'; +import { fonts } from './fonts'; +import type { FontWeight } from './types'; + +/** + * Get the URL to load a font for a text. + */ +export function getDefaultFont(input: { + /** + * GitBook font to use. + */ + font: CustomizationDefaultFont; + + /** + * Text to display with the font. + */ + text: string; + + /** + * Font weight to use. + */ + weight: FontWeight; +}): { font: string; url: string } | null { + if (!input.text.trim()) { + return null; + } + + const fontDefinition = fonts[input.font]; + if (!fontDefinition) { + return null; + } + + const variant = fontDefinition.variants[`${input.weight}`]; + if (!variant) { + return null; + } + + const script = getBestUnicodeRange(input.text, fontDefinition.unicodeRange); + if (!script) { + return null; + } + + return variant[script] + ? { + font: input.font, + url: variant[script], + } + : null; +} + +/** + * Determine which named @font-face unicode-range covers + * the greatest share of the characters in `text`. + * + * @param text The text you want to inspect. + * @param ranges An object whose keys are range names and whose + * values are CSS-style comma-separated unicode-range + * declarations (e.g. "U+0370-03FF,U+1F00-1FFF"). + * @returns The key of the best-matching range, or `null` + * when nothing matches at all. + */ +function getBestUnicodeRange(text: string, ranges: Record): string | null { + // ---------- helper: parse "U+XXXX" or "U+XXXX-YYYY" ---------- + const parseOne = (token: string): [number, number] | null => { + token = token.trim().toUpperCase(); + if (!token.startsWith('U+')) return null; + + const body = token.slice(2); // drop "U+" + const [startHex, endHex] = body.split('-'); + const start = Number.parseInt(startHex, 16); + const end = endHex ? Number.parseInt(endHex, 16) : start; + + if (Number.isNaN(start) || Number.isNaN(end) || end < start) return null; + return [start, end]; + }; + + // ---------- helper: build lookup table ---------- + const parsed: Record = {}; + for (const [label, list] of Object.entries(ranges)) { + parsed[label] = list + .split(',') + .map(parseOne) + .filter((x): x is [number, number] => x !== null); + } + + // ---------- tally code-point hits ---------- + const hits: Record = Object.fromEntries(Object.keys(parsed).map((k) => [k, 0])); + + for (let i = 0; i < text.length; ) { + const cp = text.codePointAt(i)!; + i += cp > 0xffff ? 2 : 1; // advance by 1 UTF-16 code-unit (or 2 for surrogates) + + for (const [label, rangesArr] of Object.entries(parsed)) { + if (rangesArr.some(([lo, hi]) => cp >= lo && cp <= hi)) { + hits[label]++; + } + } + } + + // ---------- choose the "best" ---------- + let winner: string | null = null; + let maxCount = 0; + + for (const [label, count] of Object.entries(hits)) { + if (count > maxCount) { + maxCount = count; + winner = label; + } + } + + return maxCount > 0 ? winner : null; +} diff --git a/packages/fonts/src/index.ts b/packages/fonts/src/index.ts new file mode 100644 index 000000000..fbb358489 --- /dev/null +++ b/packages/fonts/src/index.ts @@ -0,0 +1,2 @@ +export * from './getDefaultFont'; +export * from './types'; diff --git a/packages/fonts/src/types.ts b/packages/fonts/src/types.ts new file mode 100644 index 000000000..899b32c49 --- /dev/null +++ b/packages/fonts/src/types.ts @@ -0,0 +1,17 @@ +import type { CustomizationDefaultFont } from '@gitbook/api'; + +export type FontWeight = 400 | 700; + +export type FontDefinition = { + font: string; + unicodeRange: { + [script: string]: string; + }; + variants: { + [weight in string]: { + [script: string]: string; + }; + }; +}; + +export type FontDefinitions = { [fontName in CustomizationDefaultFont]: FontDefinition }; diff --git a/packages/fonts/tsconfig.build.json b/packages/fonts/tsconfig.build.json new file mode 100644 index 000000000..e4828bc1f --- /dev/null +++ b/packages/fonts/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "src/**/*.test.ts"] +} diff --git a/packages/fonts/tsconfig.json b/packages/fonts/tsconfig.json new file mode 100644 index 000000000..2b3fe87c5 --- /dev/null +++ b/packages/fonts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["es2023"], + "module": "ESNext", + "target": "es2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowJs": true, + "noEmit": false, + "declaration": true, + "outDir": "dist", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "types": [ + "bun-types" // add Bun global + ] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/fonts/turbo.json b/packages/fonts/turbo.json new file mode 100644 index 000000000..9097cda33 --- /dev/null +++ b/packages/fonts/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "tasks": { + "generate": { + "inputs": ["bin/**/*", "package.json"], + "outputs": ["src/data/*.json", "dist/data/*.json"] + } + } +} diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index 3993e746d..5723cdbc7 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -3,7 +3,7 @@ "version": "0.3.0", "private": true, "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/cache-tags": "workspace:*", "@opennextjs/cloudflare": "1.2.1", "@sindresorhus/fnv1a": "^3.1.0", diff --git a/packages/gitbook/package.json b/packages/gitbook/package.json index 74ef66a34..666cd114d 100644 --- a/packages/gitbook/package.json +++ b/packages/gitbook/package.json @@ -16,12 +16,13 @@ "clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math" }, "dependencies": { - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/cache-do": "workspace:*", "@gitbook/cache-tags": "workspace:*", "@gitbook/colors": "workspace:*", "@gitbook/emoji-codepoints": "workspace:*", "@gitbook/icons": "workspace:*", + "@gitbook/fonts": "workspace:*", "@gitbook/openapi-parser": "workspace:*", "@gitbook/react-contentkit": "workspace:*", "@gitbook/react-math": "workspace:*", diff --git a/packages/gitbook/src/routes/ogimage.tsx b/packages/gitbook/src/routes/ogimage.tsx index b086ffd1c..e55dcfc2d 100644 --- a/packages/gitbook/src/routes/ogimage.tsx +++ b/packages/gitbook/src/routes/ogimage.tsx @@ -1,5 +1,6 @@ import { CustomizationDefaultFont, CustomizationHeaderPreset } from '@gitbook/api'; import { colorContrast } from '@gitbook/colors'; +import { type FontWeight, getDefaultFont } from '@gitbook/fonts'; import { redirect } from 'next/navigation'; import { ImageResponse } from 'next/og'; @@ -11,24 +12,6 @@ import { getCacheTag } from '@gitbook/cache-tags'; import type { GitBookSiteContext } from '@v2/lib/context'; import { getResizedImageURL } from '@v2/lib/images'; -const googleFontsMap: { [fontName in CustomizationDefaultFont]: string } = { - [CustomizationDefaultFont.Inter]: 'Inter', - [CustomizationDefaultFont.FiraSans]: 'Fira Sans Extra Condensed', - [CustomizationDefaultFont.IBMPlexSerif]: 'IBM Plex Serif', - [CustomizationDefaultFont.Lato]: 'Lato', - [CustomizationDefaultFont.Merriweather]: 'Merriweather', - [CustomizationDefaultFont.NotoSans]: 'Noto Sans', - [CustomizationDefaultFont.OpenSans]: 'Open Sans', - [CustomizationDefaultFont.Overpass]: 'Overpass', - [CustomizationDefaultFont.Poppins]: 'Poppins', - [CustomizationDefaultFont.Raleway]: 'Raleway', - [CustomizationDefaultFont.Roboto]: 'Roboto', - [CustomizationDefaultFont.RobotoSlab]: 'Roboto Slab', - [CustomizationDefaultFont.SourceSansPro]: 'Source Sans 3', - [CustomizationDefaultFont.Ubuntu]: 'Ubuntu', - [CustomizationDefaultFont.ABCFavorit]: 'Inter', -}; - /** * Render the OpenGraph image for a site content. */ @@ -65,19 +48,15 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page const { fontFamily, fonts } = await (async () => { // google fonts if (typeof customization.styling.font === 'string') { - const fontFamily = googleFontsMap[customization.styling.font] ?? 'Inter'; + const fontFamily = customization.styling.font ?? CustomizationDefaultFont.Inter; const regularText = pageDescription; const boldText = `${contentTitle}${pageTitle}`; const fonts = ( await Promise.all([ - getWithCache(`google-font:${fontFamily}:400`, () => - loadGoogleFont({ fontFamily, text: regularText, weight: 400 }) - ), - getWithCache(`google-font:${fontFamily}:700`, () => - loadGoogleFont({ fontFamily, text: boldText, weight: 700 }) - ), + loadGoogleFont({ font: fontFamily, text: regularText, weight: 400 }), + loadGoogleFont({ font: fontFamily, text: boldText, weight: 700 }), ]) ).filter(filterOutNullable); @@ -260,37 +239,31 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page ); } -async function loadGoogleFont(input: { fontFamily: string; text: string; weight: 400 | 700 }) { - const { fontFamily, text, weight } = input; +async function loadGoogleFont(input: { + font: CustomizationDefaultFont; + text: string; + weight: FontWeight; +}) { + const lookup = getDefaultFont({ + font: input.font, + text: input.text, + weight: input.weight, + }); - if (!text.trim()) { - return null; - } - - const url = new URL('https://fonts.googleapis.com/css2'); - url.searchParams.set('family', `${fontFamily}:wght@${weight}`); - url.searchParams.set('text', text); - - const result = await fetch(url.href); - if (!result.ok) { - return null; - } - - const css = await result.text(); - const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/); - const resourceUrl = resource ? resource[1] : null; - - if (resourceUrl) { - const response = await fetch(resourceUrl); - if (response.ok) { - const data = await response.arrayBuffer(); - return { - name: fontFamily, - data, - style: 'normal' as const, - weight, - }; - } + // If we found a font file, load it + if (lookup) { + return getWithCache(`google-font-files:${lookup.url}`, async () => { + const response = await fetch(lookup.url); + if (response.ok) { + const data = await response.arrayBuffer(); + return { + name: lookup.font, + data, + style: 'normal' as const, + weight: input.weight, + }; + } + }); } // If for some reason we can't load the font, we'll just use the default one diff --git a/packages/icons/package.json b/packages/icons/package.json index 472b7a7b3..0e0b80212 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -32,7 +32,7 @@ }, "scripts": { "generate": "bun ./bin/gen-list.js", - "build": "tsc", + "build": "tsc --project tsconfig.build.json", "typecheck": "tsc --noEmit", "dev": "tsc -w", "clean": "rm -rf ./dist && rm -rf ./src/data", @@ -41,7 +41,7 @@ "bin": { "gitbook-icons": "./bin/gitbook-icons.js" }, - "files": ["dist", "src", "bin", "data", "README.md", "CHANGELOG.md"], + "files": ["dist", "src", "bin", "README.md", "CHANGELOG.md"], "engines": { "node": ">=20.0.0" } diff --git a/packages/icons/tsconfig.build.json b/packages/icons/tsconfig.build.json new file mode 100644 index 000000000..e4828bc1f --- /dev/null +++ b/packages/icons/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "src/**/*.test.ts"] +} diff --git a/packages/react-contentkit/package.json b/packages/react-contentkit/package.json index eee7aa85b..af2f56854 100644 --- a/packages/react-contentkit/package.json +++ b/packages/react-contentkit/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "classnames": "^2.5.1", - "@gitbook/api": "^0.120.0", + "@gitbook/api": "catalog:", "@gitbook/icons": "workspace:*" }, "peerDependencies": {