An open API service indexing awesome lists of open source software.

https://github.com/developer-1px/interactive-os

interactive-os for saas app
https://github.com/developer-1px/interactive-os

Last synced: 21 days ago
JSON representation

interactive-os for saas app

Awesome Lists containing this project

README

          

# ๐ŸŒŒ Interactive OS

> ์›น์„ ์œ„ํ•œ ๋ฒ”์šฉ ์ƒํ˜ธ์ž‘์šฉ ์ธํ”„๋ผ. AI๊ฐ€ ์„ ์–ธํ•˜๋ฉด, OS๊ฐ€ ๋ณด์žฅํ•œ๋‹ค.

---

## Why โ€” AI ์‹œ๋Œ€, ํ”„๋ก ํŠธ์—”๋“œ์˜ ๊ฒ€์ฆ ๋ฌธ์ œ

AI๊ฐ€ ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ์‹œ๋Œ€๊ฐ€ ์™”๋‹ค. ๋ฐฑ์—”๋“œ๋Š” ์ˆœ์กฐ๋กญ๋‹ค. ํ•จ์ˆ˜๋ฅผ ์งœ๊ณ , ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ฆฌ๊ณ , `assert(output === expected)`๋กœ ๋งž๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ์ด ๋ช…ํ™•ํ•˜๋‹ˆ๊นŒ AI๊ฐ€ ์Šค์Šค๋กœ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๋ฅผ ๋‹ซ์„ ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋‹ค๋ฅด๋‹ค. AI๊ฐ€ ๋งŒ๋“  UI๋Š” ์Šคํฌ๋ฆฐ์ƒท์œผ๋กœ ๋ณด๋ฉด ๊ฝค ๊ดœ์ฐฎ๋‹ค. ๋žœ๋”ฉ ํŽ˜์ด์ง€, ๋Œ€์‹œ๋ณด๋“œ, ๋งˆ์ผ€ํŒ… ์‚ฌ์ดํŠธ โ€” ์„ธ์ƒ์ด "ํ”„๋ก ํŠธ์—”๋“œ๋„ AI๊ฐ€ ๋Œ€์ฒดํ•  ๊ฒƒ"์ด๋ผ๊ณ  ๋งํ•˜๋Š” ์ด์œ ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ Tab ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ๋ด๋ผ.

ํ‚ค๋ณด๋“œ๋กœ ์ด๋™์ด ๋˜๋‚˜? ํฌ์ปค์Šค๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€ ๋ณด์ด๋‚˜? Dialog๋ฅผ ๋‹ซ์œผ๋ฉด ํฌ์ปค์Šค๊ฐ€ ์›๋ž˜ ์ž๋ฆฌ๋กœ ๋Œ์•„์˜ค๋‚˜? Shift+ArrowDown์œผ๋กœ ๋ฒ”์œ„ ์„ ํƒ์ด ๋˜๋‚˜? Undo๊ฐ€ ๋˜๋‚˜? ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๋ฅผ ์ผœ๋ฉด ๋ญ๋ผ๊ณ  ์ฝ๋‚˜?

์•ˆ ๋œ๋‹ค. **๋ณด์ด๊ธฐ๋งŒ ํ•˜๋Š” ์•ฑ**์ด๋‹ค. ํด๋ฆญ์€ ๋˜๋Š”๋ฐ ์“ธ ์ˆ˜๋Š” ์—†๋‹ค.

"ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ๋Œ€์ฒด๋˜์–ด ๋ณด์ด๋Š” ๊ฒƒ"์€ ๊ทธ ์ด์ƒ์˜ ์ˆ˜์ค€์ด ํ•„์š” ์—†๋Š” ๊ฒƒ๋“ค์ด ๋Œ€๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ง„์งœ ํ”„๋กœ ๋„๊ตฌ โ€” Figma, Notion, Linear, Excel โ€” ์ด๋Ÿฐ ์ˆ˜์ค€์„ AI์—๊ฒŒ ์‹œํ‚ค๋ฉด, ์žฅ๋‚œ๊ฐ์ด ๋‚˜์˜จ๋‹ค.

### ์™œ ์ด ๊ฐญ์ด ์กด์žฌํ•˜๋Š”๊ฐ€

