소스 정보
- 저장소
- arm2arm/AstroAgentAssistant
- 최근 소스 활동
- 2026년 8월 26일 12:28
- 감지된 SKILL.md 언어
- 영어
- 스타
- 4
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/arm2arm/AstroAgentAssistant --skill webxr-dev명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
S3/MinIO operations: connectivity, transfers, read benchmarks, and matplotlib visualization templates.
Complete guide to the REANA reproducible analytics platform: Dockerized client setup, multi-backend profiles, workflow authoring patterns, S3 dataset workflows, and best practices. Covers dev/prod backends, serial workflows, REANA_WORKSPACE usage, and self-learning from finished workflows.
Complete guide to working with Arepo simulation HDF5 files: structure inspection, unit conversion, radial profiles, slice projections, and dimensionality reduction (UMAP/t-SNE) for clustering analysis.
| name | webxr-dev |
| description | Build WebXR portals and AR/VR browser apps in Three.js. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| tags | ["webxr","threejs","ar","vr","immersive-web","docker","nginx","ssl"] |
Class-level skill for building WebXR-enabled immersive web experiences — portal doors, AR overlays, VR rooms, and interactive 3D pages that run in the browser.
Triggers:
index.html, css/style.css, js/app.js, package.jsonrenderer.xr.enabled = trueimmersive-ar and immersive-vr modes, provide fallbacksrenderer.xr.setSession(), renderer.setAnimationLoop() for XR frame callbacksimmersive-ar) only works on Chrome for Android (mobile AR)immersive-vr) works on desktop Chrome with flags enabled (chrome://flags → WebXR)async enterAR() {
const session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local-floor'],
optionalFeatures: ['hand-tracking', 'dom-overlay', 'hit-test'],
domOverlay: { root: document.body }
});
this.renderer.xr.setSession(session);
// Use renderer.setAnimationLoop() for XR frame callbacks
}
async enterVR() {
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['local-floor'],
optionalFeatures: ['bounded-floor']
});
this.renderer.xr.setSession(session);
}
if (!('xr' in navigator)) {
// No WebXR — show disabled button
} else {
navigator.xr.isSessionSupported('immersive-ar').then(supported => {
if (supported) { /* enable AR button */ }
else {
// Try VR as fallback
navigator.xr.isSessionSupported('immersive-vr').then(vrSupported => {
if (vrSupported) { /* enable VR button instead */ }
else { /* show not supported */ }
});
}
});
}
Use custom GLSL shaders for portal effects — vertex displacement + fragment color mixing with time uniforms:
// Vertex: displace Z based on sin/cos of position + time
// Fragment: mix colors based on UV coords + time + spiral distance
Generate on host, COPY into image. Never generate inside Dockerfile — openssl in Alpine exits code 1 silently:
mkdir -p ssl/
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ssl/server.key -out ssl/server.crt \
-subj "/CN=localhost" 2>/dev/null
Serve both HTTP (for dev) and HTTPS (for WebXR):
server { listen 8123; ... } # HTTP for dev
server { listen 8124 ssl; ... } # HTTPS for WebXR
Dockerfile: EXPOSE 8123 8124, run with -p 8123:8123 -p 8124:8124
After COPY, files are root:root with mode 600. Fix:
docker exec <container> chown -R nginx:nginx /usr/share/nginx/html
docker exec <container> find /usr/share/nginx/html -type d -exec chmod 755 {} \;
docker exec <container> find /usr/share/nginx/html -type f -exec chmod 644 {} \;
add_header Cross-Origin-Embedder-Policy require-corp always;
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Resource-Policy cross-origin always;
HAProxy must add cross-origin headers per-domain using rspdel + rspadd.
CRITICAL: A blanket Permissions-Policy: camera=() in frontend config blocks camera for ALL domains.
# Remove blanket headers for ar.aip.de
rspdel ^Permissions-Policy:\ if { ssl_fc_sni ar.aip.de }
rspdel ^Cross-Origin-Embedder-Policy:\ if { ssl_fc_sni ar.aip.de }
rspdel ^Cross-Origin-Opener-Policy:\ if { ssl_fc_sni ar.aip.de }
rspdel ^Cross-Origin-Resource-Policy:\ if { ssl_fc_sni ar.aip.de }
# Add domain-specific headers
rspadd Cross-Origin-Embedder-Policy:\ require-corp\ if { ssl_fc_sni ar.aip.de }
rspadd Cross-Origin-Opener-Policy:\ same-origin\ if { ssl_fc_sni ar.aip.de }
rspadd Cross-Origin-Resource-Policy:\ cross-origin\ if { ssl_fc_sni ar.aip.de }
rspadd Permissions-Policy:\ geolocation=(),\ microphone=(),\ camera=(self)\ if { ssl_fc_sni ar.aip.de }
Also set forwarded headers:
http-request set-header X-Forwarded-Proto https
http-request set-header Host ar.aip.de
Verify: curl -sI https://ar.aip.de | grep -iE "permissions-policy|cross-origin"
For React-based XR, use React Three Fiber + @react-three/xr (Vite build):
git clone https://github.com/WawasCode/DefaultReactXR.git
cd DefaultReactXR && pnpm install && pnpm build
# Build outputs to dist/ — serve with any static server
Key: fix Vite config for newer @vitejs/plugin-react (remove babel key).
Set pnpm config set allow-scripts true to allow esbuild build scripts.
Use Three.js 0.160.0+ for WebXR projects. Older versions (0.126.0, 0.150.0) have unreliable XR passthrough, broken renderer.xr handling, and missing features like proper reference space management.
<!-- CORRECT — modern version with full XR support -->
<script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script>
<!-- WRONG — unreliable XR passthrough -->
<script src="https://unpkg.com/three@0.126.0/build/three.js"></script>
Renderer initialization order is device/browser-dependent. Some browsers require renderer BEFORE session, others AFTER. If passthrough shows black screen, try the opposite order.
Pattern A: Renderer BEFORE session (Chrome Android typical):
// Create renderer with XR support
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true,
preserveDrawingBuffer: false,
powerPreference: 'high-performance'
});
renderer.xr.enabled = true;
renderer.xr.setReferenceSpaceType('local-floor');
renderer.autoClear = false; // XR compositor manages clearing
// THEN request session
session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test', 'local-floor'],
optionalFeatures: ['dom-overlay'],
domOverlay: { root: uiLayer }
});
renderer.xr.setSession(session);
Pattern B: Renderer AFTER session (if Pattern A shows black):
// Request session FIRST
session = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test', 'local-floor'],
optionalFeatures: ['dom-overlay'],
domOverlay: { root: uiLayer }
});
// THEN create renderer with active session
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true,
preserveDrawingBuffer: false
});
renderer.xr.enabled = true;
renderer.xr.setSession(session); // Bind immediately
Critical settings for passthrough:
preserveDrawingBuffer: false — let XR compositor manage framebufferautoClear: false — XR session handles clearing, not Three.jsrenderer.setClearColor() — avoid or use with alpha=0scene.background = null — no background color/texturebackground: transparent !importantBLOCKS PASSTHROUGH (do NOT do any of these):
// ❌ Manual camera matrix overrides during XR frame
camera.matrixAutoUpdate = false;
camera.projectionMatrix.fromArray(view.projectionMatrix);
// ❌ Manual framebuffer clear
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// ❌ Non-transparent body/canvas backgrounds in CSS
body { background: #0a1929; } // ← leaks through in AR
Transparent background in AR: Dark CSS body backgrounds leak through the composited view. Use a CSS class toggle + inline styles:
// On AR entry:
document.body.style.background = 'transparent';
canvas.style.background = 'transparent';
document.body.classList.add('ar-active');
// In CSS — strip dark backgrounds from DOM overlays:
body.ar-active .header,
body.ar-active #ui-layer {
background: transparent !important;
backdrop-filter: none !important;
box-shadow: none !important;
}
// On session end — restore:
document.body.style.background = '';
document.body.classList.remove('ar-active');
DOM overlays in XR: Elements that should appear in XR (scanning overlays, reticle guides, status) must be children of the domOverlay.root element. Elements outside it are not composited.
// WRONG — scanning overlay outside domOverlay root, won't render in XR
<div id="scanning-overlay">...</div> <!-- sibling of #ui-layer → NOT composited -->
// CORRECT — scanning overlay INSIDE domOverlay root
<div id="ui-layer">
<header>...</header>
<div id="scanning-overlay">...</div> <!-- child of #ui-layer → composited -->
</div>
Use opencode as the primary coding agent for WebXR projects. Configure opencode with a custom OpenAI-compatible provider and run iterative tasks via opencode run in the project directory.
openssl in Alpine exits code 1 for obscure reasons. Generate on host, COPY into image.add_header is NOT allowed inside if ($request_method = 'OPTIONS') blocks in nginx. Move all add_header directives to server {} or location {} level.nginx:nginx. Fix ownership and permissions after COPY.immersive-ar only works on Chrome for Android. Samsung Browser may not support WebXR — test with Chrome.alert() in error handlers is non-UX-friendly — use styled DOM overlay instead.renderer.setAnimationLoop() — not requestAnimationFrame() during XR sessions.-k to wget.gl.clear(), camera.matrixAutoUpdate = false, manual camera matrix overrides, non-transparent body background, or DOM elements outside domOverlay.root. See "AR Camera Passthrough" section.<meta http-equiv="Cache-Control"> tags and nginx add_header Cache-Control "no-cache" to force reload on mobile. Use version query strings (style.css?v=20260730) for CSS/JS files.MeshBasicMaterial or MeshStandardMaterial with textures instead of custom fragment shaders. Test on target device early.let glowRing = null; at module level, then assign glowRing = new THREE.Mesh(...) inside init function. Three distinct failure modes (all crash AR at runtime, none caught by node --check):
When AR button appears unresponsive on mobile:
Add visible status messages — don't rely on console.log. Show each step in the DOM:
setStatus('WebXR detected, initializing...');
setStatus('✓ WebXR available, checking AR...');
setStatus('✓ AR supported, starting session...');
Add alert() for button click confirmation — temporary debug to verify event listener works:
startButton.addEventListener('click', () => {
alert('Button clicked! navigator.xr: ' + !!navigator.xr);
activateXR();
});
Force cache refresh — tell user to:
?v=2 to URL: https://ar.aip.de?v=2Check for Samsung Browser vs Chrome — Samsung Browser may lack WebXR support. Recommend Chrome + ARCore.
Graceful degradation — if immersive-ar fails, try immersive-vr fallback or show clear error message with device requirements.
Syntax errors prevent all JavaScript — duplicate variable declarations (const glowMaterial twice) cause silent script failure. Validate with node --check before deploying.
Camera passthrough troubleshooting — if screen stays black after session starts:
preserveDrawingBuffer: false and autoClear: falselet portal = null;portal = new THREE.Mesh(...)portal.parentTypeError: Cannot read properties of null (reading 'parent')letconst shadows the outer let — const portalSceneGroup = new THREE.Group(); inside activateXR() re-declares a new local that shadows the outer let portalSceneGroup = null;. The top-level render() still reads null → ReferenceError/TypeError once the portal is placed, killing the animation loop. When you intend to fill an outer-scope variable, ASSIGN (portalGroup = new THREE.Group()), never const-redeclare..parent — if portal (the mesh) is added to portalGroup, portal.parent works only after portal is assigned. Safer: keep a top-level let portalGroup = null; and use it directly instead of deriving from portal.parent.
Quick verification after an AR refactor: grep -nE "let portal|portal = new THREE.Mesh|portalGroup = new|portalSceneGroup = " and eyeball that each outer let has a matching assignment. node --check only catches syntax, never scoping/runtime null-derefs.TextureLoader.load() to catch CORS or network issues on mobile.