Improve font import; replace Inconsolata with Source Code Pro; show third-party licenses in editor dialog (#3079)

* Improve font import; replace Inconsolata with Source Code Pro; show third-party licenses in editor dialog

* Code review
This commit is contained in:
Keavon Chambers
2025-08-21 11:57:04 -07:00
committed by GitHub
parent e56f858ced
commit 0e467907e2
26 changed files with 365 additions and 178 deletions
+28 -29
View File
@@ -7,11 +7,11 @@
"name": "graphite-web-frontend",
"license": "Apache-2.0",
"dependencies": {
"@fontsource/inconsolata": "^5.2.5",
"@fontsource/source-sans-pro": "^5.2.5",
"class-transformer": "^0.5.1",
"idb-keyval": "^6.2.1",
"reflect-metadata": "^0.2.2"
"reflect-metadata": "^0.2.2",
"source-code-pro": "github:adobe-fonts/source-code-pro#2.042R-u/1.062R-i/1.026R-vf",
"source-sans": "github:adobe-fonts/source-sans#2.045R-ro%2F1.095R-it"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
@@ -29,7 +29,7 @@
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.6",
"process": "^0.11.10",
"rollup-plugin-license": "^3.5.3",
"rollup-plugin-license": "^3.6.0",
"sass": "1.78.0",
"svelte": "^4.2.19",
"svelte-preprocess": "^6.0.2",
@@ -556,21 +556,6 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@fontsource/inconsolata": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.2.5.tgz",
"integrity": "sha512-OvzkZY5qYghv/jEV6cfGZzFhdFTvSnU+ExPC7WcZ7w8PdRhtiu/SpcBWOBt+3LXgS0n9qyepgq4zZmxlDTlGGQ==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@fontsource/source-sans-pro": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/@fontsource/source-sans-pro/-/source-sans-pro-5.2.5.tgz",
"integrity": "sha512-ypendqc4pYUc+EgF7qqPY9iVYEz1t/Qr03VojKxG/2g3dnpHa1B6DOlDxWQjQXDj5QrG6inEqGT0g+edjALZyg==",
"license": "OFL-1.1"
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -4622,14 +4607,14 @@
}
},
"node_modules/rollup-plugin-license": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.5.3.tgz",
"integrity": "sha512-r3wImZSo2d6sEk9BRJtlzeI/upjyjnpthy06Fdl0EzqRrlg3ULb9KQR7xHJI0zuayW/8bchEXSF5dO6dha4OyA==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.6.0.tgz",
"integrity": "sha512-1ieLxTCaigI5xokIfszVDRoy6c/Wmlot1fDEnea7Q/WXSR8AqOjYljHDLObAx7nFxHC2mbxT3QnTSPhaic2IYw==",
"dev": true,
"license": "MIT",
"dependencies": {
"commenting": "~1.1.0",
"fdir": "6.3.0",
"fdir": "^6.4.3",
"lodash": "~4.17.21",
"magic-string": "~0.30.0",
"moment": "~2.30.1",
@@ -4645,11 +4630,14 @@
}
},
"node_modules/rollup-plugin-license/node_modules/fdir": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
"integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -4660,9 +4648,9 @@
}
},
"node_modules/rollup-plugin-license/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -4864,6 +4852,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/source-code-pro": {
"version": "2.38.0",
"resolved": "git+ssh://git@github.com/adobe-fonts/source-code-pro.git#d3f1a5962cde503f9409c21e58527611d4a19ef1",
"license": "OFL-1.1"
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -4874,6 +4867,12 @@
"node": ">=0.10.0"
}
},
"node_modules/source-sans": {
"name": "source-sans-pro",
"version": "2.40.0",
"resolved": "git+ssh://git@github.com/adobe-fonts/source-sans.git#ce77773581f4d454f0fa985c073bb25c721bfcf5",
"license": "SIL Open Font License 1.1"
},
"node_modules/spdx-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz",
+4 -4
View File
@@ -31,11 +31,11 @@
"wasm:watch-production": "cargo watch --postpone --watch-when-idle --workdir=wasm --shell \"wasm-pack build . --release --target=web -- --color=always\""
},
"dependencies": {
"@fontsource/inconsolata": "^5.2.5",
"@fontsource/source-sans-pro": "^5.2.5",
"class-transformer": "^0.5.1",
"idb-keyval": "^6.2.1",
"reflect-metadata": "^0.2.2"
"reflect-metadata": "^0.2.2",
"source-code-pro": "github:adobe-fonts/source-code-pro#2.042R-u/1.062R-i/1.026R-vf",
"source-sans": "github:adobe-fonts/source-sans#2.045R-ro%2F1.095R-it"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
@@ -53,7 +53,7 @@
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.6",
"process": "^0.11.10",
"rollup-plugin-license": "^3.5.3",
"rollup-plugin-license": "^3.6.0",
"sass": "1.78.0",
"svelte": "^4.2.19",
"svelte-preprocess": "^6.0.2",
+40
View File
@@ -352,4 +352,44 @@
:not(.optional-input) > .checkbox-input input:focus-visible + label.checked {
outline: 1px dashed var(--color-2-mildblack);
}
@font-face {
font-family: "Source Sans Pro";
font-weight: 400;
font-style: normal;
font-stretch: normal;
src: url("@graphite/../node_modules/source-sans/WOFF2/TTF/SourceSansPro-Regular.ttf.woff2") format("woff2");
}
@font-face {
font-family: "Source Sans Pro";
font-weight: 400;
font-style: italic;
font-stretch: normal;
src: url("@graphite/../node_modules/source-sans/WOFF2/TTF/SourceSansPro-It.ttf.woff2") format("woff2");
}
@font-face {
font-family: "Source Sans Pro";
font-weight: 700;
font-style: normal;
font-stretch: normal;
src: url("@graphite/../node_modules/source-sans/WOFF2/TTF/SourceSansPro-Bold.ttf.woff2") format("woff2");
}
@font-face {
font-family: "Source Sans Pro";
font-weight: 700;
font-style: italic;
font-stretch: normal;
src: url("@graphite/../node_modules/source-sans/WOFF2/TTF/SourceSansPro-BoldIt.ttf.woff2") format("woff2");
}
@font-face {
font-family: "Source Code Pro";
font-weight: 400;
font-style: normal;
font-stretch: normal;
src: url("@graphite/../node_modules/source-code-pro/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2") format("woff2");
}
</style>
@@ -89,6 +89,7 @@
.header-area,
.footer-area {
background: var(--color-1-nearblack);
flex: 0 0 auto;
}
.header-area,
@@ -113,6 +114,8 @@
.content {
margin: -4px 0;
padding-right: calc(24px + 1px * var(--even-integer-subpixel-expansion-x));
padding-bottom: calc(16px + 1px * var(--even-integer-subpixel-expansion-y));
&.center .row {
justify-content: center;
@@ -126,18 +129,22 @@
}
}
.details.text-label {
-webkit-user-select: text; // Required as of Safari 15.0 (Graphite's minimum version) through the latest release
user-select: text;
white-space: pre-wrap;
max-width: 400px;
height: auto;
}
.radio-input button {
flex-grow: 1;
}
.text-label.multiline {
-webkit-user-select: text; // Still required by Safari as of 2025
user-select: text;
}
// Used by the "Third-Party Software License Notices" dialog
.details:has(.text-label.multiline.monospace) {
max-height: 60vh;
max-width: 80vw;
overflow: auto;
}
// Used by the "Open Demo Artwork" dialog
.image-label {
border-radius: 2px;
@@ -137,20 +137,23 @@
// This solves antialiasing issues when the content isn't cleanly divisible by 2 and gets translated by (-50%, -50%) causing all its content to be blurry.
const floatingMenuContentDiv = floatingMenuContent?.div?.();
if (type === "Dialog" && floatingMenuContentDiv) {
// TODO: Also use https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver to detect any changes which may affect the size of the content.
// TODO: The current method only notices when the dialog size increases but can't detect when it decreases.
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
let { width, height } = entry.contentRect;
const existingWidth = Number(floatingMenuContentDiv.style.getPropertyValue("--even-integer-subpixel-expansion-x"));
const existingHeight = Number(floatingMenuContentDiv.style.getPropertyValue("--even-integer-subpixel-expansion-y"));
width = Math.ceil(width);
if (width % 2 === 1) width += 1;
height = Math.ceil(height);
if (height % 2 === 1) height += 1;
let { width, height } = entry.contentRect;
width -= existingWidth;
height -= existingHeight;
let targetWidth = Math.ceil(width);
if (targetWidth % 2 === 1) targetWidth += 1;
let targetHeight = Math.ceil(height);
if (targetHeight % 2 === 1) targetHeight += 1;
// We have to set the style properties directly because attempting to do it through a Svelte bound property results in `afterUpdate()` being triggered
floatingMenuContentDiv.style.setProperty("min-width", width === 0 ? "unset" : `${width}px`);
floatingMenuContentDiv.style.setProperty("min-height", height === 0 ? "unset" : `${height}px`);
floatingMenuContentDiv.style.setProperty("--even-integer-subpixel-expansion-x", `${targetWidth - width}`);
floatingMenuContentDiv.style.setProperty("--even-integer-subpixel-expansion-y", `${targetHeight - height}`);
});
});
resizeObserver.observe(floatingMenuContentDiv);
@@ -158,14 +161,6 @@
});
afterUpdate(() => {
// Remove the size constraint after the content updates so the resize observer can measure the content and reapply a newly calculated one
const floatingMenuContentDiv = floatingMenuContent?.div?.();
if (type === "Dialog" && floatingMenuContentDiv) {
// We have to set the style properties directly because attempting to do it through a Svelte bound property results in `afterUpdate()` being triggered
floatingMenuContentDiv.style.setProperty("min-width", "unset");
floatingMenuContentDiv.style.setProperty("min-height", "unset");
}
// Gets the client bounds of the elements and apply relevant styles to them.
// TODO: Use DOM attribute bindings more whilst not causing recursive updates. Turning measuring on and off both causes the component to change,
// TODO: which causes the `afterUpdate()` Svelte event to fire extraneous times (hurting performance and sometimes causing an infinite loop).
+1 -1
View File
@@ -770,7 +770,7 @@
width: 100%;
&:disabled {
-webkit-user-select: none; // Required as of Safari 15.0 (Graphite's minimum version) through the latest release
-webkit-user-select: none; // Still required by Safari as of 2025
user-select: none;
// Workaround for `user-select: none` not working on <input> elements
pointer-events: none;
@@ -8,9 +8,10 @@
export let disabled = false;
export let bold = false;
export let italic = false;
export let monospace = false;
export let centerAlign = false;
export let tableAlign = false;
export let minWidth = 0;
export let minWidth = "";
export let multiline = false;
export let tooltip: string | undefined = undefined;
export let forCheckbox: bigint | undefined = undefined;
@@ -28,10 +29,11 @@
class:disabled
class:bold
class:italic
class:monospace
class:multiline
class:center-align={centerAlign}
class:table-align={tableAlign}
style:min-width={minWidth > 0 ? `${minWidth}px` : undefined}
style:min-width={minWidth || undefined}
style={`${styleName} ${extraStyles}`.trim() || undefined}
title={tooltip}
for={forCheckbox !== undefined ? `checkbox-input-${forCheckbox}` : undefined}
@@ -58,6 +60,11 @@
font-style: italic;
}
&.monospace {
font-family: "Source Code Pro", monospace;
font-size: 12px;
}
&.multiline {
white-space: pre-wrap;
margin: 4px 0;
@@ -183,7 +183,8 @@
display: flex;
justify-content: center;
align-items: center;
font-family: "Inconsolata", monospace;
font-family: "Source Code Pro", monospace;
font-size: 12px;
font-weight: 400;
text-align: center;
height: 16px;
-7
View File
@@ -1,12 +1,5 @@
// This file is the browser's entry point for the JS bundle
// Fonts
import "@fontsource/inconsolata";
import "@fontsource/source-sans-pro/400-italic.css";
import "@fontsource/source-sans-pro/400.css";
import "@fontsource/source-sans-pro/700-italic.css";
import "@fontsource/source-sans-pro/700.css";
// `reflect-metadata` allows for runtime reflection of types in JavaScript.
// It is needed for class-transformer to work and is imported as a side effect.
// The library replaces the Reflect API on the window to support more features.
+8 -3
View File
@@ -946,6 +946,8 @@ export class TriggerAboutGraphiteLocalizedCommitDate extends JsMessage {
readonly commitDate!: string;
}
export class TriggerDisplayThirdPartyLicensesDialog extends JsMessage {}
// WIDGET PROPS
export abstract class WidgetProps {
@@ -1370,13 +1372,15 @@ export class TextLabel extends WidgetProps {
italic!: boolean;
monospace!: boolean;
multiline!: boolean;
centerAlign!: boolean;
tableAlign!: boolean;
minWidth!: number;
multiline!: boolean;
minWidth!: string;
@Transform(({ value }: { value: string }) => value || undefined)
tooltip!: string | undefined;
@@ -1692,6 +1696,7 @@ export const messageMakers: Record<string, MessageMaker> = {
DisplayRemoveEditableTextbox,
SendUIMetadata,
TriggerAboutGraphiteLocalizedCommitDate,
TriggerDisplayThirdPartyLicensesDialog,
TriggerSaveDocument,
TriggerSaveFile,
TriggerExportImage,
+21 -1
View File
@@ -1,7 +1,16 @@
import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor";
import { defaultWidgetLayout, DisplayDialog, DisplayDialogDismiss, UpdateDialogButtons, UpdateDialogColumn1, UpdateDialogColumn2, patchWidgetLayout } from "@graphite/messages";
import {
defaultWidgetLayout,
DisplayDialog,
DisplayDialogDismiss,
UpdateDialogButtons,
UpdateDialogColumn1,
UpdateDialogColumn2,
patchWidgetLayout,
TriggerDisplayThirdPartyLicensesDialog,
} from "@graphite/messages";
import { type IconName } from "@graphite/utility-functions/icons";
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@@ -78,6 +87,17 @@ export function createDialogState(editor: Editor) {
});
editor.subscriptions.subscribeJsMessage(DisplayDialogDismiss, dismissDialog);
editor.subscriptions.subscribeJsMessage(TriggerDisplayThirdPartyLicensesDialog, async () => {
const BACKUP_URL = "https://editor.graphite.rs/third-party-licenses.txt";
let licenseText = `Content was not able to load. Please check your network connection and try again.\n\nOr visit ${BACKUP_URL} for the license notices.`;
if (editor.handle.inDevelopmentMode()) licenseText = `Third-party licenses are not available in development builds.\n\nVisit ${BACKUP_URL} for the license notices.`;
const response = await fetch("/third-party-licenses.txt");
if (response.ok && response.headers.get("Content-Type")?.includes("text/plain")) licenseText = await response.text();
editor.handle.requestLicensesThirdPartyDialogWithLicenseText(licenseText);
});
return {
subscribe,
dismissDialog,
+83 -38
View File
@@ -13,34 +13,19 @@ import { DynamicPublicDirectory as viteMultipleAssets } from "vite-multiple-asse
const projectRootDir = path.resolve(__dirname);
// Keep this list in sync with those in `/about.toml` and `/deny.toml`.
const ALLOWED_LICENSES = [
"Apache-2.0 WITH LLVM-exception",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"BSL-1.0",
"CC0-1.0",
"CDLA-Permissive-2.0",
"ISC",
"MIT-0",
"MIT",
"MPL-2.0",
"OpenSSL",
"Unicode-3.0",
"Unicode-DFS-2016",
"Zlib",
"NCSA",
];
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
svelte({
preprocess: [sveltePreprocess()],
onwarn(warning, defaultHandler) {
// NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
const suppressed = ["css-unused-selector", "vite-plugin-svelte-css-no-scopable-elements", "a11y-no-static-element-interactions", "a11y-no-noninteractive-element-interactions"];
const suppressed = [
"css-unused-selector", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
"vite-plugin-svelte-css-no-scopable-elements", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
"a11y-no-static-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
"a11y-no-noninteractive-element-interactions", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
"a11y-click-events-have-key-events", // NOTICE: Keep this list in sync with the list in `.vscode/settings.json`
];
if (suppressed.includes(warning.code)) return;
defaultHandler?.(warning);
@@ -66,8 +51,10 @@ export default defineConfig({
plugins: [
rollupPluginLicense({
thirdParty: {
includePrivate: false,
multipleVersions: true,
allow: {
test: `(${ALLOWED_LICENSES.join(" OR ")})`,
test: `(${getAcceptedLicenses()})`,
failOnUnlicensed: true,
failOnViolation: true,
},
@@ -103,10 +90,11 @@ type PackageInfo = {
function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
// Generate the Rust license information.
let licenses = generateRustLicenses() || [];
const rustLicenses = generateRustLicenses();
const additionalLicenses = generateAdditionalLicenses();
// Ensure we have license information to work with before proceeding.
if (licenses.length === 0) {
// Ensure we have the required license information to work with before proceeding.
if (rustLicenses.length === 0) {
// This is probably caused by `cargo about` not being installed.
console.error("Could not run `cargo about`, which is required to generate license information.");
console.error("To install cargo-about on your system, you can run `cargo install cargo-about`.");
@@ -121,9 +109,11 @@ function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
process.exit(1);
}
// Find then duplicate this license if one of its packages is `path-bool`, adding its notice text.
let foundLicensesIndex;
let foundPackagesIndex;
let licenses = rustLicenses.concat(additionalLicenses);
// SPECIAL CASE: Find then duplicate this license if one of its packages is `path-bool`, adding its notice text.
let foundLicensesIndex: number | undefined = undefined;
let foundPackagesIndex: number | undefined = undefined;
licenses.forEach((license, licenseIndex) => {
license.packages.forEach((pkg, pkgIndex) => {
if (pkg.name === "path-bool") {
@@ -147,7 +137,7 @@ function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
});
}
// Augment the imported Rust license list with the provided JS license list.
// Extend the license list with the provided JS licenses.
jsLicenses.forEach((jsLicense) => {
const name = jsLicense.name || "";
const version = jsLicense.version || "";
@@ -158,14 +148,12 @@ function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
let repository = jsLicense.repository || "";
if (repository && typeof repository === "object") repository = repository.url;
// Remove the `git+` or `git://` prefix and `.git` suffix.
const repo = repository ? repository.replace(/^.*(github.com\/.*?\/.*?)(?:.git)/, "https://$1") : repository;
const matchedLicense = licenses.find(
(license) => license.licenseName === licenseName && trimBlankLines(license.licenseText || "") === licenseText && trimBlankLines(license.noticeText || "") === noticeText,
);
const pkg: PackageInfo = { name, version, author, repository: repo };
const pkg: PackageInfo = { name, version, author, repository };
if (matchedLicense) matchedLicense.packages.push(pkg);
else licenses.push({ licenseName, licenseText, noticeText, packages: [pkg] });
});
@@ -232,7 +220,15 @@ function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
licenses.forEach((license) => {
let packagesWithSameLicense = license.packages.map((packageInfo) => {
const { name, version, author, repository } = packageInfo;
return `${name} ${version}${author ? ` - ${author}` : ""}${repository ? ` - ${repository}` : ""}`;
// Remove the `git+` or `git://` prefix and `.git` suffix.
let repo = repository;
if (repo.startsWith("git+")) repo = repo.slice("git+".length);
if (repo.startsWith("git://")) repo = repo.slice("git://".length);
if (repo.endsWith(".git")) repo = repo.slice(0, -".git".length);
if (repo.endsWith(".git#release")) repo = repo.slice(0, -".git#release".length);
return `${name} ${version}${author ? ` - ${author}` : ""}${repo ? ` - ${repo}` : ""}`;
});
const multi = packagesWithSameLicense.length !== 1;
const saysLicense = license.licenseName.toLowerCase().includes("license");
@@ -249,10 +245,44 @@ function formatThirdPartyLicenses(jsLicenses: Dependency[]): string {
formattedLicenseNotice += ` ${"‾".repeat(packagesLineLength + 2)}\n`;
formattedLicenseNotice += `${license.licenseText}\n`;
});
formattedLicenseNotice += "\n";
return formattedLicenseNotice;
}
function generateRustLicenses(): LicenseInfo[] | undefined {
// Include additional licenses that aren't automatically generated by `cargo about` or `rollup-plugin-license`.
function generateAdditionalLicenses(): LicenseInfo[] {
const ADDITIONAL_LICENSES = [
{
licenseName: "SIL Open Font License 1.1",
licenseTextPath: "node_modules/source-sans/LICENSE.txt",
manifestPath: "node_modules/source-sans/package.json",
},
{
licenseName: "SIL Open Font License 1.1",
licenseTextPath: "node_modules/source-code-pro/LICENSE.md",
manifestPath: "node_modules/source-code-pro/package.json",
},
];
return ADDITIONAL_LICENSES.map(({ licenseName, licenseTextPath, manifestPath }) => {
const licenseText = (fs.existsSync(licenseTextPath) && fs.readFileSync(licenseTextPath, "utf8")) || "";
const manifestJSON = (fs.existsSync(manifestPath) && JSON.parse(fs.readFileSync(manifestPath, "utf8"))) || {};
const name = manifestJSON.name || "";
const version = manifestJSON.version || "";
const author = manifestJSON.author.name || manifestJSON.author || "";
const repository = manifestJSON.repository?.url || "";
return {
licenseName,
licenseText: trimBlankLines(licenseText),
packages: [{ name, version, author, repository }],
};
});
}
function generateRustLicenses(): LicenseInfo[] {
// Log the starting status to the build output.
console.info("\n\nGenerating license information for Rust code\n");
@@ -272,14 +302,14 @@ function generateRustLicenses(): LicenseInfo[] | undefined {
if (status !== 101) {
console.error("cargo-about failed", status, stderr);
}
return undefined;
return [];
}
// Make sure the output starts with this expected label, which lets us know the file generated with expected output.
// We don't want to eval an error message or something else, so we fail early if that happens.
if (!stdout.trim().startsWith("GENERATED_BY_CARGO_ABOUT:")) {
console.error("Unexpected output from cargo-about", stdout);
return undefined;
return [];
}
// Convert the array JS syntax string into an actual JS array in memory.
@@ -308,7 +338,7 @@ function generateRustLicenses(): LicenseInfo[] | undefined {
return rustLicenses;
} catch (_) {
return undefined;
return [];
}
}
@@ -355,3 +385,18 @@ function trimBlankLines(input: string): string {
return result;
}
function getAcceptedLicenses() {
const tomlContent = fs.readFileSync(path.resolve(__dirname, "../about.toml"), "utf8");
const licensesBlock = tomlContent?.match(/accepted\s*=\s*\[([^\]]*)\]/)?.[1] || "";
return licensesBlock
.split("\n")
.map((line) => line.replace(/#.*$/, "")) // Remove comments
.join("\n")
.split(",")
.map((license) => license.trim().replace(/"/g, ""))
.filter((license) => license.length > 0)
.join(" OR ");
}
+6
View File
@@ -466,6 +466,12 @@ impl EditorHandle {
self.dispatch(message);
}
#[wasm_bindgen(js_name = requestLicensesThirdPartyDialogWithLicenseText)]
pub fn request_licenses_third_party_dialog_with_license_text(&self, license_text: String) {
let message = DialogMessage::RequestLicensesThirdPartyDialogWithLicenseText { license_text };
self.dispatch(message);
}
/// Send new bounds when document panel viewports get resized or moved within the editor
/// [left, top, right, bottom]...
#[wasm_bindgen(js_name = boundsOfViewports)]