AI์˜ ์ง€๋Šฅ ๋ถ€์กฑ์ด ์•„๋‹ˆ๋‹ค. **ํ”„๋ก ํŠธ์—”๋“œ์˜ ๊ตฌ์กฐ์  ํŠน์„ฑ** ๋•Œ๋ฌธ์ด๋‹ค.

AI๊ฐ€ ์ฝ”๋”ฉ์„ ํ•˜๋ ค๋ฉด, ์ง  ์ฝ”๋“œ๊ฐ€ ๋งž๋Š”์ง€ ์Šค์Šค๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ์˜ ๊ฒฐ๊ณผ๋ฌผ์€ **์‹œ๊ฐ(View)**๊ณผ **์ƒํ˜ธ์ž‘์šฉ(Interaction)**, ๋‘ ์ถ•์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

**์‹œ๊ฐ์€ ๊ฒ€์ฆ ๋น„์šฉ์ด ๋‚ฎ๋‹ค.** AI๋Š” ๋ˆˆ์ด ์—†์ง€๋งŒ, ์‚ฌ๋žŒ์ด ์Šคํฌ๋ฆฐ์ƒท ํ•œ ์žฅ ๋ณด๊ณ  "์—ฌ๊ธฐ ํ‹€์–ด์กŒ์–ด" ํ•˜๋ฉด ๊ทธ๋งŒ์ด๋‹ค. ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๊ฐ€ ์งง๋‹ค.

**์ƒํ˜ธ์ž‘์šฉ์€ ๊ฒ€์ฆ์ด ํญ๋ฐœํ•œ๋‹ค.** ํฌ์ปค์Šค ์œ„์น˜ ร— ์„ ํƒ ์ƒํƒœ ร— ํ™•์žฅ ์ƒํƒœ ร— ์˜ค๋ฒ„๋ ˆ์ด ์ƒํƒœ ร— ์ž…๋ ฅ ๋ชจ๋“œ โ€” ์ƒํƒœ ์ถ•๋“ค์ด ์„œ๋กœ ์˜์กดํ•˜๋ฉด์„œ ์กฐํ•ฉ์ด ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ๋Š˜์–ด๋‚œ๋‹ค. ์ธ๊ฐ„์€ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด๊ณ  ์ง์ ‘ ๋ˆŒ๋Ÿฌ๋ณด๋ฉด์„œ ๋ชธ์œผ๋กœ ๊ฒ€์ฆํ•œ๋‹ค. AI๋Š” ๋ˆˆ๋„ ์†๋„ ์—†๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ˜„์žฌ์˜ ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŒ… ๋„๊ตฌ๋“ค(Playwright ๋“ฑ)์€ ์ธ๊ฐ„์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•ด์•ผ ๋Œ์•„๊ฐ„๋‹ค. ์ถ”์ƒํ™”๊ฐ€ ๋ฐ˜์ฏค์—์„œ ๋ฉˆ์ถฐ ์žˆ๋‹ค. **AI๊ฐ€ ์Šค์Šค๋กœ ์ƒํ˜ธ์ž‘์šฉ์„ ํƒ์ƒ‰ํ•˜๊ณ  ๊ฒ€์ฆํ•  ํ™˜๊ฒฝ ์ž์ฒด๊ฐ€ ์—†๋‹ค.**

๊ฒฐ๊ณผ์ ์œผ๋กœ AI๋Š” ์ž์‹ ์ด ์ง  ์ƒํ˜ธ์ž‘์šฉ ์ฝ”๋“œ๊ฐ€ ๋งž๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์—†๊ณ , ๊ฒ€์ฆ์„ ํฌ๊ธฐํ•œ ๊ป๋ฐ๊ธฐ UI๋ฅผ ๋ฑ‰์–ด๋‚ธ๋‹ค.

---

## How โ€” ๊ฒ€์ฆ ๋ฌธ์ œ๋ฅผ ํ’€์ง€ ์•Š๊ณ , ๊ฒ€์ฆ์˜ ํ•„์š”์„ฑ์„ ์—†์•ค๋‹ค

