XML로 네이티브 컨트롤을 선언하고 JavaScript로 속성과 구조를 갱신한다. WebView/HTML/DOM/CSS/React를 사용하지 않는다. 기준일: 2026-10-03.
Fragment와 surface 만들기
ui.parse(xml: string): ui.Fragment;
ui.loadXML(relativePath: string): ui.Fragment;
ui.createPanel(options: {
id: string; title: string; content: ui.Fragment;
}): ui.Panel;
ui.createWindow(options: ui.WindowOptions): ui.Window;
ui.createDialog(options: ui.WindowOptions): ui.Window;
// WindowOptions: id, title, content, width?, height?
TypeScript
복사
parse는 정확히 한 root를 가진 XML을 검증한다. surface root는 row, column, group, panel 중 하나다. Fragment는 직접 생성하지 않는다. 세션당 createPanel은 하나이며 도킹 초기화/Preview UI script에서만 허용된다. 일반 Run은 createWindow 또는 createDialog를 사용한다.
loadXML은 스크립트 폴더 내부의 UTF-8 .xml 파일만 읽는다. 절대 경로와 상위 경로 탐색은 거부한다. VS Code 빌드는 리터럴 ui.loadXML('view.aeui.xml')을 읽어 생성 JavaScript에 포함한다. 동적 파일 경로는 빌드에서 거부한다. XML을 변경하면 저장 후 다시 빌드한다.
Window의 width는 160..4096(기본 420), height는 100..4096(기본 320), 단위는 96 DPI 기준 논리 픽셀이다. createDialog는 modeless 창이다. AE를 막는 modal 실행, 동기 결과 또는 dialog를 기다리는 Promise API가 없다.
Panel / Window API
•
get(id: string) — ui.Element union. 없으면 오류
•
get(id, kind) — ui.ElementMap[K]. runtime kind 검사 + 편집기 타입 좁히기
•
on(id, type, callback) — () => void unsubscribe. id/type당 하나; 재등록은 교체
•
show(): void / hide(): void — 표시/숨김 요청. hide는 세션을 유지
•
window.close(): void — 영구 닫기. 닫힌 객체/요소를 재사용하지 않음
•
window.onClose(callback) — unsubscribe 반환. idle에서 close 이벤트 전달
onClose 이벤트는 읽기 전용 { type: 'close', target: ui.Window }다. 입력값이 필요하면 close() 전에 읽는다. 닫힌 창은 show()로 다시 열 수 없으며 새 창을 만들어야 한다. 닫힌 surface 정리가 완료되면 같은 문자열 ID로 새 창을 만들 수 있다.
ui.Panel에는 close/onClose가 없다. 도킹 패널을 명시적으로 해제하려면 관리 UI의 Close를 사용한다. 일반 창의 수명과 AE의 도킹 tab/view 수명을 구분한다.
요소의 공통 API
kind와 id는 읽기 전용이다. 모든 요소의 enabled, visible은 boolean으로 갱신할 수 있다. remove(): void는 자손도 제거하며 기존 참조를 무효화한다. root는 제거할 수 없다.
row, column, group, XML panel, select는 append(content: ui.Fragment): ui.Element를 제공한다. select에는 option만 넣는다. 새 요소의 ID도 해당 surface 안에서 중복되면 안 된다.
텍스트·숫자·선택값은 해당 속성 setter로 갱신한다. 변경 가능한 속성만 아래 목록에 명시했다. XML의 width/min/step 등을 임의의 JS 속성으로 바꾸는 API는 없다.
•
row, column — 추가 XML 속성: gap, padding · JS 갱신: append/remove · 이벤트: 없음
•
group / Group — 추가 XML 속성: orientation, gap, padding · JS 갱신: orientation, append/remove · 이벤트: 없음
•
panel / PanelGroup — 추가 XML 속성: text, orientation, gap, padding · JS 갱신: text, orientation, append/remove · 이벤트: 없음
•
label / Label — 추가 XML 속성: text · JS 갱신: text: string · 이벤트: 없음
•
button / Button — 추가 XML 속성: text · JS 갱신: text: string · 이벤트: click
•
input / Input — 추가 XML 속성: value · JS 갱신: value: string · 이벤트: change
•
number / NumberInput — 추가 XML 속성: min, max, value, step, precision · JS 갱신: value: number · 이벤트: input, change
•
checkbox / Checkbox — 추가 XML 속성: text, checked · JS 갱신: text: string, checked: boolean · 이벤트: change
•
slider / Slider — 추가 XML 속성: min, max, value · JS 갱신: value: 정수 · 이벤트: input, change
•
select / Select — 추가 XML 속성: value, option 자식 · JS 갱신: value: string, append/remove · 이벤트: change
•
option / Option — 추가 XML 속성: text, value · JS 갱신: text: string, value: string · 이벤트: 없음
이벤트
이벤트가 있는 요소는 on(type, callback): () => void를 제공한다. 인수는 읽기 전용 { type, target }이며 get(id, kind)를 사용하면 target 타입도 좁혀진다. 반환 함수를 호출해 구독을 해제한다.
input은 number/slider 조작 중 값 변경, change는 조작 완료다. 취소하여 시작값을 복원하면 input만 전달한다. 같은 요소·종류의 대기 이벤트는 합쳐진다. event.target.value는 처리 시점의 최신 모델 값이며 모든 마우스 이동을 개별 전달하는 기록이 아니다.
JS 대입은 이벤트를 발생시키지 않는다. input 요소의 이벤트는 change이며 number/slider의 input 이벤트와 구분한다. 콜백은 AE idle에서 실행되고 최대 250 ms다.
배치와 XML 문법
공통 XML 속성은 id, enabled, visible, grow, width, height다. bool은 true/false. 크기·gap·padding은 0..4096 정수다. width/height=0은 자동 크기, grow=true는 남는 축 공간을 분배한다.
row는 가로, column은 세로 배치다. group은 테두리 없는 컨테이너, XML panel은 제목과 테두리를 가진 컨테이너다. group/panel의 orientation 기본값은 column이며 row로 변경할 수 있다. 기본 gap=6, padding은 group=0/panel=10이다. 제목 있는 panel은 16 논리 픽셀의 제목 공간을 더한다.
XML panel은 도킹 창이 아니다. 도킹 surface는 ui.createPanel로 만든다. 중첩 컨테이너를 조합해 가로로 묶은 요소들을 다시 세로로 배치할 수 있다. 자동 줄바꿈/축소는 없으므로 좁은 dock에서는 고정 폭의 합을 줄이거나 column을 사용한다.
문자열은 text/value 속성에 쓴다. <label>Hello</label> 형태의 text node는 지원하지 않는다. & 등 XML 문자는 &처럼 escape한다. 사용자 입력은 XML 문자열에 직접 끼워 넣기보다 생성 후 .text/.value로 설정하는 편이 안전하다.
XML 선언, DTD, CDATA, processing instruction, 알 수 없는 태그·속성, inline event handler는 거부한다. 이벤트는 JavaScript의 on으로 등록한다. CSS/class/style 속성은 없다.
숫자·선택 컨트롤 세부 계약
number: value=0, min=-1e9, max=1e9, step=1, precision=2가 기본이다. 값·범위·step은 유한한 ±1e9 이내 수이고 step>0, precision은 0..6 정수다. value는 min/max 안이어야 한다.
한 번 클릭하면 텍스트 편집, 누른 채 수평 이동하면 숫자 드래그다. step은 논리 픽셀당 변화량이다. Shift는 10배, Ctrl은 0.1배(Ctrl 우선); 위/아래 키도 같은 step/modifier를 사용한다. Enter/포커스 이동으로 확정하고 Escape는 시작값을 복원한다.
수식 계산은 없으며 숫자·소수·지수 표기만 받는다. 잘못된 값에서 Enter를 누르면 편집을 유지하고, 포커스를 옮기면 시작값으로 복원한다. 사용자 입력은 범위로 제한하고 precision으로 반올림한다. 유효한 JS/XML 대입값 자체는 정밀도를 보존하며 표시만 precision에 맞춘다. 범위 밖 JS 대입은 오류다.
slider: 정수, 기본 min=0/max=100/value=0. 명시 범위는 -100000..100000이다. 빈 트랙 클릭은 해당 위치로 즉시 이동하고 이어 드래그한다. 손잡이를 잡으면 클릭 오프셋을 유지한다.
select: option value는 비어 있지 않고 중복되지 않아야 한다. 선택값은 option과 일치해야 한다. 선택한 option을 제거하면 선택값은 빈 문자열이고, 다른 option을 제거하면 현재 선택을 유지한다.
동적 추가·제거 예제
다음을 일반 .aeu로 빌드하고 Common에서 실행한다. 매번 새 ID를 사용하고 제거한 요소 참조를 버린다.
//@ID com.example.dynamic-ui
//@NAME 'Dynamic UI example'
//@TYPE script
const view = ui.createWindow({
id: 'dynamic', title: 'Dynamic UI',
content: ui.parse(`<column gap="8" padding="10">
<group id="items" orientation="column"/>
<button id="add" text="Add label"/>
<button id="remove" text="Remove last label"/>
</column>`)
});
const items: ui.Element[] = [];
let nextId = 0;
view.get('add', 'button').on('click', () => {
const item = view.get('items', 'group').append(
ui.parse(`<label id="item${++nextId}" text="New item"/>`)
);
items.push(item);
});
view.get('remove', 'button').on('click', () => {
const item = items.pop();
if (item) item.remove();
});
view.show();
TypeScript
복사
한도와 스타일
세션당 최대 16 live surfaces, 256 UI 노드, 128 대기 이벤트. XML은 128 KiB, 깊이 16단계, 속성 값은 4096 UTF-8 bytes다. native 텍스트 입력은 1024 UTF-16 code units, number 편집은 96 code units다.
현재 Windows adapter가 AE 팔레트 기반 컨트롤을 그린다. 테마 변경은 AE 재시작 후 반영한다. OS/AE 소유 타이틀바·파일 선택창·메뉴는 해당 외관을 유지한다. 사용자 CSS/폰트/그림/드래그 드롭/전역 단축키 API는 제공하지 않는다.
근거
docs/SCRIPT_UI_API.md, docs/DOCKABLE_SCRIPTS.md, AEUScripts for vscode/types/aeuscript.d.ts, src/ui.cpp, src/ui_session.cpp. 실제 AE 검증 범위는 hub의 버전·검증 안내를 따른다.




