Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"cSpell.words": [
"affordance",
"Andika",
"Autofac",
"bloomdraggable",
"bloomlibrary",
"bloompub",
Expand All @@ -44,44 +43,30 @@
"configr",
"contenteditable",
"contentful",
"datadiv",
"defn",
"deshroud",
"distractors",
"divs",
"draggables",
"editables",
"elevenlabs",
"Expando",
"invisibles",
"jquery",
"localizable",
"Longpress",
"Newtonsoft",
"nicescroll",
"Palaso",
"pixabay",
"portafino",
"Prefs",
"printshop",
"qtip",
"shellbook",
"sillsdev",
"snappable",
"thema",
"tinycolor",
"Velopack",
"Winform",
"Winforms",
"xmatter"
],
"chat.useNestedAgentsMdFiles": true,
"workbench.colorCustomizations": {
"statusBar.background": "#96668f",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#ab84a5",
"statusBarItem.remoteBackground": "#96668f",
"statusBarItem.remoteForeground": "#e7e7e7"
},
"peacock.color": "#96668f"
"chat.useNestedAgentsMdFiles": true
}
21 changes: 6 additions & 15 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,19 @@ Internally, Bloom is a hybrid. It started as a C#/WinForms app with an embedded

#### Install Dependencies

1. Install C# dependencies
1. Install [volta](https://docs.volta.sh/guide/getting-started) globally on your computer. This will provide you with the correct node and yarn. You may need to restart your computer for the installation to take effect.

```bash
./build/getDependencies-windows.sh
```

2. Install [volta](https://docs.volta.sh/guide/getting-started) globally on your computer. This will provide you with the correct node and yarn. You may need to restart your computer for the installation to take effect.

3. Install browser code dependencies:
2. Install other dependencies:

```bash
cd src/content
yarn
cd ../BloomBrowserUI
yarn
./init.sh
```

#### Build the browser part
#### Build the front-end (browser) part

1. In `/src/BloomBrowserUI`, run `yarn build`.
In `/src/BloomBrowserUI`, run `yarn dev` for a hot-reloading dev server. Alternatively, you can run `yarn build` to generate the needed files once.

#### Build the .NET part
#### Build the back-end (.NET) part

##### In VS Code:

Expand Down
21 changes: 11 additions & 10 deletions src/BloomBrowserUI/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,27 @@
"node": ">=22.12.0"
},
"scripts": {
"dev": "vite",
"dev": "node ./scripts/dev.mjs",
"// Watching: yarn dev starts vite + file watchers (LESS, pug, static assets, and key content folders)": " ",
"// COMMENTS: make the action a space rather than empty string so `yarn run` can list the scripts": " ",
"test": "vitest run",
"test:watch": "vitest",
"test:ci": "vitest run",
"check-that-node-modules-exists-in-content-dir": "cd ../content && node checkForNodeModules.js && cd ../BloomBrowserUI",
"// 'build:ui': 'builds all the stuff handled directly by vite'": " ",
"build:ui": "vite build",
"// 'build': 'builds all the core stuff devs need in both this folder and content. Does not clean.'": " ",
"build": "npm-run-all --parallel build:ui build:content",
"build:ui": "vite build --logLevel error",
"// 'build': 'builds all the core stuff devs need in both this folder and content. Cleans first.'": " ",
"build": "node scripts/build.js",
"build:clean": "node scripts/clean.js",
"// 'build-prod': 'production build: clean, then build+content in parallel, then l10n'": " ",
"build-prod": "npm run build:clean && npm-run-all --parallel build:ui build:content && npm-run-all --parallel build:l10n:translate build:l10n:create",
"build:pug": "node ./scripts/compilePug.mjs",
"// 'build-prod': 'production build: clean, pageSizes, then build:ui and build:content, then l10n'": " ",
"build-prod": "yarn build:clean && yarn --cwd ../content build:pageSizes && npm-run-all build:ui build:content && npm-run-all --parallel build:l10n:translate build:l10n:create",
"// 'build:l10n': creates/updates xliff files and translates html files.": " ",
"// 'build:l10n': is needed when markdown/html content changes or when testing l10n.": " ",
"// 'build:l10n': should be run after build. (build-prod includes this functionality.)": " ",
"build:l10n": "node scripts/l10n-build.js",
"build:l10n:translate": "node scripts/l10n-build.js translate",
"build:l10n:create": "node scripts/l10n-build.js create",
"build:content": "npm run check-that-node-modules-exists-in-content-dir && cd ../content && npm run build",
"// We shouldn't need'watchBookEditLess' anymore once we finish getting vite dev working": " ",
"watchBookEditLess": "less-watch-compiler bookEdit ../../output/browser/bookEdit",
"build:content": "yarn run check-that-node-modules-exists-in-content-dir && yarn --cwd ../content build",
"// 'watch': rebuilds bundles when source files change (for entrypoints not yet working with vite dev)": " ",
"watch": "vite build --watch",
"// You can use yarn link to symlink bloom-player. But also, bloom needs a copy in output/!": " ",
Expand Down Expand Up @@ -139,6 +137,7 @@
"color-blind": "^0.1.1",
"comicaljs": "^0.3.100",
"contentful": "^8.3.7",
"css-element-queries": "^1.2.3",
"filesize": "^6.1.0",
"global": "^4.3.2",
"html-entities": "^2.1.0",
Expand Down Expand Up @@ -232,11 +231,13 @@
"less": "^3.13.1",
"less-watch-compiler": "^1.13.0",
"lessc-glob": "^1.0.9",
"chokidar": "^3.6.0",
"lint-staged": "^15.4.3",
"lorem-ipsum": "^2.0.2",
"markdown-it-attrs": "^4.3.1",
"markdown-it-container": "^4.0.0",
"npm-run-all": "^4.1.5",
"onchange": "^7.1.0",
"patch-package": "^6.4.7",
"path": "^0.12.7",
"playwright": "^1.56.1",
Expand Down
112 changes: 112 additions & 0 deletions src/BloomBrowserUI/scripts/__tests__/compilePug.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import pug from "pug";
import { compilePugFiles } from "../compilePug.mjs";

let tempDir: string;
let browserUIRoot: string;
let contentRoot: string;
let outputBase: string;

function makeDir(dirPath: string) {
fs.mkdirSync(dirPath, { recursive: true });
}

function writeFile(filePath: string, contents: string) {
makeDir(path.dirname(filePath));
fs.writeFileSync(filePath, contents);
}

beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "compile-pug-"));
browserUIRoot = path.join(tempDir, "browserUI");
contentRoot = path.join(tempDir, "content");
outputBase = path.join(tempDir, "out");
makeDir(browserUIRoot);
makeDir(contentRoot);
});

afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});

describe("compilePugFiles", () => {
it("recompiles dependents when an included pug file changes", async () => {
const partialPath = path.join(browserUIRoot, "partials", "partial.pug");
const mainPath = path.join(browserUIRoot, "pages", "main.pug");

writeFile(partialPath, "p Partial A\n");

writeFile(
mainPath,
[
"doctype html",
"html",
" body",
" include ../partials/partial.pug",
"",
].join("\n"),
);

await compilePugFiles({ browserUIRoot, contentRoot, outputBase });

const outPath = path.join(outputBase, "pages", "main.html");
expect(fs.existsSync(outPath)).toBe(true);
const firstHtml = fs.readFileSync(outPath, "utf8");
expect(firstHtml).toContain("Partial A");

await new Promise((resolve) => setTimeout(resolve, 30));
writeFile(partialPath, "p Partial B\n");

await compilePugFiles({ browserUIRoot, contentRoot, outputBase });
const secondHtml = fs.readFileSync(outPath, "utf8");
expect(secondHtml).toContain("Partial B");
expect(secondHtml).not.toContain("Partial A");
});

it("skips expensive compilation when files are up to date", async () => {
const mainPath = path.join(browserUIRoot, "pages", "main.pug");
writeFile(mainPath, "p Hello\n");

await compilePugFiles({ browserUIRoot, contentRoot, outputBase });

const compileSpy = vi.spyOn(pug, "compileFile");
const result = await compilePugFiles({
browserUIRoot,
contentRoot,
outputBase,
});

expect(compileSpy).not.toHaveBeenCalled();
expect(result.compiled).toBe(0);
expect(result.skipped).toBeGreaterThan(0);

compileSpy.mockRestore();
});

it("writes content pug output under output base", async () => {
const contentPugPath = path.join(contentRoot, "dialogs", "info.pug");
writeFile(contentPugPath, "p From content\n");

await compilePugFiles({ browserUIRoot, contentRoot, outputBase });

const expectedOutputPath = path.join(
outputBase,
"dialogs",
"info.html",
);
const unexpectedContentHtmlPath = path.join(
contentRoot,
"dialogs",
"info.html",
);

expect(fs.existsSync(expectedOutputPath)).toBe(true);
expect(fs.readFileSync(expectedOutputPath, "utf8")).toContain(
"From content",
);
expect(fs.existsSync(unexpectedContentHtmlPath)).toBe(false);
});
});
54 changes: 54 additions & 0 deletions src/BloomBrowserUI/scripts/__tests__/copyStaticFile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { copyStaticFile } from "../copyStaticFile.mjs";

let tempDir: string;
let browserUIRoot: string;
let outputBase: string;

function makeDir(dirPath: string) {
fs.mkdirSync(dirPath, { recursive: true });
}

function writeFile(filePath: string, contents: string) {
makeDir(path.dirname(filePath));
fs.writeFileSync(filePath, contents);
}

beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "copy-static-"));
browserUIRoot = path.join(tempDir, "browserUI");
outputBase = path.join(tempDir, "out");
makeDir(browserUIRoot);
});

afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});

describe("copyStaticFile", () => {
it("ignores tsconfig.json files in nested folders", () => {
const nestedTsConfig = path.join(
browserUIRoot,
"sub",
"folder",
"tsconfig.json",
);
writeFile(nestedTsConfig, '{"compilerOptions":{}}\n');

const copied = copyStaticFile(nestedTsConfig, {
browserUIRoot,
outputBase,
quiet: true,
});

expect(copied).toBe(false);
expect(
fs.existsSync(
path.join(outputBase, "sub", "folder", "tsconfig.json"),
),
).toBe(false);
});
});
Loading