Skip to content
Merged
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
36 changes: 36 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Build & Release

on:
push:
branches: ["main"]

jobs:
build:
name: Build Packages
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable

- uses: jetli/wasm-pack-action@v0.4.0

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm run build
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["**"]

jobs:
check:
name: Lint, Type Check & Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable

- uses: jetli/wasm-pack-action@v0.4.0

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build Layout Engine (WASM)
run: pnpm run build

- name: Check Formatting
run: pnpm run format --check

- name: Lint
run: pnpm run lint

- name: Type Check
run: pnpm run check

- name: Run Tests
run: pnpm run test
105 changes: 105 additions & 0 deletions .github/workflows/profiler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Performance Benchmark

on:
pull_request:
branches: ["**"]
paths-ignore:
- "**.md"
- "docs/**"
workflow_dispatch:

jobs:
benchmark:
name: Run Benchmark
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable

- uses: jetli/wasm-pack-action@v0.4.0

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build Layout Engine (WASM)
run: pnpm run build

- name: Run Limit Test
run: |
pnpm run profiler:limit | tee benchmark_result.txt

- name: Extract Report
id: extract_report
run: |
REPORT=$(sed -n '/Performance Limits Report/,$p' benchmark_result.txt)

echo "REPORT_CONTENT<<EOF" >> $GITHUB_ENV
echo "$REPORT" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV

- name: Comment Benchmark Result
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const report = process.env.REPORT_CONTENT;

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Performance Limits Report');
});

const body = `### 🚀 Performance Benchmark Result\n\n\`\`\`text\n${report}\n\`\`\`\n\n<details><summary>Run Details</summary>\nTriggered by commit: ${context.sha}</details>`;

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ out
out.*
dist
*.tgz
pkg
**/profiles/*.json

# code coverage
coverage
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ bun packages/showcase/counter.ts
bun packages/showcase/dashboard.ts
```

### Profiling / Perf Regression

```bash
# 大量要素のストレス(JSON出力、--io=off で stdout を捨てて純粋な計算寄りに)
bun run profile:stress --n=10000 --frames=120 --io=off --out=profiles/stress.json

# パフォーマンス予算テスト(将来的にCIで回帰検知に使う想定)
# 例: まずは計測してから budget を詰めるのがおすすめ
bun run perf:budget --task=frame --n=10000 --iterations=30 --out=profiles/budget.json
bun run perf:budget --task=diff --rows=200 --cols=400 --iterations=20 --out=profiles/budget-diff.json

# bun test に載せる場合(CI or BTUIN_PERF=1 のときのみ実行)
CI=1 bun run test:perf
# 予算/サイズは env で上書き可能(例: BTUIN_BUDGET_FRAME_P95=120 など)
```

## 使い方(最小例)

```ts
Expand Down
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
"packages/*"
],
"scripts": {
"start": "bun run --cwd packages/btuin src/app.ts",
"build": "bun run --cwd packages/btuin build.ts",
"start": "bun run",
"build": "pnpm --filter @btuin/layout-engine build",
"lint": "oxlint packages/*",
"lint:fix": "oxlint packages/* --fix",
"format": "oxfmt packages/*",
"test": "bun test packages/btuin packages/layout-engine packages/reactivity packages/renderer packages/terminal packages/showcase",
"test:watch": "pnpm test --watch",
"test:all": "bun test packages",
"test": "bun test packages/btuin packages/layout-engine packages/reactivity packages/renderer packages/terminal",
"profiler": "bun test scripts/profiler*.spec.ts",
"profiler:stress": "bun test scripts/profiler-stress.test.ts",
"profiler:layout": "bun test scripts/profiler-layout.test.ts",
"profiler:limit": "bun test scripts/profiler-limit.spec.ts",
"check": "bunx tsc --noEmit"
},
"devDependencies": {
Expand Down
3 changes: 1 addition & 2 deletions packages/btuin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* btuin core entry point
*/

export * from "./runtime/app";
export * from "./runtime/error-boundary";
export * from "./runtime";

export * from "./view/components";

Expand Down
4 changes: 3 additions & 1 deletion packages/btuin/src/layout/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export function collectFocusTargetMap(
effectiveKey: string | undefined = element.key,
): Map<string, FocusTarget> {
const map = new Map<string, FocusTarget>();
visitFocusTargets(element, layoutMap, parentX, parentY, effectiveKey, (t) => map.set(t.focusKey, t));
visitFocusTargets(element, layoutMap, parentX, parentY, effectiveKey, (t) =>
map.set(t.focusKey, t),
);
return map;
}
Loading