From 8af84b424a9b13dc29308423cd2268bd8a208afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?= <252359928+Huynhthuongg@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:45:10 +0700 Subject: [PATCH 1/6] Optimize RKIX3 mobile web interface --- .github/workflows/hadolint.yml | 2 +- .github/workflows/static.yml | 3 + README.md | 26 ++- index.html | 322 +++++++++++++++++++++++---------- scripts/smoke-test-static.mjs | 41 +++++ 5 files changed, 293 insertions(+), 101 deletions(-) create mode 100644 scripts/smoke-test-static.mjs diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index dc73566..a264df3 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -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 diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 09631a2..8e55f57 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -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: | diff --git a/README.md b/README.md index 91f1766..66293ea 100644 --- a/README.md +++ b/README.md @@ -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 ``` @@ -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 @@ -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. diff --git a/index.html b/index.html index 12bb580..3f12d05 100644 --- a/index.html +++ b/index.html @@ -1,118 +1,256 @@ - + - RKIX3 — Developer Workspace Platform - + RKIX3 — Mobile AI Workspace +
- -

RKIX3 Command Layer

Dashboard

main · healthy
-
RKIX3
+ + +
+
+ + + +
+ +
+
+
+
+
+
+ + +
+ diff --git a/scripts/smoke-test-static.mjs b/scripts/smoke-test-static.mjs new file mode 100644 index 0000000..6b82bd4 --- /dev/null +++ b/scripts/smoke-test-static.mjs @@ -0,0 +1,41 @@ +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(''), 'index.html must declare an HTML doctype.'); +assert(html.includes('
${m.text}

'), 'Chat messages must not render raw user text into innerHTML.'); + +const scripts = [...html.matchAll(/]*)?>([\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.'); From bb5e175b17989488dfdcfb95626b1f758e4567b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?= <252359928+Huynhthuongg@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:02:58 +0700 Subject: [PATCH 2/6] Fix mobile route overflow --- index.html | 32 +++++++++++++++++++------------- scripts/smoke-test-static.mjs | 4 ++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/index.html b/index.html index 3f12d05..a81cb46 100644 --- a/index.html +++ b/index.html @@ -23,8 +23,8 @@ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } - * { box-sizing: border-box; } - html { background: var(--bg); } + *, *::before, *::after { box-sizing: border-box; min-width: 0; } + html { width: 100%; max-width: 100%; overflow-x: hidden; background: var(--bg); } body { margin: 0; min-height: 100vh; @@ -33,6 +33,8 @@ radial-gradient(circle at 50% 16%, rgba(18,108,255,.28), transparent 21rem), radial-gradient(circle at 12% 8%, rgba(22,200,255,.16), transparent 18rem), linear-gradient(145deg, #020711 0%, #020915 48%, #00040a 100%); + width: 100%; + max-width: 100%; overflow-x: hidden; } body::before { @@ -61,11 +63,12 @@ button, input { font: inherit; } button { cursor: pointer; } - img { max-width: 100%; display: block; } + img, svg { max-width: 100%; display: block; } h1, h2, h3, p { margin-top: 0; } + pre, code { max-width: 100%; overflow-wrap: anywhere; white-space: pre-wrap; } - .app { min-height: 100vh; } - .mobile-shell { width: min(100%, 460px); margin: 0 auto; min-height: 100vh; padding: 18px 16px 92px; } + .app { width: 100%; max-width: 100%; min-height: 100vh; overflow-x: clip; } + .mobile-shell { width: min(100%, 460px); max-width: 100%; margin: 0 auto; min-height: 100vh; padding: 18px clamp(12px, 4vw, 16px) 92px; overflow-x: clip; } .topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 48px; } .icon-button, .send-button, .prompt-tool { border: 1px solid var(--line-soft); @@ -94,6 +97,7 @@ .prompt-card { width: 100%; + max-width: 100%; border: 1px solid rgba(18,108,255,.82); border-radius: 22px; background: linear-gradient(145deg, rgba(4,18,39,.84), rgba(2,9,22,.92)); @@ -117,7 +121,7 @@ input::placeholder { color: #8f9db0; } .send-button { width: 42px; height: 42px; border-radius: 14px; border-color: rgba(18,108,255,.72); background: linear-gradient(135deg, #177aff, #0750d9); box-shadow: 0 12px 26px rgba(18,108,255,.3); } - .quick-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 16px; } + .quick-grid { width: 100%; max-width: 100%; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 16px; } .quick-card { min-height: 124px; border: 1px solid var(--line); @@ -138,15 +142,17 @@ .quick-card strong { color: #fff; font-size: 15px; } .quick-card span { color: #bdcadd; font-size: 12px; line-height: 1.35; } - .mobile-page { display: none; animation: rise .22s ease both; } + .mobile-page { display: none; width: 100%; max-width: 100%; overflow-x: clip; animation: rise .22s ease both; } + .mobile-page:not(.active) { display: none !important; } .mobile-page.active { display: block; } @keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } - .panel { border: 1px solid var(--line); border-radius: 18px; background: var(--panel); padding: 16px; box-shadow: 0 20px 54px rgba(0,0,0,.24); } + .panel { width: 100%; max-width: 100%; overflow: hidden; border: 1px solid var(--line); border-radius: 18px; background: var(--panel); padding: 16px; box-shadow: 0 20px 54px rgba(0,0,0,.24); } .section-title { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin: 18px 0 10px; } .section-title h2 { margin: 0; font-size: 18px; } .muted { color: var(--muted); } - .list { display: grid; gap: 10px; } - .list-item { border: 1px solid var(--line-soft); border-radius: 14px; background: rgba(255,255,255,.035); padding: 12px; display: flex; justify-content: space-between; gap: 12px; align-items: center; } + .list { width: 100%; max-width: 100%; display: grid; gap: 10px; } + .list-item { width: 100%; max-width: 100%; overflow: hidden; border: 1px solid var(--line-soft); border-radius: 14px; background: rgba(255,255,255,.035); padding: 12px; display: flex; justify-content: space-between; gap: 12px; align-items: center; } + .list-item > * { min-width: 0; overflow-wrap: anywhere; } .status-dot { width: 9px; height: 9px; border-radius: 3px; background: var(--cyan); box-shadow: 0 0 14px var(--cyan); } .bottom-nav { @@ -248,9 +254,9 @@ function renderWorkspace(){$('#workspace').innerHTML=`

