{"id":50412291,"url":"https://github.com/cskwork/cube-site","last_synced_at":"2026-05-31T04:04:52.883Z","repository":{"id":361015025,"uuid":"1251426527","full_name":"cskwork/cube-site","owner":"cskwork","description":"3D interactive cube portal — render any website inside a rotating 3D cube. HTML-in-Canvas / Three.js / Vite.","archived":false,"fork":false,"pushed_at":"2026-05-28T20:10:59.000Z","size":81,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T22:07:45.800Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://html-in-canvas-edu-app.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cskwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-27T15:07:16.000Z","updated_at":"2026-05-28T20:11:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cskwork/cube-site","commit_stats":null,"previous_names":["cskwork/cube-site"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/cskwork/cube-site","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cskwork%2Fcube-site","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cskwork%2Fcube-site/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cskwork%2Fcube-site/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cskwork%2Fcube-site/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cskwork","download_url":"https://codeload.github.com/cskwork/cube-site/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cskwork%2Fcube-site/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33718494,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T04:04:51.801Z","updated_at":"2026-05-31T04:04:52.872Z","avatar_url":"https://github.com/cskwork.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cube Site\n\n\u003e Render **any website** inside an interactive 3D cube. Decorate the cube with trendy \"꾸미기\" presets, drop stickers, imprint text, and share the whole thing — site + decoration — through a single URL hash.\n\nA Three.js 3D site-portal: one cube face shows a live, interactive `\u003ciframe\u003e` of any URL, tracked to the face each frame. It **also** ships a real, spec-correct implementation of Chrome's experimental **HTML-in-Canvas** API (`ctx.drawElementImage()`) as an opt-in, capability-gated live-face mode that paints live DOM onto the cube via a WebGL texture.\n\n```\n┌──────────────────────────┐  ┌──────────────────┐\n│                          │  │  꾸미기          │\n│        ╱──────────╲      │  │  글자 새기기     │\n│       ╱            ╲     │  │  공유            │\n│      │  ◢ live ◣   │    │  │ ──────────────── │\n│      │  iframe    │     │  │  URL:            │\n│       ╲           ╱     │  │  [https://...]   │\n│        ╲─────────╱      │  │  적용            │\n│                          │  │ ──────────────── │\n│  drag empty space        │  │ 초기화 · 공유 링크│\n└──────────────────────────┘  └──────────────────┘\n```\n\n## ✨ What it does\n\n- **Cube IS the site.** One face of the cube is a real `\u003ciframe\u003e`, screen-tracked to the live face each frame over the Three.js WebGL canvas. You click buttons, scroll, type, submit forms — through the cube. (A 2D overlay was chosen over `CSS3DRenderer` because Chrome's CSS-3D iframe hit-testing is unreliable.)\n- **Three live-face modes.** `실제 사이트` (interactive iframe, default), `스타일 카드` (a Canvas2D site-preview card for sites that block framing), and `HTML-in-Canvas` (experimental — see below).\n- **Decorate the other 5 faces.** 5 trend presets (Y2K Cyber, Frutiger Aero, Soft Pastel, Holographic, Bento), color/glow/hue/radius sliders, 24 emoji stickers, per-face text imprint.\n- **Swap any URL at runtime.** A `라이브 사이트 URL` field in the 공유 tab — paste any URL, hit 적용, and the cube becomes that site.\n- **Share via URL hash.** The decoration state AND the active URL serialize to a compact base64url hash. Copy the link, paste in another tab/device, identical cube reappears.\n- **Pure static site.** Vite + TypeScript + Three.js. `npm run build` emits `dist/` that drops onto GitHub Pages / Vercel / Netlify / Cloudflare Pages / S3 with no backend.\n- **Korean-first UI** following the [10 award-worthy web design rules](https://github.com/cskwork/web-design-10-rules) — tokens, 8pt rhythm, instant 5-second clarity, prefers-reduced-motion, WCAG-grade focus rings.\n\n## 🚀 Quick start\n\n```bash\ngit clone \u003cthis-repo\u003e\ncd cube-site\nnpm install\ncp .env.example .env     # optional — TARGET_URL etc. are runtime-editable\nnpm run dev              # http://localhost:5173\n```\n\nProduction build:\n\n```bash\nnpm run build            # → dist/\nnpx serve dist           # smoke-test locally on http://localhost:3000\n```\n\n## 🧩 How HTML-in-Canvas fits\n\nChrome's [HTML-in-Canvas](https://developer.chrome.com/blog/html-in-canvas-origin-trial) (WICG `drawElementImage`) lets you paint live DOM into a `\u003ccanvas\u003e`. The `HTML-in-Canvas` live-face mode is a real implementation of it:\n\n1. A hidden `\u003ccanvas layoutsubtree\u003e` hosts a live HTML panel as its direct child ([`src/htmlCanvas/livePanel.ts`](src/htmlCanvas/livePanel.ts)).\n2. On each `onpaint`, `ctx.drawElementImage(panel, 0, 0)` snapshots that DOM into the canvas ([`src/htmlCanvas/adapter.ts`](src/htmlCanvas/adapter.ts)).\n3. That canvas backs a `THREE.CanvasTexture` mapped onto the rotating cube's live face ([`src/htmlCanvas/liveFace.ts`](src/htmlCanvas/liveFace.ts)) — so it's genuinely *HTML → canvas → WebGL*.\n\nThe adapter feature-detects `ctx.drawElementImage` (`detectMode()`); the [status banner](src/ui/banner.ts) reports whether native mode is active, and the mode pill is disabled with an explanation when it isn't.\n\n**This is an experimental, single-browser feature** — it runs only on Chromium 147+ with the `chrome://flags/#canvas-draw-element` flag, or Chrome 148–151 with an Origin-Trial token; never Firefox/Safari. So `실제 사이트` (iframe) is the honest cross-browser default, and the `HTML-in-Canvas` mode gracefully falls back to the iframe when unsupported.\n\n## 🌐 Live demo\n\n- **GitHub Pages (production):** https://cskwork.github.io/cube-site/\n\nTo experience the experimental HTML-in-Canvas mode, open it in Chrome/Brave 147+ with `chrome://flags/#canvas-draw-element` enabled, then pick `HTML-in-Canvas` in the 공유 tab's 미리보기 모드.\n\n## 📦 Deploy\n\n### GitHub Pages (recommended for OSS)\n\nThe repo ships with [`.github/workflows/pages.yml`](.github/workflows/pages.yml). On every push to `main`:\n\n1. CI runs typecheck + tests.\n2. Builds with `BASE_PATH=/\u003crepo-name\u003e/`.\n3. Publishes `dist/` to the `gh-pages` environment.\n\nEnable Pages in repo Settings → Pages → Build and deployment → Source: **GitHub Actions**.\n\nOptional repo secrets / variables:\n- `OT_TOKEN` (secret) — Chrome HTML-in-Canvas Origin Trial token.\n- `TARGET_URL` (variable) — default URL the cube loads on first visit.\n\n### Vercel\n\n```bash\nnpm i -g vercel\nvercel --prod\n```\n\nOr click the GitHub → Vercel \"Import Project\" button — [`vercel.json`](vercel.json) is already wired (build `npm run build`, output `dist`, immutable asset caching).\n\nSet `TARGET_URL` and `OT_TOKEN` as Vercel project env vars if you want non-default initial URL or to ship the OT meta tag.\n\n### Netlify\n\n```bash\nnpm i -g netlify-cli\nnetlify deploy --prod --dir=dist\n```\n\n### Cloudflare Pages\n\n```bash\nnpm i -g wrangler\nwrangler pages deploy dist --project-name=cube-site\n```\n\n### S3 + CloudFront\n\n```bash\nBUCKET=my-bucket DISTRIBUTION_ID=E123ABC ./deploy/s3.sh\n```\n\nThe bundled `deploy/*.sh` scripts cover all four.\n\n## ⚙️ Configuration\n\nEverything is optional — the app boots with sane defaults and the 공유 tab lets users override the URL at runtime.\n\n| Env var | Default | Notes |\n|---------|---------|-------|\n| `TARGET_URL` | `https://example.com` | Default URL the cube loads. Runtime-overridable in 공유 tab. |\n| `TARGET_NAME` | `Live Site` | Brand text shown in the styled \"card\" fallback mode. |\n| `TARGET_TITLE` | `내 사이트,\\n바로 입장` | Card mode hero title. |\n| `TARGET_SUB` | `원하는 어떤 URL이든 큐브 안에 띄울 수 있어요.` | Card mode subline. |\n| `TARGET_CHIPS` | `3D · HTML in Canvas · Vite` | Comma-separated card chips. |\n| `TARGET_CTA_LABEL` | `입장` | Card mode CTA. |\n| `TARGET_TAGLINE` | `3D 사이트 큐브` | Brand subline. |\n| `OT_TOKEN` | _empty_ | Chrome HTML-in-Canvas Origin Trial token. |\n| `BASE_PATH` | `/` | Sub-path for GitHub Pages project pages, etc. |\n\n## 🧪 Scripts\n\n```bash\nnpm run dev         # Vite dev server\nnpm run build       # production build → dist/\nnpm run preview     # preview the production build\nnpm run typecheck   # tsc --noEmit\nnpm run test        # vitest (state validation, hash codec, html-canvas adapter, tools panel)\nnpm run verify      # typecheck + test + build (CI gate)\n```\n\n## 🧱 Architecture\n\n```\nsrc/\n├── main.ts\n├── app/\n│   ├── bootstrap.ts        wires state, scene, tools, live-canvas, banner\n│   ├── state.ts            AppState + Store + localStorage + validate()\n│   └── state.test.ts\n├── htmlCanvas/\n│   ├── adapter.ts          drawElementImage detect + \u003ccanvas layoutsubtree\u003e source\n│   ├── adapter.test.ts\n│   ├── livePanel.ts        the live DOM painted onto the face\n│   └── liveFace.ts         drawElementImage → CanvasTexture → cube controller\n├── dice/\n│   ├── scene.ts            Three.js cube + 2D iframe overlay + live-face texture swap\n│   └── faceTextures.ts     per-face CanvasTexture painter (+ live preview card)\n├── share/\n│   ├── hash.ts             state ↔ base64url URL-hash codec (validated)\n│   └── hash.test.ts\n├── ui/\n│   ├── tools.ts            tabs + URL input + mode pills + footer\n│   ├── tools.test.ts\n│   ├── banner.ts           HTML-in-Canvas status banner\n│   └── toast.ts\n├── util/dom.ts\n└── styles/\n    ├── tokens.css          design tokens (8pt rhythm, type scale, motion)\n    ├── base.css            reset + reduced-motion\n    ├── layout.css          2-pane shell (stage | tools)\n    └── components.css      buttons, sliders, presets, mode pills\n```\n\n## ⚠️ Known limitations\n\n- **Cross-origin popups.** A target site's `target=\"_blank\"` links or `window.open` calls cannot be redirected back into the cube iframe — by browser security spec, cross-origin documents can't be re-targeted from outside. For same-origin targets, the app injects `\u003cbase target=\"_self\"\u003e` on load so internal nav stays in-cube.\n- **X-Frame-Options.** Sites that serve `X-Frame-Options: DENY` or strict `Content-Security-Policy: frame-ancestors` will load blank. Toggle the 공유 tab's \"스타일 카드\" mode to hide the empty iframe and show the WebGL face decoration instead.\n- **HTML-in-Canvas is experimental \u0026 Chromium-only.** The `HTML-in-Canvas` mode needs Chromium 147+ with `chrome://flags/#canvas-draw-element`, or an Origin-Trial token (trial runs ~M148–M151, 2026; it expires). Firefox/Safari are unsupported. The iframe/card modes need none of this and work everywhere.\n- **Origin Trial on a real deploy.** GitHub Pages cannot send an `Origin-Trial` response header, so the only way to enable the OT in production is the `\u003cmeta http-equiv=\"origin-trial\"\u003e` slot — set the `OT_TOKEN` repo secret (CI injects it via `%OT_TOKEN%`) with a token registered for `https://cskwork.github.io`. Note: `*.vercel.app` is on the Public Suffix List, so no wildcard Vercel token can be issued — use a custom domain (Vercel *can* set the `Origin-Trial` response header) or register per preview URL.\n\n## ♿ Accessibility\n\n- All interactive elements have visible focus rings and ARIA labels.\n- Keyboard navigation supported across the tools panel.\n- Body contrast ≥ 4.5:1; large text ≥ 3:1.\n- `prefers-reduced-motion` halts idle cube spin.\n- Touch targets ≥ 44×44 px.\n- Semantic HTML first (`section`, `aside`, `button`, `h1`/`h2`).\n\n## 🧠 Design principles followed\n\nBuilt against the [cskwork/web-design-10-rules](https://github.com/cskwork/web-design-10-rules) checklist — see the original repo for the full rationale behind every principle.\n\n## 📦 License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcskwork%2Fcube-site","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcskwork%2Fcube-site","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcskwork%2Fcube-site/lists"}