-
Notifications
You must be signed in to change notification settings - Fork 0
docs: refresh API site and localization #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| name: Test | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| docs-and-build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 | ||
| cache: npm | ||
| - run: npm ci | ||
| - run: npm test |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| declare module "@bloomengine/engine" { | ||
| export interface Vec3 { x: number; y: number; z: number } | ||
| export interface Color { r: number; g: number; b: number; a: number } | ||
| export interface Camera3D { | ||
| position: Vec3; | ||
| target: Vec3; | ||
| up: Vec3; | ||
| fovy: number; | ||
| projection: "perspective" | "orthographic"; | ||
| } | ||
| export interface Model { handle: number } | ||
|
|
||
| export const Colors: Record<string, Color>; | ||
| export function initWindow(width: number, height: number, title: string, fullscreen?: boolean): void; | ||
| export function runGame(frame: (dt: number) => void): void; | ||
| export function beginDrawing(): void; | ||
| export function endDrawing(): void; | ||
| export function clearBackground(color: Color): void; | ||
| export function drawText(text: string, x: number, y: number, size: number, color: Color): void; | ||
| export function beginMode3D(camera: Camera3D): void; | ||
| export function endMode3D(): void; | ||
| export function drawModel(model: Model, position: Vec3, scale: number, tint: Color): void; | ||
| export function drawGrid(slices: number, spacing: number): void; | ||
| export function updateModelAnimation(handle: number, animIndex: number, time: number, scale: number, px: number, py: number, pz: number, rotY: number): void; | ||
| } | ||
|
|
||
| declare module "@bloomengine/engine/shapes" { | ||
| import type { Color } from "@bloomengine/engine"; | ||
| interface Rect { x: number; y: number; width: number; height: number } | ||
| export function drawRect(x: number, y: number, width: number, height: number, color: Color): void; | ||
| export function drawCircle(x: number, y: number, radius: number, color: Color): void; | ||
| export function checkCollisionRecs(a: Rect, b: Rect): boolean; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #!/usr/bin/env node | ||
|
|
||
| import fs from "node:fs"; | ||
| import path from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
|
|
||
| const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); | ||
| const read = (relative) => fs.readFileSync(path.join(root, relative), "utf8"); | ||
| let failures = 0; | ||
| const fail = (message) => { | ||
| console.error(`FAIL ${message}`); | ||
| failures += 1; | ||
| }; | ||
|
|
||
| const expectedModules = [ | ||
| "core", "shapes", "textures", "text", "audio", "models", "math", | ||
| "physics", "scene", "mobile", "world", "vfx", "quality", | ||
| ]; | ||
| const numberWords = { | ||
| en: ["nine", "thirteen"], | ||
| de: ["neun", "dreizehn"], | ||
| es: ["nueve", "trece"], | ||
| fr: ["neuf", "treize"], | ||
| id: ["sembilan", "tiga belas"], | ||
| it: ["nove", "tredici"], | ||
| ja: ["9 ", "13 "], | ||
| ko: ["9", "13"], | ||
| pt: ["nove", "treze"], | ||
| th: ["เก้า", "สิบสาม"], | ||
| tr: ["dokuz", "on üç"], | ||
| vi: ["chín", "mười ba"], | ||
| "zh-Hans": ["九", "十三"], | ||
| }; | ||
|
|
||
| const messageDir = path.join(root, "src/messages"); | ||
| const messageFiles = fs.readdirSync(messageDir).filter((name) => name.endsWith(".json")); | ||
| for (const file of messageFiles) { | ||
| const locale = file.slice(0, -5); | ||
| const messages = JSON.parse(read(`src/messages/${file}`)); | ||
| const ids = messages.home.modules.items.map((item) => item.id); | ||
| if (JSON.stringify(ids) !== JSON.stringify(expectedModules)) { | ||
| fail(`${file}: expected ${expectedModules.length} modules, got ${ids.join(", ")}`); | ||
| } | ||
| const [nine, thirteen] = numberWords[locale]; | ||
| if (!messages.home.heroDesc.toLocaleLowerCase(locale).includes(nine)) { | ||
| fail(`${file}: hero does not state the nine-target platform count`); | ||
| } | ||
| if (!messages.home.modules.desc.toLocaleLowerCase(locale).includes(thirteen)) { | ||
| fail(`${file}: module copy does not state the thirteen-module count`); | ||
| } | ||
| for (const platform of ["Android", "watchOS", "visionOS"]) { | ||
| if (!messages.home.why.shipEverywhere.desc.includes(platform)) { | ||
| fail(`${file}: ship-everywhere copy omits ${platform}`); | ||
| } | ||
| } | ||
| if (!messages.meta.defaultDescription.includes("WebAssembly") || | ||
| !messages.home.why.desc.includes("WebAssembly")) { | ||
| fail(`${file}: native-versus-Web copy does not explain the WebAssembly target`); | ||
| } | ||
| } | ||
|
|
||
| const sourceFiles = [ | ||
| "src/pages/docs.astro", | ||
| "src/components/HomePage.astro", | ||
| "src/components/BlogPostWhereBloomStands.astro", | ||
| ]; | ||
| const source = sourceFiles.map((file) => `${file}\n${read(file)}`).join("\n"); | ||
| for (const [label, pattern] of [ | ||
| ["removed Colors.RAYWHITE constant", /Colors\.<span class="prop">RAYWHITE/], | ||
| ["legacy bloom package import", /<span class="str">"bloom(?:\/|"<)/], | ||
| ["legacy module label", /module-import">bloom\//], | ||
| ["numeric Camera3D projection", /projection:\s*<span class="num">[01](?:\.0)?<\/span>/], | ||
| ]) { | ||
| if (pattern.test(source)) fail(`site source contains ${label}`); | ||
| } | ||
|
|
||
| const docs = read("src/pages/docs.astro"); | ||
| for (const id of expectedModules) { | ||
| if (!docs.includes(`id="${id}"`)) fail(`docs page omits the ${id} module`); | ||
| if (!docs.includes(`@bloomengine/engine/${id}`)) fail(`docs page omits the ${id} public import`); | ||
| } | ||
| for (const required of [ | ||
| 'pathWithoutLocale="/docs"', | ||
| "localizedPathAvailable={false}", | ||
| "technical reference is maintained in English", | ||
| ]) { | ||
| if (!docs.includes(required)) fail(`docs language policy omits: ${required}`); | ||
| } | ||
|
|
||
| const home = read("src/components/HomePage.astro"); | ||
| if (!home.includes('<span class="proof-number">9</span>')) fail("home platform count is not 9"); | ||
| if (!home.includes('<span class="proof-number">13</span>')) fail("home module count is not 13"); | ||
|
|
||
| const layout = read("src/layouts/Layout.astro"); | ||
| if (!layout.includes("alternateLocales") || !layout.includes("languageMenuPath")) { | ||
| fail("layout does not distinguish localized and English-only pages"); | ||
| } | ||
| if (!read("src/pages/[lang]/docs.astro").includes('Astro.redirect("/docs", 308)')) { | ||
| fail("localized docs routes do not redirect to the English reference"); | ||
| } | ||
|
|
||
| const stableRoot = "node_modules/@bloomengine/engine"; | ||
| const stableTypes = read(`${stableRoot}/src/core/types.ts`); | ||
| const stableColors = read(`${stableRoot}/src/core/colors.ts`); | ||
| const stableModels = read(`${stableRoot}/src/models/index.ts`); | ||
| if (!/projection:\s*"perspective"\s*\|\s*"orthographic"/.test(stableTypes)) { | ||
| fail("stable npm package does not accept the documented Camera3D projection strings"); | ||
| } | ||
| if (!/^\s*SNOW:\s+Color\.Snow,/m.test(stableColors)) { | ||
| fail("stable npm package does not contain the documented Colors.SNOW constant"); | ||
| } | ||
| if (!/export function drawModel\([^)]*model:\s*Model,[^)]*position:\s*Vec3,[^)]*scale:\s*number,[^)]*tint:\s*Color[^)]*\)/s.test(stableModels)) { | ||
| fail("stable npm package drawModel signature differs from the website smoke test"); | ||
| } | ||
| if (!/export function updateModelAnimation\([^)]*handle:\s*number,[^)]*animIndex:\s*number,[^)]*time:\s*number,[^)]*scale:\s*number,[^)]*px:\s*number,[^)]*py:\s*number,[^)]*pz:\s*number,[^)]*rotY:\s*number[^)]*\)/s.test(stableModels)) { | ||
| fail("stable npm package updateModelAnimation signature differs from the website smoke test"); | ||
| } | ||
|
|
||
| console.log(`${messageFiles.length} locales checked; ${failures} failures`); | ||
| process.exit(failures === 0 ? 0 : 1); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { | ||
| Colors, | ||
| beginDrawing, | ||
| beginMode3D, | ||
| clearBackground, | ||
| drawGrid, | ||
| drawModel, | ||
| drawText, | ||
| endDrawing, | ||
| endMode3D, | ||
| initWindow, | ||
| runGame, | ||
| updateModelAnimation, | ||
| type Camera3D, | ||
| type Model, | ||
| } from "@bloomengine/engine"; | ||
| import { checkCollisionRecs, drawCircle, drawRect } from "@bloomengine/engine/shapes"; | ||
|
|
||
| // Compile-only copies of the high-traffic website examples. This file is never | ||
| // executed; CI type-checks it against the latest stable npm package. | ||
| initWindow(800, 450, "Hello Bloom"); | ||
| runGame(() => { | ||
| beginDrawing(); | ||
| clearBackground(Colors.SNOW); | ||
| drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY); | ||
| endDrawing(); | ||
| }); | ||
|
|
||
| declare const tree: Model; | ||
| declare const animation: number; | ||
| const camera: Camera3D = { | ||
| position: { x: 10, y: 10, z: 10 }, | ||
| target: { x: 0, y: 0, z: 0 }, | ||
| up: { x: 0, y: 1, z: 0 }, | ||
| fovy: 45, | ||
| projection: "perspective", | ||
| }; | ||
|
|
||
| beginMode3D(camera); | ||
| drawModel(tree, { x: 0, y: 0, z: 0 }, 1, Colors.WHITE); | ||
| drawGrid(10, 1); | ||
| endMode3D(); | ||
|
|
||
| updateModelAnimation(animation, 0, 0, 1, 0, 0, 0, 0); | ||
| drawRect(100, 100, 200, 80, Colors.GREEN); | ||
| drawCircle(400, 300, 50, Colors.GOLD); | ||
| checkCollisionRecs( | ||
| { x: 0, y: 0, width: 10, height: 10 }, | ||
| { x: 5, y: 5, width: 10, height: 10 }, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,10 @@ const platforms = [ | |
| { name: "Linux", api: "Vulkan" }, | ||
| { name: "iOS", api: "Metal" }, | ||
| { name: "tvOS", api: "Metal" }, | ||
| { name: "Web", api: "WebGPU / WASM" }, | ||
| { name: "watchOS", api: "SwiftUI / SceneKit" }, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Align the watchOS backend label with the documented renderer. This table presents the backend for each platform, but watchOS is listed as 🤖 Prompt for AI Agents |
||
| { name: "visionOS", api: "Metal" }, | ||
| { name: "Android", api: "Vulkan / OpenGL ES" }, | ||
| { name: "Web", api: "WebGPU / WebGL" }, | ||
| ]; | ||
| --- | ||
|
|
||
|
|
@@ -28,11 +31,11 @@ const platforms = [ | |
| <section class="hero"> | ||
| <div class="container hero-inner"> | ||
| <div class="hero-content"> | ||
| <p class="hero-badge">{t("home.heroBadge")}</p> | ||
| <p class="hero-badge">{t("home.heroBadge")} · 0.5 preview</p> | ||
| <h1>{t("home.heroTitlePrefix")}<span class="highlight">{t("home.heroTitleHighlight")}</span></h1> | ||
| <p class="hero-desc">{t("home.heroDesc")}</p> | ||
| <div class="hero-actions"> | ||
| <a href="https://github.com/Bloom-Engine" target="_blank" rel="noopener" class="btn btn-primary"> | ||
| <a href="/docs" class="btn btn-primary"> | ||
| <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg> | ||
| {t("home.getStarted")} | ||
| </a> | ||
|
|
@@ -52,13 +55,13 @@ const platforms = [ | |
| <pre><code><span class="kw">import</span> {"{"} initWindow, windowShouldClose, | ||
| beginDrawing, endDrawing, | ||
| clearBackground, drawText, | ||
| Colors {"}"} <span class="kw">from</span> <span class="str">"bloom"</span>; | ||
| Colors {"}"} <span class="kw">from</span> <span class="str">"@bloomengine/engine"</span>; | ||
|
|
||
| initWindow(<span class="num">800</span>, <span class="num">450</span>, <span class="str">"My Game"</span>); | ||
|
|
||
| <span class="kw">while</span> (!windowShouldClose()) {"{"} | ||
| beginDrawing(); | ||
| clearBackground(Colors.<span class="prop">RAYWHITE</span>); | ||
| clearBackground(Colors.<span class="prop">SNOW</span>); | ||
| drawText(<span class="str">"Hello, Bloom!"</span>, | ||
| <span class="num">190</span>, <span class="num">200</span>, <span class="num">20</span>, Colors.<span class="prop">DARKGRAY</span>); | ||
| endDrawing(); | ||
|
|
@@ -77,11 +80,11 @@ initWindow(<span class="num">800</span>, <span class="num">450</span>, <span cla | |
| <span class="proof-label">{t("home.proof.runtimeOverhead")}</span> | ||
| </div> | ||
| <div class="proof-card"> | ||
| <span class="proof-number">6</span> | ||
| <span class="proof-number">9</span> | ||
| <span class="proof-label">{t("home.proof.targetPlatforms")}</span> | ||
| </div> | ||
| <div class="proof-card"> | ||
| <span class="proof-number">9</span> | ||
| <span class="proof-number">13</span> | ||
| <span class="proof-label">{t("home.proof.modules")}</span> | ||
| </div> | ||
| <div class="proof-card"> | ||
|
|
@@ -134,7 +137,7 @@ initWindow(<span class="num">800</span>, <span class="num">450</span>, <span cla | |
| <div class={`module-icon ${m.id}`}></div> | ||
| <h3>{m.title}</h3> | ||
| <p>{m.desc}</p> | ||
| <code class="module-import">bloom/{m.id}</code> | ||
| <code class="module-import">@bloomengine/engine/{m.id}</code> | ||
| </div> | ||
| ))} | ||
| </div> | ||
|
|
@@ -210,7 +213,7 @@ initWindow(<span class="num">800</span>, <span class="num">450</span>, <span cla | |
| <span class="code-dot" style="background: #28C840"></span> | ||
| </div> | ||
| <pre><code><span class="kw">import</span> {"{"} drawRect, drawCircle, | ||
| checkCollisionRecs {"}"} <span class="kw">from</span> <span class="str">"bloom/shapes"</span>; | ||
| checkCollisionRecs {"}"} <span class="kw">from</span> <span class="str">"@bloomengine/engine/shapes"</span>; | ||
|
|
||
| drawRect(<span class="num">100</span>, <span class="num">100</span>, <span class="num">200</span>, <span class="num">80</span>, Colors.<span class="prop">GREEN</span>); | ||
| drawCircle(<span class="num">400</span>, <span class="num">300</span>, <span class="num">50</span>, Colors.<span class="prop">GOLD</span>); | ||
|
|
@@ -232,11 +235,11 @@ drawCircle(<span class="num">400</span>, <span class="num">300</span>, <span cla | |
| position: {"{"} x: <span class="num">10</span>, y: <span class="num">10</span>, z: <span class="num">10</span> {"}"}, | ||
| target: {"{"} x: <span class="num">0</span>, y: <span class="num">0</span>, z: <span class="num">0</span> {"}"}, | ||
| up: {"{"} x: <span class="num">0</span>, y: <span class="num">1</span>, z: <span class="num">0</span> {"}"}, | ||
| fovy: <span class="num">45</span>, projection: <span class="num">0</span> | ||
| fovy: <span class="num">45</span>, projection: <span class="str">"perspective"</span> | ||
| {"}"}; | ||
|
|
||
| beginMode3D(camera); | ||
| drawModel(tree, {"{"} x: <span class="num">0</span>, y: <span class="num">0</span>, z: <span class="num">0</span> {"}"}); | ||
| drawModel(tree, {"{"} x: <span class="num">0</span>, y: <span class="num">0</span>, z: <span class="num">0</span> {"}"}, <span class="num">1.0</span>, Colors.<span class="prop">WHITE</span>); | ||
| drawGrid(<span class="num">10</span>, <span class="num">1</span>); | ||
| endMode3D();</code></pre> | ||
| </div> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Bloom-Engine/landing
Length of output: 5242
🏁 Script executed:
Repository: Bloom-Engine/landing
Length of output: 2903
🌐 Web query:
TypeScript 5.9 noResolve compiler option import module resolution exact behavior💡 Result:
The --noResolve compiler option in TypeScript is a configuration flag that alters how the compiler manages the scope of the files included in a project [1][2]. By default, when TypeScript encounters import statements or triple-slash directives, it attempts to resolve the referenced files and automatically adds them to the compilation context [1][3]. When --noResolve is set to true, this automatic file discovery and addition process is disabled [1][4][3]. Key behavioral details include: 1. Restriction on File Inclusion: The compiler will only include the files explicitly passed to it on the command line (or listed in the 'files' section of a tsconfig.json) [3][5]. Any module or dependency that is imported in your code but not explicitly provided to the compiler will not be added to the project, even if the compiler can technically resolve the path to that file [3][5]. 2. Continued Validation: Setting --noResolve does not stop the compiler from checking import statements [1][2]. The compiler still performs resolution to verify that an import refers to a valid module [1][3]. If a module cannot be resolved, you will receive a standard compilation error (e.g., TS2307: Cannot find module) [3][5]. Therefore, while you prevent the compiler from automatically pulling files into the compilation, you must still ensure those modules are resolvable or otherwise satisfied in the environment [1][4]. 3. Use Case: This option is rarely used and is generally intended for advanced or specialized build scenarios where you want strict control over exactly which files are processed by the compiler, avoiding the "expanding" nature of TypeScript's default module resolution behavior [2][6]. This behavior remains consistent across TypeScript versions, including version 5.9 [1][7].
Citations:
Resolve the installed package declarations in the smoke check.
The command explicitly compiles
scripts/bloom-docs-api.d.ts, whose ambient declarations satisfy both imports inscripts/docs-smoke.ts. Remove--noResolveand exclude the shim so the check validates the locked@bloomengine/enginepackage declarations.🤖 Prompt for AI Agents