Dự án

${entities.projects.length} mục
${entities.projects.map(p=>`
${p[0]}

${p[1]}

${p[2]}
`).join('')}
`} function renderCode(){$('#code').innerHTML=`

Thư viện Code

Snippets
export async function POST(req) {\n  const { prompt } = await req.json();\n  return Response.json({ output: prompt });\n}
`} function renderProfile(){$('#profile').innerHTML=`

Cài đặt

RKIX3
Giao diện mobile-first
Nút vuông mỏng
Icon line xanh
`} -function setRoute(id){const route=routes.find(r=>r[0]===id)||routes[0];$$('.mobile-page').forEach(p=>p.classList.toggle('active',p.id===route[0]));$$('[data-route]').forEach(b=>b.classList.toggle('active',b.dataset.route===route[0]));$('#drawer').classList.remove('open')} -function bind(){document.body.addEventListener('click',e=>{const route=e.target.closest('[data-route]');if(route)setRoute(route.dataset.route);if(e.target.closest('#sendChat')){const input=$('#chatInput');if(!input||!input.value.trim())return;entities.messages.push({role:'user',text:input.value.trim()},{role:'agent',text:'Đã nhận yêu cầu. RKIX3 sẽ chia nhỏ thành code, build, test và deploy.'});input.value='';renderChat();$('#chatLog').scrollTop=$('#chatLog').scrollHeight}});$('#mobileMenu').onclick=()=>$('#drawer').classList.add('open');$('#drawerClose').onclick=()=>$('#drawer').classList.remove('open')} -function init(){document.querySelector('[aria-label="Mở menu"]').innerHTML=icons.menu;document.querySelector('[aria-label="Tuỳ chọn"]').innerHTML=icons.menu;document.querySelector('[aria-label="Đóng menu"]').innerHTML=icons.close;bottomNav.innerHTML=navHTML();sideNav.innerHTML=drawerHTML();drawerNav.innerHTML=drawerHTML();renderHome();renderAI();renderWorkspace();renderCode();renderProfile();bind();setRoute('home')} +function setRoute(id){const route=routes.find(r=>r[0]===id)||routes[0];$$('.mobile-page').forEach(p=>{const active=p.id===route[0];p.classList.toggle('active',active);p.setAttribute('aria-hidden',String(!active))});$$('[data-route]').forEach(b=>b.classList.toggle('active',b.dataset.route===route[0]));$('#drawer').classList.remove('open');$('#drawer').setAttribute('aria-hidden','true');if(location.hash.slice(1)!==route[0])history.replaceState(null,'',`#${route[0]}`);window.scrollTo(0,0)} +function bind(){document.body.addEventListener('click',e=>{const route=e.target.closest('[data-route]');if(route)setRoute(route.dataset.route);if(e.target.closest('#sendChat')){const input=$('#chatInput');if(!input||!input.value.trim())return;entities.messages.push({role:'user',text:input.value.trim()},{role:'agent',text:'Đã nhận yêu cầu. RKIX3 sẽ chia nhỏ thành code, build, test và deploy.'});input.value='';renderChat();$('#chatLog').scrollTop=$('#chatLog').scrollHeight}});$('#mobileMenu').onclick=()=>{$('#drawer').classList.add('open');$('#drawer').setAttribute('aria-hidden','false')};$('#drawerClose').onclick=()=>{$('#drawer').classList.remove('open');$('#drawer').setAttribute('aria-hidden','true')}} +function init(){document.querySelector('[aria-label="Mở menu"]').innerHTML=icons.menu;document.querySelector('[aria-label="Tuỳ chọn"]').innerHTML=icons.menu;document.querySelector('[aria-label="Đóng menu"]').innerHTML=icons.close;bottomNav.innerHTML=navHTML();sideNav.innerHTML=drawerHTML();drawerNav.innerHTML=drawerHTML();renderHome();renderAI();renderWorkspace();renderCode();renderProfile();bind();window.addEventListener('hashchange',()=>setRoute(location.hash.slice(1)||'home'));setRoute(location.hash.slice(1)||'home')} init(); diff --git a/scripts/smoke-test-static.mjs b/scripts/smoke-test-static.mjs index 6b82bd4..8a14429 100644 --- a/scripts/smoke-test-static.mjs +++ b/scripts/smoke-test-static.mjs @@ -13,6 +13,10 @@ function assert(condition, message) { assert(html.includes(''), 'index.html must declare an HTML doctype.'); assert(html.includes('
Date: Thu, 4 Jun 2026 21:04:02 +0700 Subject: [PATCH 3/6] Update index.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Huỳnh Thương <252359928+Huynhthuongg@users.noreply.github.com> --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index a81cb46..245ecd2 100644 --- a/index.html +++ b/index.html @@ -247,7 +247,7 @@ const quick=[['code','Lập trình','Viết code, debug và tối ưu',icons.code],['workspace','Build','Xây dựng sản phẩm nhanh chóng',icons.cube],['profile','Tự động hoá','Tự động hoá quy trình và công việc',icons.gear],['ai','AI thông minh','Hỗ trợ bởi AI thế hệ mới',icons.bot]]; const entities={messages:[{role:'agent',text:'RKIX3 đã sẵn sàng. Hỏi bất kỳ điều gì để bắt đầu build.'}],projects:[['Nebula Console','Đang build','76%'],['AgentOps CLI','Lên kế hoạch','42%'],['Docs Pulse','Review','88%']]}; function navHTML(){return routes.map(r=>``).join('')} -function drawerHTML(){return [['ai','Đoạn chat mới',icons.code],['home','Tìm kiếm đoạn chat',icons.menu],['code','Thư viện',icons.library],['workspace','Dự án',icons.folder],['profile','Ứng dụng',icons.apps]].map(r=>``).join('')} +function drawerHTML(){return [['home','Đoạn chat mới',icons.code],['home','Tìm kiếm đoạn chat',icons.menu],['code','Thư viện',icons.library],['workspace','Dự án',icons.folder],['profile','Ứng dụng',icons.apps]].map(r=>``).join('')} function renderChat(){$('#chatLog').innerHTML=entities.messages.map(m=>`
${esc(m.text)}
`).join('')} function renderHome(){$('#home').innerHTML=`

Chúng ta nên bắt đầu từ đâu?

Lập trình · Code · Build · Tự động hoá trên một giao diện mobile gọn, sáng và nhanh.

${quick.map(q=>``).join('')}
`;renderChat()} function renderAI(){$('#ai').innerHTML=`

AI thông minh

Online

Tập trung cho mobile trước: prompt nhanh, nút mỏng, icon xanh và layout giống app RKIX3 ban đầu.

Generate code
Debug issue
Plan sprint
`} From f7b2c3df22f71dc4bc7e7176bbbdb0caf049174a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?= <252359928+Huynhthuongg@users.noreply.github.com> Date: Sun, 7 Jun 2026 10:43:24 +0700 Subject: [PATCH 4/6] Update .github/workflows/generator-generic-ossf-slsa3-publish.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Huỳnh Thương <252359928+Huynhthuongg@users.noreply.github.com> --- .github/workflows/generator-generic-ossf-slsa3-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generator-generic-ossf-slsa3-publish.yml b/.github/workflows/generator-generic-ossf-slsa3-publish.yml index 35c829b..945c409 100644 --- a/.github/workflows/generator-generic-ossf-slsa3-publish.yml +++ b/.github/workflows/generator-generic-ossf-slsa3-publish.yml @@ -20,7 +20,7 @@ jobs: build: runs-on: ubuntu-latest outputs: - digests: ${{ steps.hash.outputs.digests }} + digests: ${{ steps.hash.outputs.hashes }} steps: - uses: actions/checkout@v4 From bd9e9daba756815adf44e09a9e0203cd07431365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?= <252359928+Huynhthuongg@users.noreply.github.com> Date: Sun, 7 Jun 2026 10:43:34 +0700 Subject: [PATCH 5/6] Update .github/workflows/google.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Huỳnh Thương <252359928+Huynhthuongg@users.noreply.github.com> --- .github/workflows/google.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml index 0b5c7d1..5c618df 100644 --- a/.github/workflows/google.yml +++ b/.github/workflows/google.yml @@ -103,7 +103,7 @@ jobs: # Set up kustomize - name: 'Set up Kustomize' run: |- - curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + curl -sSfL https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz | tar -xz chmod u+x ./kustomize # Deploy the Docker image to the GKE cluster From 45b6bbfd6f2f4aba9442e504c316eb0769856879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=E1=BB=B3nh=20Th=C6=B0=C6=A1ng?= <252359928+Huynhthuongg@users.noreply.github.com> Date: Sun, 7 Jun 2026 10:44:17 +0700 Subject: [PATCH 6/6] Update CNAME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Huỳnh Thương <252359928+Huynhthuongg@users.noreply.github.com> --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index 2037010..2c40239 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -app.github.rkix \ No newline at end of file +app.rkix.com \ No newline at end of file