프론트엔드 API
This content is not available in your language yet.
CEF renderer의 V8 컨텍스트에 자동 주입되는 window.__suji__ 객체와 npm 래퍼.
두 가지 API 레이어
섹션 제목: “두 가지 API 레이어”┌────────────────────────┐│ @suji/api (npm 래퍼) │ 권장 — 타입 안전 + JSON 자동 처리└────────────┬───────────┘ │ wraps┌────────────▼───────────┐│ window.__suji__ │ raw 바인딩 — CEF가 V8 context에 주입└────────────────────────┘권장 — @suji/api
섹션 제목: “권장 — @suji/api”import { invoke, on, send, quit, platform } from '@suji/api';
const r = await invoke('ping'); // 객체 반환const r2 = await invoke('greet', { name: 'Suji' });const r3 = await invoke('ping', {}, { target: 'rust' }); // 특정 백엔드
const cancel = on('theme-changed', (data) => { ... });send('clicked', { button: 'save' });quit();platform; // "macos" | "linux" | "windows"Raw — window.__suji__
섹션 제목: “Raw — window.__suji__”주입된 전역. TypeScript 타입 없음, 모든 인자는 문자열이어야 함.
// invoke(channel: string, dataJSONString: string, optionsJSONString: string) → Promise<any>await window.__suji__.invoke('ping', '{}', '{"target":"zig"}')
// core(requestJSONString: string) → Promise<any> — 코어 커맨드용await window.__suji__.core(JSON.stringify({ cmd: 'create_window', title: 'X' }))
// send(channel, dataJSONString): voidwindow.__suji__.send('clicked', '{}')
// on(channel, callback): unsubscribeFnconst off = window.__suji__.on('event', (data) => { ... })invoke 옵션
섹션 제목: “invoke 옵션”interface InvokeOptions { target?: string; // 특정 백엔드 지정 ("zig" | "rust" | "go" | "node")}target미지정 — 채널 이름으로 자동 라우팅 (백엔드가handle(ch, ...)등록한 것에 자동 매칭)- 같은 채널을 여러 백엔드가 등록한 경우 반드시
target명시 (아니면no handler에러)
코어 커맨드 (__suji__.core)
섹션 제목: “코어 커맨드 (__suji__.core)”프레임워크 내장 기능. 백엔드 거치지 않고 Zig 코어가 직접 처리.
| cmd | 파라미터 | 응답 |
|---|---|---|
create_window | title, url?, name?, width?, height? | {from:"zig-core", windowId:N} |
set_title | windowId, title | {windowId:N, ok:bool} |
set_bounds | windowId, x?, y?, width?, height? | {windowId:N, ok:bool} |
fs_read_file | path | {success:true,text} |
fs_write_file | path, text | {success:true} |
fs_stat | path | {success:true,type,size,mtime} |
fs_mkdir | path, recursive? | {success:true} |
fs_readdir | path | {success:true,entries} |
app_set_badge_count | count | {success:bool,native:bool} |
app_get_badge_count | — | {count:N} |
screen_get_all_displays | — | {displays:[...]} |
desktop_capturer_get_sources | types? | {sources:[...]} |
desktop_capturer_capture_thumbnail | sourceId(screen:<id>:0/window:<id>:0), path | {success:bool} |
crash_reporter_start | uploadToServer?, submitURL?, extra? | {success:true,enabled:bool} |
crash_reporter_get_parameters | — | {parameters:{...}} |
crash_reporter_add_extra_parameter | key, value | {success:bool} |
crash_reporter_remove_extra_parameter | key | {success:bool} |
crash_reporter_get_uploaded_reports | — | {reports:[{id,date}]} |
crash_reporter_get_last_crash_report | — | {report:{id,date}|null} |
quit | — | {cmd:"quit"} |
core_info | — | {backends:[...]} |
예:
const w = await window.__suji__.core(JSON.stringify({ cmd: 'create_window', title: 'Settings', name: 'settings', // 선택 — 백엔드에서 event.window.name으로 식별 url: 'http://localhost:12300/settings',}));console.log(w.windowId); // 2, 3, ...@suji/api에서는 raw core 대신 typed wrapper를 권장:
import { fs } from '@suji/api';
await fs.mkdir('/tmp/suji', { recursive: true });await fs.writeFile('/tmp/suji/hello.txt', 'hello');const text = await fs.readFile('/tmp/suji/hello.txt');Electron 대응
섹션 제목: “Electron 대응”| Electron | Suji |
|---|---|
ipcRenderer.invoke(ch, ...args) | invoke(ch, data, options) — args 단일 객체 |
ipcRenderer.send(ch, data) | send(ch, data) |
ipcRenderer.on(ch, cb) | on(ch, cb) |
ipcRenderer.once(ch, cb) | once(ch, cb) |
app.quit() | quit() |
app.setBadgeCount(n) / app.getBadgeCount() | app.setBadgeCount(n) / app.getBadgeCount() |
process.platform | platform ("macos" not "darwin") |
BrowserWindow.getAllWindows() | core({cmd:"core_info"}) (추후 별도 API 예정) |
preload.js | 미제공 — CEF 환경이라 Node API 노출 X |
contextBridge | 미제공 — window.__suji__는 메인 월드 frozen bridge로 하드닝, 진짜 isolated-world는 roadmap |
자세한 차이는 events.mdx 참조.