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
- Host: GitHub
- URL: https://github.com/developer-1px/interactive-os
- Owner: developer-1px
- Created: 2026-02-04T13:03:22.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-14T03:08:33.000Z (3 months ago)
- Last Synced: 2026-03-14T13:58:49.216Z (3 months ago)
- Language: TypeScript
- Size: 12.2 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
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
```