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
4 changes: 2 additions & 2 deletions .github/workflows/generator-generic-ossf-slsa3-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ name: SLSA generic generator
on:
workflow_dispatch:
release:
types: [created]
types: [published]

jobs:
build:
runs-on: ubuntu-latest
outputs:
digests: ${{ steps.hash.outputs.digests }}
digests: ${{ steps.hash.outputs.hashes }}

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/google.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ name: 'Build and Deploy to GKE'
on:
push:
branches:
- '"main"'
- 'main'

env:
PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/greetings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/first-interaction@v1
- uses: actions/first-interaction@3c71ce730280171fd1cfb57c00c774f8998586f7 # v1.3.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: "Message that will be displayed on users' first issue"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/hadolint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# hadoint is a Dockerfile linter written in Haskell
# hadolint is a Dockerfile linter written in Haskell
# that helps you build best practice Docker images.
# More details at https://github.com/hadolint/hadolint

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Smoke test static app
run: node scripts/smoke-test-static.mjs

- name: Prepare static site
shell: bash
run: |
Expand Down
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
RKIX3/
├─ index.html # Single-file AI Studio UI
├─ README.md # Trang giới thiệu chuyên nghiệp trên GitHub
├─ scripts/smoke-test-static.mjs # Smoke test HTML/JS trước khi deploy
├─ 1780136894650-Photoroom.png # Logo chính
└─ .github/workflows/static.yml # Build _site + deploy GitHub Pages
```
Expand All @@ -66,17 +67,26 @@ python3 -m http.server 4173
# mở http://127.0.0.1:4173
```

## 🧪 Kiểm thử

```bash
node scripts/smoke-test-static.mjs
```

Smoke test sẽ kiểm tra cấu trúc route chính, sự tồn tại của chat input/send button, cú pháp JavaScript inline và guard chống render raw user message vào `innerHTML`.

## 🚀 Deploy GitHub Pages

Workflow `.github/workflows/static.yml` sẽ:
Workflow chính `.github/workflows/static.yml` sẽ:

1. Checkout source.
2. Setup GitHub Pages.
3. Tạo `_site` chứa `index.html`, ảnh và file đánh dấu static site.
4. Upload artifact Pages.
5. Deploy bằng `actions/deploy-pages`.
2. Chạy smoke test static app bằng `node scripts/smoke-test-static.mjs`.
3. Setup GitHub Pages.
4. Tạo `_site` chứa `index.html`, ảnh và file đánh dấu static site.
5. Upload artifact Pages.
6. Deploy bằng `actions/deploy-pages`.

> Nếu GitHub vẫn báo lỗi deploy, hãy vào **Settings → Pages → Build and deployment** và chọn **Source: GitHub Actions** cho repository.
> Nếu GitHub vẫn báo lỗi deploy, hãy vào **Settings → Pages → Build and deployment** và chọn **Source: GitHub Actions** cho repository. Các workflow mẫu khác trong `.github/workflows/` chỉ nên được bật khi dự án thật sự dùng stack tương ứng.

## 🏅 Huy hiệu dự án

Expand Down Expand Up @@ -121,13 +131,13 @@ Workflow `.github/workflows/static.yml` sẽ:

## ✅ Ba xung đột đã được chốt

- **Workflow Pages**: chỉ giữ một pipeline static ở `.github/workflows/static.yml`, dùng `_site` làm artifact triển khai.
- **Workflow Pages**: `.github/workflows/static.yml` là pipeline deploy chính, chạy smoke test rồi dùng `_site` làm artifact triển khai.
- **Tài liệu GitHub**: README là trang giới thiệu chính thức của RKIX3, không còn nội dung cũ trùng lặp.
- **Web app RKIX3**: `index.html` tiếp tục là nguồn giao diện single-file được workflow copy trực tiếp khi deploy.

## 🗺️ Roadmap

- [x] Giao diện RKIX3 Studio single-file.
- [x] Giao diện RKIX3 Studio single-file, ưu tiên mobile web với shell giống app gốc.
- [x] Command Center cho CLI/mobile workflow.
- [x] Demo Offline để sinh blueprint khi chưa có API key.
- [x] GitHub Pages static deploy workflow.
Expand Down
335 changes: 242 additions & 93 deletions index.html

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions scripts/smoke-test-static.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { spawnSync } from 'node:child_process';

const html = readFileSync('index.html', 'utf8');
const failures = [];

function assert(condition, message) {
if (!condition) failures.push(message);
}

assert(html.includes('<!DOCTYPE html>'), 'index.html must declare an HTML doctype.');
assert(html.includes('<section id="home"'), 'index.html must include the home route container.');
assert(html.includes('<section id="ai"'), 'index.html must include the AI workspace route container.');
assert(html.includes('<section id="workspace"'), 'index.html must include the workspace route container.');
assert(html.includes('<section id="code"'), 'index.html must include the code route container.');
assert(html.includes('<section id="profile"'), 'index.html must include the profile route container.');
assert(html.includes('overflow-x: clip') || html.includes('overflow-x: hidden'), 'Mobile layout must explicitly prevent horizontal overflow.');
assert(html.includes('position: fixed') && html.includes('bottom: 0'), 'Bottom navigation must stay fixed to the viewport bottom.');
assert(html.includes('min-height: 64px'), 'Bottom navigation buttons must keep a larger mobile tap target.');
assert(html.includes('id="chatInput"'), 'AI workspace must expose the chat input.');
assert(html.includes('id="sendChat"'), 'AI workspace must expose the send button.');
assert(html.includes('esc(m.text)'), 'Chat messages must be escaped before rendering into innerHTML.');
assert(!html.includes('<p>${m.text}</p>'), 'Chat messages must not render raw user text into innerHTML.');

const scripts = [...html.matchAll(/<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/gi)].map((match) => match[1]);
assert(scripts.length > 0, 'index.html must include an inline script to validate.');

if (scripts.length > 0) {
const workDir = mkdtempSync(join(tmpdir(), 'rkix3-smoke-'));
const scriptPath = join(workDir, 'index-inline-script.js');
writeFileSync(scriptPath, scripts.join('\n'), 'utf8');
const result = spawnSync(process.execPath, ['--check', scriptPath], { encoding: 'utf8' });
if (result.status !== 0) {
failures.push(`Inline JavaScript failed syntax check:\n${result.stderr || result.stdout}`);
}
rmSync(workDir, { recursive: true, force: true });
}

if (failures.length > 0) {
console.error('Static smoke test failed:');
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}

console.log('Static smoke test passed.');
Loading