์ด ๋ฌธ์ œ๋ฅผ ํ‘ธ๋Š” ๋ฐฉ๋ฒ•์€ Playwright๋ฅผ AI ์นœํ™”์ ์œผ๋กœ ๊ณ ์น˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ๋ฐœ์ƒ์„ ๋’ค์ง‘๋Š”๋‹ค.

**AI๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์—†๋‹ค๋ฉด, ์ƒํ˜ธ์ž‘์šฉ์„ AI๊ฐ€ ์งœ์ง€ ์•Š๊ฒŒ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.**

๊ธฐ์กด ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค `onKeyDown`, `useState(focusIndex)`, `useEffect`๋กœ ํฌ์ปค์Šค ๋ณต์› โ€” ์ƒํ˜ธ์ž‘์šฉ์„ ํŒŒํŽธ์ ์œผ๋กœ ํฉ๋ฟŒ๋ ค์™”๋‹ค. ๊ฐ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž๊ธฐ ์ธํ„ฐ๋ž™์…˜์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ณ , ์•ฑ ์ „์ฒด์˜ ์ผ๊ด€์„ฑ์€ ๊ฐœ๋ฐœ์ž์˜ ๋จธ๋ฆฟ์†์—๋งŒ ์žˆ์—ˆ๋‹ค.

Interactive OS๋Š” ์ด ํฉ์–ด์ง„ ์ƒํ˜ธ์ž‘์šฉ ์ฝ”๋“œ๋ฅผ ์ „๋ถ€ ์•ฑ์—์„œ ๋œฏ์–ด๋‚ด์„œ, **์‹œ์Šคํ…œ ๊ณ„์ธต(OS)์œผ๋กœ ์˜ฌ๋ฆฐ๋‹ค.** ์•ฑ์€ "์ด ์˜์—ญ์€ ๋ฆฌ์ŠคํŠธ๋‹ค", "์ด๊ฑด ํŠธ๋ฆฌ๋‹ค" โ€” ์—ญํ• (role)๋งŒ ์„ ์–ธํ•œ๋‹ค. ํ‚ค๋ณด๋“œ ๋‚ด๋น„๊ฒŒ์ด์…˜, ํฌ์ปค์Šค ๊ด€๋ฆฌ, ์„ ํƒ ๋กœ์ง, ์ ‘๊ทผ์„ฑ โ€” ์ „๋ถ€ OS๊ฐ€ ํ•œ๋‹ค.

AI๋Š” ์ƒํ˜ธ์ž‘์šฉ ์ฝ”๋“œ๋ฅผ ์งœ์ง€ ์•Š๋Š”๋‹ค. ์งœ์ง€ ์•Š์œผ๋‹ˆ๊นŒ ๊ฒ€์ฆํ•  ํ•„์š”๋„ ์—†๋‹ค. **ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๊ฐ€์žฅ ๋น„์‹ผ ๊ฒ€์ฆ ์ถ•์ด ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค.**

์ด๊ฑด ์ธ๊ฐ„ ๊ฐœ๋ฐœ์ž์˜ ํŽธ์˜๋ฅผ ์œ„ํ•ด ๋งŒ๋“  ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์ธ๊ฐ„ ๊ฐœ๋ฐœ์ž๋Š” `onKeyDown`์„ ์ง์ ‘ ์งœ๋Š” ๊ฒŒ ์˜คํžˆ๋ ค ์ดํ•ด๊ฐ€ ๋˜๊ณ  ์ œ์–ด๊ฐ€ ๋œ๋‹ค. **์ด ์‹œ์Šคํ…œ์€ LLM์„ ์œ„ํ•œ ์ธํ”„๋ผ๋‹ค.** LLM์€ ์ดํ•ด๊ฐ€ ํ•„์š” ์—†๋‹ค. ์„ ์–ธํ•˜๋ฉด ๋ณด์žฅ๋˜๋Š” ๊ฒƒ์ด LLM์—๊ฒŒ๋Š” ์ด์ƒ์ ์ด๋‹ค.

---

## What โ€” Architecture

### ZIFT Primitives

UI๋ฅผ ์ƒํ˜ธ์ž‘์šฉ ๊ด€์ ์—์„œ ์ถ”์ƒํ™”ํ•˜๋Š” 4๊ฐ€์ง€ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ.

| ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ | ์—ญํ•  | ์˜ˆ์‹œ |
|:---|:---|:---|
| **Zone** | ์ƒํ˜ธ์ž‘์šฉ์˜ ๊ด€ํ•  ๊ตฌ์—ญ. 27๊ฐœ ARIA role preset ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ์–ธํ•˜๋ฉด, ํ•ด๋‹น ํŒจํ„ด์˜ ํ‚ค๋ณด๋“œ/์ ‘๊ทผ์„ฑ ๊ทœ์น™์ด ์ž๋™ ์ ์šฉ | `role: "listbox"`, `role: "dialog"`, `role: "treegrid"` |
| **Item** | ํฌ์ปค์Šค์˜ ์ตœ์†Œ ๋‹จ์œ„. ์„ ํƒ, ํ™œ์„ฑํ™”, ํ™•์žฅ์˜ ๋Œ€์ƒ | ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ ํ•ญ๋ชฉ, ํŠธ๋ฆฌ์˜ ๊ฐ ๋…ธ๋“œ |
| **Field** | ํŽธ์ง‘ ๊ฐ€๋Šฅํ•œ ์ž…๋ ฅ ์˜์—ญ. IME ์•ˆ์ „, ์ปค๋งจ๋“œ ๊ธฐ๋ฐ˜ commit/cancel | ์ธ๋ผ์ธ ์—๋””ํ„ฐ, ๊ฒ€์ƒ‰ ์ž…๋ ฅ |
| **Trigger** | ์ปค๋งจ๋“œ๋ฅผ ๋ฐœ๋™ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฒ„๋ ˆ์ด๋ฅผ ์—ฌ๋Š” ์ธํ„ฐํŽ˜์ด์Šค | ์‚ญ์ œ ๋ฒ„ํŠผ, ๋“œ๋กญ๋‹ค์šด ๋ฉ”๋‰ด ํŠธ๋ฆฌ๊ฑฐ |

### 5-Phase Kernel Pipeline

์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ๋ฌผ๋ฆฌ์  ์ž…๋ ฅ์ด ํ†ต๊ณผํ•˜๋Š” ๋‹จ์ผ ํŒŒ์ดํ”„๋ผ์ธ. ํฉ์–ด์ง„ `addEventListener`๊ฐ€ 0๊ฐœ๊ฐ€ ๋œ๋‹ค.

```
์‚ฌ์šฉ์ž ์ž…๋ ฅ (ํ‚ค๋ณด๋“œ/๋งˆ์šฐ์Šค/ํด๋ฆฝ๋ณด๋“œ)
โ”‚
โ–ผ
โ”Œโ”€ 1-listen โ”€โ”€โ”€โ”€ ์ด๋ฒคํŠธ ์บก์ฒ˜ (์ „์—ญ ๋‹จ์ผ ๋ฆฌ์Šค๋„ˆ)
โ”œโ”€ 2-resolve โ”€โ”€โ”€ ๋ฌผ๋ฆฌ ์ž…๋ ฅ โ†’ ๋…ผ๋ฆฌ ์ปค๋งจ๋“œ ๋ณ€ํ™˜ (ํฌ์ปค์Šค ์œ„์น˜ยทrole์— ๋”ฐ๋ผ)
โ”œโ”€ 3-inject โ”€โ”€โ”€โ”€ ์ปค๋งจ๋“œ์— ๋งฅ๋ฝ ์ฃผ์ž… (์„ ํƒ ์ƒํƒœ, ํƒ€๊ฒŸ ID ๋“ฑ)
โ”œโ”€ 4-command โ”€โ”€โ”€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ƒํƒœ ํŠธ๋žœ์žญ์…˜ ์‹คํ–‰
โ””โ”€ 5-effect โ”€โ”€โ”€โ”€ DOM ๋ถ€์ˆ˜ ํšจ๊ณผ (focus(), scrollIntoView)
```

๊ฐ™์€ `Enter` ํ‚ค๊ฐ€ listbox์—์„œ๋Š” "์„ ํƒ", dialog์—์„œ๋Š” "ํ™•์ธ", menu์—์„œ๋Š” "์‹คํ–‰ ํ›„ ๋‹ซ๊ธฐ"๊ฐ€ ๋œ๋‹ค. ๋งฅ๋ฝ์— ๋”ฐ๋ผ **๊ฒฐ๊ณผ**๊ฐ€ ๋‹ฌ๋ผ์งˆ ๋ฟ, ํ•ด์„ ๊ฒฝ๋กœ๋Š” ํ•˜๋‚˜๋‹ค. macOS์—์„œ `โŒ˜S`๊ฐ€ ์–ด๋””์„œ๋“  "์ €์žฅ"์ธ ๊ฒƒ์ฒ˜๋Ÿผ.

### 28 Universal OS Commands

์•ฑ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜๋˜ ๋™์ž‘์ด ์‹œ์Šคํ…œ ์ˆ˜์ค€์˜ 28๊ฐœ ์ปค๋งจ๋“œ๋กœ ํ†ต์ผ๋œ๋‹ค.

```
๊ณต๊ฐ„: OS_NAVIGATE ยท OS_TAB ยท OS_FOCUS ยท OS_STACK_PUSH ยท OS_STACK_POP
ํ™œ์„ฑํ™”: OS_ACTIVATE ยท OS_CHECK ยท OS_PRESS ยท OS_EXPAND
์„ ํƒ: OS_SELECT ยท OS_SELECT_ALL ยท OS_SELECTION_CLEAR
ํŽธ์ง‘: OS_FIELD_START_EDIT ยท OS_FIELD_COMMIT ยท OS_FIELD_CANCEL
CRUD: OS_DELETE ยท OS_MOVE_UP ยท OS_MOVE_DOWN
ํžˆ์Šคํ† ๋ฆฌ: OS_UNDO ยท OS_REDO
ํด๋ฆฝ๋ณด๋“œ: OS_COPY ยท OS_CUT ยท OS_PASTE
์˜ค๋ฒ„๋ ˆ์ด: OS_OVERLAY_OPEN ยท OS_OVERLAY_CLOSE ยท OS_ESCAPE
๊ฐ’: OS_VALUE_CHANGE
์•Œ๋ฆผ: OS_NOTIFY ยท OS_NOTIFY_DISMISS
```

### 27 Role Presets (W3C APG ์™„์ „ ์ค€์ˆ˜)

Zone์— role์„ ์„ ์–ธํ•˜๋ฉด, W3C ARIA Authoring Practices Guide์˜ ํ•ด๋‹น ํŒจํ„ด ๊ทœ์น™์ด ์ž๋™ ์ ์šฉ๋œ๋‹ค.

```
๋ฆฌ์ŠคํŠธ: listbox ยท menu ยท menubar ยท radiogroup ยท tablist ยท toolbar ยท feed
ํŠธ๋ฆฌ/๊ทธ๋ฆฌ๋“œ: tree ยท treegrid ยท grid
์˜ค๋ฒ„๋ ˆ์ด: dialog ยท alertdialog ยท combobox
์ปจํ…์ธ : accordion ยท disclosure
๊ฐ’: slider ยท spinbutton ยท meter ยท separator
ํ† ๊ธ€: switch ยท checkbox
๊ธฐํƒ€: group ยท application ยท textbox ยท builderBlock
```

๊ฐ preset์€ ๋ฐฉํ–ฅ(orientation), ๋ฃจํ”„(loop), ์„ ํƒ ๋ชจ๋“œ(single/multiple/followFocus), ํƒญ ๋™์ž‘(escape/trap/flow), ์—”ํŠธ๋ฆฌ ์ „๋žต(first/selected/restore) ๋“ฑ 8์ถ•์˜ ์„ค์ •์„ ๋‚ด์žฅํ•œ๋‹ค.

---

## ์‹ค์ œ ์ฝ”๋“œ

### ์•ฑ ์ •์˜ (Todo App)

```typescript
// ์•ฑ ์„ ์–ธ โ€” ์ƒํƒœ ๋ชจ๋ธ๊ณผ ํžˆ์Šคํ† ๋ฆฌ ๋ชจ๋“ˆ
const TodoApp = defineApp("todo", INITIAL_STATE, {
modules: [history()],
});

// Zone์— role ์„ ์–ธ โ€” ์ด ํ•œ ์ค„๋กœ ํ‚ค๋ณด๋“œ, ์„ ํƒ, ์ ‘๊ทผ์„ฑ์ด ํ™œ์„ฑํ™”๋จ
const TodoListUI = listCollection.bind({
role: "listbox",
options: {
select: { mode: "multiple", range: true, toggle: true },
},
onCheck: (cursor) => toggleTodo({ id: cursor.focusId }),
onAction: (cursor) => startEdit({ id: cursor.focusId }),
onDelete: (cursor) => requestDeleteTodo({ ids: cursor.selection }),
onUndo: undoCommand(),
onRedo: redoCommand(),
});
```

`role: "listbox"` ํ•œ ์ค„๋กœ ArrowUp/Down ๋‚ด๋น„๊ฒŒ์ด์…˜, Home/End, Shift+Arrow ๋ฒ”์œ„ ์„ ํƒ, Meta+Click ํ† ๊ธ€ ์„ ํƒ, ๊ฒฝ๊ณ„ ํด๋žจํ•‘, roving tabindex, `aria-selected` ํ”„๋กœ์ ์…˜์ด ์ „๋ถ€ ํ™œ์„ฑํ™”๋œ๋‹ค. ์•ฑ ์ฝ”๋“œ์— `onKeyDown`์€ 0์ค„์ด๋‹ค.

### Headless ํ…Œ์ŠคํŠธ (DOM ์—†์ด < 1ms)

```typescript
// Playwright-subset API โ€” ๋ธŒ๋ผ์šฐ์ € ์—†์ด ๋™์ž‘
const page = createHeadlessPage(TodoApp, TodoPage);
page.goto("/");

// ๋‚ด๋น„๊ฒŒ์ด์…˜ ๊ฒ€์ฆ
page.locator("#todo_1").click();
page.keyboard.press("ArrowDown");
expect(page.locator("#todo_2").toBeFocused()).toBe(true);

// ๋ฒ”์œ„ ์„ ํƒ ๊ฒ€์ฆ
page.keyboard.press("Shift+ArrowDown");
expect(page.locator("#todo_2").attrs["aria-selected"]).toBe(true);
expect(page.locator("#todo_3").attrs["aria-selected"]).toBe(true);

// Undo ๊ฒ€์ฆ
page.keyboard.press("Delete");
page.dispatch(confirmDeleteTodo());
expect(page.state.data.todoOrder.length).toBe(3);
page.keyboard.press("Meta+z");
expect(page.state.data.todoOrder.length).toBe(4);
```

OS ๋‚ด๋ถ€ ์ƒํƒœ๊ฐ€ ๋ธŒ๋ผ์šฐ์ € DOM์—์„œ ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  ์ƒํ˜ธ์ž‘์šฉ์„ DOM ์—†์ด ์ˆœ์ˆ˜ํ•œ ์ƒํƒœ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค. 571๊ฐœ ์ด์ƒ์˜ headless ํ…Œ์ŠคํŠธ๊ฐ€ 3.5์ดˆ ์•ˆ์— ํ†ต๊ณผํ•œ๋‹ค.

### APG Contract ํ…Œ์ŠคํŠธ

24๊ฐœ W3C APG ํŒจํ„ด์— ๋Œ€ํ•œ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ๊ฐ€ headless ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋œ๋‹ค. ๊ฐ ํ…Œ์ŠคํŠธ๋Š” W3C ์ŠคํŽ™์˜ ํ‚ค๋ณด๋“œ ์ธํ„ฐ๋ž™์…˜ ๊ทœ์น™์„ 1:1๋กœ ๊ฒ€์ฆํ•œ๋‹ค.

```typescript
// listbox.apg.test.ts โ€” W3C APG Listbox ํŒจํ„ด ๊ณ„์•ฝ
it("ArrowDown: moves focus to next option", () => {
const t = singleSelect("apple");
t.keyboard.press("ArrowDown");
expect(t.focusedItemId()).toBe("banana");
expect(t.attrs("banana")["aria-selected"]).toBe(true); // followFocus
});

it("single-select invariant: exactly 1 item selected at all times", () => {
const t = singleSelect("apple");
t.keyboard.press("ArrowDown");
expect(t.selection()).toHaveLength(1);
t.keyboard.press("Home");
expect(t.selection()).toHaveLength(1);
t.keyboard.press("End");
expect(t.selection()).toHaveLength(1);
});
```

---

## ๋™์ž‘ํ•˜๋Š” ์•ฑ

| ์•ฑ | ์„ค๋ช… |
|---|---|
| **Reference Todo** | ํ’€์ŠคํŽ™ SaaS ๋ฒค์น˜๋งˆํฌ. Kanban 2D ๋‚ด๋น„๊ฒŒ์ด์…˜, ๋ฉ€ํ‹ฐ์…€๋ ‰์…˜, Clipboard, Undo/Redo, ์นดํ…Œ๊ณ ๋ฆฌ ์‚ฌ์ด๋“œ๋ฐ”, ์ธ๋ผ์ธ ์—๋””ํŒ…, DnD |
| **Web Builder** | ๋น„์ฃผ์–ผ CMS ๋นŒ๋”. Bento Grid, Block Preset, Seamless Section Navigation |
| **Inspector** | OS ์ปค๋งจ๋“œ ํŠธ๋ ˆ์ด์‹ฑ, ์‹ค์‹œ๊ฐ„ ์ƒํƒœ ๊ฒ€์‚ฌ (`Cmd+D`) |
| **APG Showcase** | 28๊ฐœ W3C APG ํŒจํ„ด์˜ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ฐ๋ชจ์™€ ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ |

---

## ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

```
packages/
โ”œโ”€โ”€ kernel/ # ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ์ƒํƒœ ๊ด€๋ฆฌ ์ฝ”์–ด (UI ๋ฌด๊ด€)
โ”œโ”€โ”€ os-core/ # 5-Phase ํŒŒ์ดํ”„๋ผ์ธ, 27 Role Preset ์—”์ง„
โ”‚ โ”œโ”€โ”€ 1-listen/ # ์ด๋ฒคํŠธ ์บก์ฒ˜
โ”‚ โ”œโ”€โ”€ 2-resolve/ # ์ž…๋ ฅ โ†’ ์ปค๋งจ๋“œ ๋ณ€ํ™˜
โ”‚ โ”œโ”€โ”€ 3-inject/ # ๋งฅ๋ฝ ์ฃผ์ž…
โ”‚ โ”œโ”€โ”€ 4-command/ # 28๊ฐœ ์ปค๋งจ๋“œ ๋„๋ฉ”์ธ
โ”‚ โ””โ”€โ”€ 5-effect/ # DOM ๋ถ€์ˆ˜ํšจ๊ณผ
โ”œโ”€โ”€ os-sdk/ # defineApp, createZone, createCollectionZone
โ”œโ”€โ”€ os-react/ # React ๋ฐ”์ธ๋”ฉ (Zone, Item, Field, Trigger)
โ”œโ”€โ”€ os-devtool/ # Headless ํ…Œ์ŠคํŠธ ์ธํ”„๋ผ (createHeadlessPage)
โ””โ”€โ”€ surface/ # ๋””์ž์ธ ํ† ํฐ, ํ…Œ๋งˆ, ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ

src/
โ”œโ”€โ”€ apps/todo/ # Reference Todo ์•ฑ
โ”œโ”€โ”€ apps/builder/ # Web Builder
โ”œโ”€โ”€ inspector/ # Command Inspector
โ””โ”€โ”€ pages/apg-showcase/ # 28๊ฐœ APG ํŒจํ„ด ๋ฐ๋ชจ

tests/
โ”œโ”€โ”€ apg/ # 24๊ฐœ APG ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ
โ”œโ”€โ”€ headless/ # ์•ฑ ์ˆ˜์ค€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
โ””โ”€โ”€ unit/ # OS ๋‚ด๋ถ€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
```

## ๊ธฐ์ˆ  ์Šคํƒ

React 19 ยท TypeScript 5.9 ยท Vite 7 ยท Tailwind CSS v4 ยท Vitest ยท Playwright

## ์‹œ์ž‘ํ•˜๊ธฐ

```bash
git clone https://github.com/developer-1px/interactive-os.git
cd interactive-os
npm install
npm run dev # ์•ฑ ๊ตฌ๋™
npm test # 571+ headless ํ…Œ์ŠคํŠธ (< 4์ดˆ)
npm run test:e2e # Playwright E2E
```