@morit/cli 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +3 -5
  2. package/assets/plugin_contract.json +4 -2
  3. package/bin/morit.js +0 -0
  4. package/package.json +1 -1
  5. package/src/cli.js +5 -0
  6. package/src/workspace.js +83 -12
  7. package/assets/docs/README.md +0 -107
  8. package/assets/docs/ai-response-and-timeline.md +0 -211
  9. package/assets/docs/ai-skill-and-docx-workflow.md +0 -83
  10. package/assets/docs/app-builder.md +0 -56
  11. package/assets/docs/authentication.md +0 -140
  12. package/assets/docs/components.md +0 -225
  13. package/assets/docs/design-tokens-responsive.md +0 -171
  14. package/assets/docs/docs-index.json +0 -94
  15. package/assets/docs/examples-notion.md +0 -83
  16. package/assets/docs/examples-school-life.md +0 -79
  17. package/assets/docs/getting-started.md +0 -132
  18. package/assets/docs/information-hierarchy.md +0 -81
  19. package/assets/docs/instances-and-connectors.md +0 -93
  20. package/assets/docs/lifecycle-and-api.md +0 -169
  21. package/assets/docs/local-cli.md +0 -125
  22. package/assets/docs/manifest.md +0 -247
  23. package/assets/docs/packaging-and-testing.md +0 -131
  24. package/assets/docs/permissions-and-data.md +0 -149
  25. package/assets/docs/platform-compatibility.md +0 -62
  26. package/assets/docs/plugin-storage.md +0 -175
  27. package/assets/docs/project-structure.md +0 -102
  28. package/assets/docs/remote-mcp.md +0 -158
  29. package/assets/docs/school-life-privacy.md +0 -55
  30. package/assets/docs/screens-layout-navigation.md +0 -95
  31. package/assets/docs/sdk-and-mcp.md +0 -199
  32. package/assets/docs/tool-and-skill.md +0 -188
  33. package/assets/docs/troubleshooting.md +0 -121
  34. package/assets/docs/ui-extensions.md +0 -75
  35. package/assets/docs/ui-runtime-v2.md +0 -406
  36. package/assets/docs/verification.md +0 -133
@@ -1,56 +0,0 @@
1
- # 앱에서 플러그인 만들기
2
-
3
- Plugin Builder는 개발 환경 없이 작은 Tool 또는 Skill을 만드는 경로입니다. Builder도 일반
4
- `.mplg`를 생성하고 같은 설치·권한·삭제 흐름을 사용하지만, 안전하게 표현할 수 있는 범위를
5
- 의도적으로 제한합니다.
6
-
7
- ## 지원 범위
8
-
9
- | 항목 | 지원 값 |
10
- |---|---|
11
- | capability kind | `tool`, `skill` |
12
- | runtime | `text_stats`, `text_template` |
13
- | UI 위치 | `card`, `screen`, `settings` 중 최대 3개 |
14
- | 선택 기능 | Search Provider, Slash Command, background, notification |
15
- | 권한 | `background`, `notifications` |
16
- | credential | 최대 4개의 `api_token` 선언(현재 앱 화면은 고급 source 흐름에서 설정) |
17
- | background 주기 | 15~10,080분 |
18
-
19
- Builder는 ID, 이름, 설명, version, capability 제목과 runtime 설정을 입력받아 최소 Manifest와
20
- Runtime v1 UI를 만듭니다. UI 코드를 생성하거나 외부 코드를 실행하지 않습니다.
21
-
22
- ## Builder가 적합한 예
23
-
24
- - 입력 텍스트의 길이와 단어 수를 계산하는 Tool
25
- - 정해진 템플릿으로 회의 메모를 정리하는 Skill
26
- - settings의 작은 정적 목록을 Search Provider로 노출
27
- - 정해진 문구를 일정 주기로 알리는 개인 알림
28
-
29
- ## 소스 프로젝트로 전환할 때
30
-
31
- 다음 중 하나가 필요하면 공식 CLI 프로젝트를 사용합니다.
32
-
33
- - Runtime v2의 화면·내비게이션·theme·Response UI
34
- - 외부 HTTPS API, OAuth, Remote MCP
35
- - 여러 데이터 source와 동적 목록
36
- - `sandbox_python` 데이터 처리
37
- - child package와 dependency
38
- - Cloud Secret, Connector retry/rate-limit
39
- - package asset, 이미지, 복수 source file
40
-
41
- [시작하기](getting-started.md)의 `@morit/cli plugin setup`으로 프로젝트를 만들고 Builder에서 정한
42
- ID와 capability 이름을 유지하면 사용자가 기능을 다시 익힐 필요가 없습니다. 기존 설치를 업데이트할
43
- 때는 publisher와 서명 identity, plugin ID, semantic version 순서를 유지해야 합니다.
44
-
45
- ## 실제 확인
46
-
47
- Builder 생성 성공만 확인하지 말고 다음을 앱에서 실행합니다.
48
-
49
- 1. 생성 직후 설치 목록에 나타나는지
50
- 2. 필요한 권한을 거부했을 때 기능이 실행되지 않는지
51
- 3. Tool/Skill과 Slash Command가 정확한 capability를 호출하는지
52
- 4. background/notification을 켜고 끌 수 있는지
53
- 5. 설정 변경 후 다시 열었을 때 유지되는지
54
- 6. 삭제 후 pending 알림과 credential이 정리되는지
55
-
56
- 고급 화면 개발은 [화면, 레이아웃, 내비게이션](screens-layout-navigation.md)부터 시작하세요.
@@ -1,140 +0,0 @@
1
- # 외부 서비스 인증과 Cloud Secrets
2
-
3
- 플러그인 source와 `.mplg`에는 비밀 값이 들어가지 않습니다. package는 필요한 Credential·Connector·
4
- Secret ID만 선언하고 실제 값은 사용자 Connection 또는 조직의 Cloud Secret에 저장합니다.
5
-
6
- ## 선택 기준
7
-
8
- | 값의 소유자 | 사용 모델 | 예 |
9
- |---|---|---|
10
- | 각 사용자 | Credential + Connection | 개인 Notion OAuth, 개인 API token |
11
- | Developer 조직 | Cloud Secret | 서버 간 API key, OAuth client secret |
12
- | 공개 설정 | Connector 또는 manifest | 공개 HTTPS endpoint, provider ID |
13
-
14
- 사용자 token을 Cloud Secret으로 공유하거나 조직 secret을 Instance settings에 복사하지 않습니다.
15
-
16
- ## Manifest 선언
17
-
18
- OAuth 계정:
19
-
20
- ```json
21
- {
22
- "credentials": [
23
- {
24
- "id": "notion_account",
25
- "label": "Notion 계정",
26
- "description": "공유한 페이지를 읽습니다.",
27
- "kind": "oauth_access_token",
28
- "required": true,
29
- "oauth_provider": "notion",
30
- "allow_multiple": true
31
- }
32
- ],
33
- "connectors": [
34
- {
35
- "id": "notion_cloud",
36
- "kind": "oauth",
37
- "label": "Notion Cloud",
38
- "description": "Notion 공식 OAuth 연결",
39
- "credential_id": "notion_account",
40
- "cloud_connection_id": "notion"
41
- }
42
- ]
43
- }
44
- ```
45
-
46
- 조직 Secret:
47
-
48
- ```json
49
- {
50
- "cloud_project_id": "00000000-0000-4000-8000-000000000000",
51
- "required_secrets": [
52
- {
53
- "id": "EXAMPLE_API_KEY",
54
- "label": "Example API key",
55
- "description": "서버 간 API 호출에 사용합니다.",
56
- "required": true
57
- }
58
- ],
59
- "connectors": [
60
- {
61
- "id": "example_api",
62
- "kind": "api_key",
63
- "label": "Example API",
64
- "description": "조직 API 연결",
65
- "cloud_secret_id": "EXAMPLE_API_KEY"
66
- }
67
- ]
68
- }
69
- ```
70
-
71
- `cloud_project_id`는 Developer Project에 연결할 때 발급된 실제 UUID를 사용합니다.
72
-
73
- ## Developer Platform 설정
74
-
75
- 1. 조직을 선택하고 Plugin Project를 엽니다.
76
- 2. Secrets에서 manifest의 `required_secrets.id`와 같은 ID를 등록합니다.
77
- 3. Connections에서 `cloud_connection_id`와 같은 provider 설정을 만듭니다.
78
- 4. OAuth authorize URL, token URL, client ID, client secret, redirect URI를 공급자 문서와 맞춥니다.
79
- 5. 필요한 header/content type과 PKCE 지원 여부를 명시합니다.
80
- 6. source를 sync하고 새 Deployment를 build합니다.
81
-
82
- Secret 값은 저장 후 다시 평문으로 표시되거나 source ZIP, MCP 응답, build log에 포함되면 안 됩니다.
83
- 변경은 새 값으로 덮어쓰고 필요한 경우 provider에서 기존 값을 폐기합니다.
84
-
85
- ## 사용자 연결 흐름
86
-
87
- ```text
88
- 설치·활성화
89
- → 필요한 Connection 없음
90
- → 설정에서 “계정 연결”
91
- → Host가 state와 redirect를 생성
92
- → provider 승인
93
- → Host callback에서 state 검증과 token 교환
94
- → 암호화 저장
95
- → default Instance에 Connection 연결
96
- ```
97
-
98
- `allow_multiple: true`이면 같은 default Instance 안에 여러 Connection을 만들고 사용자가 표시 이름으로
99
- 선택합니다. 같은 package root Instance를 복제해 계정을 추가하지 않습니다.
100
-
101
- OAuth state와 redirect URI는 필수입니다. PKCE는 공급자 호환 설정에 따라 사용하지만 state 검증을
102
- 대체하지 않습니다. callback이 성공하기 전에 연결 완료로 표시하지 않습니다.
103
-
104
- ## 실행과 갱신
105
-
106
- Capability는 `connector_id`만 참조합니다. Host는 선택된 Connection의 token을 요청 직전에 주입하고
107
- 응답과 로그에서 제거합니다. credential 거부가 명확하고 provider가 refresh를 지원하면 한 번 갱신한
108
- 뒤 전체 요청을 재시도합니다. 반복 실패는 “다시 연결” 상태로 전환합니다.
109
-
110
- Connection 상태를 구분합니다.
111
-
112
- - `ready`: 필요한 credential과 runtime이 준비됨
113
- - `not_configured`: 필수 Connection/Secret이 없음
114
- - `unavailable`: provider, 암호화, dependency, network runtime을 사용할 수 없음
115
- - 만료·거부: 사용자 재연결 필요
116
-
117
- ## 연결 해제와 삭제
118
-
119
- Instance Connection 삭제는 해당 연결 row의 encrypted credential, 진행 중 OAuth state, 관련 pending
120
- Host action을 로컬에서 정리하고 generation을 갱신합니다. 이 API를 provider token revoke가
121
- 완료됐다는 증거로 사용하지 않습니다.
122
-
123
- 기존 default credential 해제 API는 provider에 revocation endpoint가 있으면 먼저 revoke하고 성공한
124
- 경우에만 local credential을 삭제합니다. provider 오류가 나면 암호화된 credential을 유지하고 오류를
125
- 반환하므로 사용자가 다시 시도할 수 있습니다. Plugin 전체 삭제는 remote revoke를 가능한 범위에서
126
- 시도한 뒤 `data_policy: retain`이어도 local credential과 OAuth state를 보존하지 않습니다.
127
-
128
- ## 운영 점검
129
-
130
- - authorize 요청마다 새로운 state가 생성되는가
131
- - redirect URI가 등록값과 정확히 일치하는가
132
- - token URL과 content type이 provider 요구와 맞는가
133
- - client secret과 access token이 로그·MCP·artifact에 없는가
134
- - 여러 Connection의 token과 표시 이름이 섞이지 않는가
135
- - token 만료 후 한 번 갱신하고, 실패 시 재연결을 안내하는가
136
- - Connection 삭제 후 해당 연결의 실행이 즉시 차단되고 local credential이 제거되는가
137
- - provider revoke가 제품 요구사항이면 공급자 console에서도 token 폐기를 별도로 확인했는가
138
- - 플러그인 삭제 후 local credential과 OAuth state가 남지 않는가
139
-
140
- Notion의 실제 구성은 [Notion 플러그인](examples-notion.md)을 참고하세요.
@@ -1,225 +0,0 @@
1
- # 기본·커스텀 컴포넌트
2
-
3
- UI Runtime v2 node는 공통으로 `type`, 선택적 `id`, `props`, `children`,
4
- `visible_when`을 가집니다. `action`은 명확한 탭 영역을 제공하는 `surface`, `card`, `button`,
5
- `chip`에서만 허용됩니다. 구조·표시·입력 node에 action을 붙이면 Host, CLI, MCP 검증이 모두
6
- 거부하므로 버튼이나 탭 가능한 surface로 감싸세요. Host가 Material 3 widget으로 렌더링하며
7
- 플러그인은 Flutter widget이나 HTML을 전달하지 않습니다.
8
-
9
- 아래 모든 node는 현재 Android Host에서 지원됩니다. iOS·Desktop도 같은 JSON 계약을 재사용하도록
10
- 설계되어 있지만 Host 구현과 실기기 검증 전에는 지원 완료로 표시하지 않습니다. 플랫폼별 상태는
11
- [Android, iOS, Desktop 호환](platform-compatibility.md)을 참고하세요.
12
-
13
- ## 레이아웃 컴포넌트
14
-
15
- | type | 목적 | 주요 props | 제약 |
16
- |---|---|---|---|
17
- | `column` | 세로 읽기 흐름 | `spacing`, `padding`, axis 정렬 | children 최대 32개 |
18
- | `row` | 짧은 항목의 가로 배치 | `spacing`, `stack_at`, axis 정렬 | 좁은 화면 전환을 정의 |
19
- | `wrap` | chip·필터의 자동 줄바꿈 | `spacing`, `alignment` | 의미 순서는 children 순서 |
20
- | `grid` | 같은 중요도의 반복 카드 | `columns`, `min_item_width`, `spacing` | 열 수보다 최소 너비 우선 |
21
- | `stack` | 겹치는 장식·badge 배치 | `alignment`, `clip` | `positioned`의 직접 부모 |
22
- | `positioned` | stack 안의 위치 지정 | `left`, `top`, `right`, `bottom` | 정확히 한 child, offset 하나 이상 |
23
- | `scroll` | 제한된 영역의 스크롤 | `scroll_direction`, `shrink_wrap` | 정확히 한 child, 중첩 스크롤 자제 |
24
- | `padding` | 한 subtree의 내부 여백 | `padding` | 정확히 한 child |
25
- | `center` | 한 subtree 정렬 | `alignment` | 정확히 한 child |
26
- | `expanded` | row/column의 남은 공간 | `flex` | row/column의 직접 child, child 하나 |
27
-
28
- `row`의 `stack_at`은 0~1200이고, 0은 자동 세로 전환을 끕니다. `grid`의
29
- `min_item_width`는 96~600입니다. `expanded.flex`는 1~24입니다.
30
-
31
- ## Surface와 구조
32
-
33
- | type | 목적 | 주요 props | children |
34
- |---|---|---|---|
35
- | `surface` | 안전한 커스텀 시각 surface | 크기, 여백, 색 token, border, elevation, opacity | 1개 이상 필수 |
36
- | `card` | 독립된 요약·선택 영역 | `title`, `subtitle`, `tone`, `action` | 선택 |
37
- | `section` | 제목이 있는 정보 그룹 | `title`, `subtitle`, `spacing` | 선택 |
38
- | `divider` | 같은 흐름 안의 구분 | `color`, `margin` | 없음 |
39
- | `spacer` | 제한적인 빈 공간 | `size`, `width`, `height` | 없음 |
40
-
41
- `surface`가 Runtime v2의 커스텀 컴포넌트 경계입니다. 허용된 primitive와 Material token을 조합할
42
- 수 있지만 실행 코드, HTML, CSS, WebView, native view, 임의 shader는 넣을 수 없습니다. surface
43
- props는 `spacing`, `padding`, `margin`, 크기 제약, `alignment`, 색상, border,
44
- `elevation`, `opacity`, `clip`, `enabled`, `tooltip`, semantics로 제한됩니다.
45
-
46
- ```json
47
- {
48
- "type": "surface",
49
- "props": {
50
- "padding": {"horizontal": 16, "vertical": 12},
51
- "background_color": "primary_container",
52
- "foreground_color": "on_primary_container",
53
- "border_radius": 20
54
- },
55
- "children": [
56
- {"type": "text", "props": {"text": "오늘 일정 3개", "style": "heading"}}
57
- ]
58
- }
59
- ```
60
-
61
- ## 텍스트와 데이터 표시
62
-
63
- | type | 목적 | 주요 props |
64
- |---|---|---|
65
- | `text` | 제목·본문·라벨 | `text`, `style`, `align`, `max_lines`, `color` |
66
- | `icon` | Host icon token | `icon`, `size`, `color`, `semantic_label` |
67
- | `image` | package 또는 capability 결과 이미지 | `asset`/`url`, `fit`, 크기, `semantic_label` |
68
- | `avatar` | 사람·계정·공간의 작은 이미지 | `asset`/`url`, `size`, `semantic_label` |
69
- | `badge` | child 위의 짧은 상태 표식 | child 하나, `alignment` |
70
- | `metric` | 라벨·큰 값·보조 문구 | `label`, `value`, `supporting`, `tone` |
71
- | `progress` | 결정/비결정 진행률 | `label`, `value` |
72
- | `empty` | 빈 데이터와 다음 행동 안내 | `title`, `supporting`, `icon` |
73
-
74
- `image`와 `avatar`는 source를 정확히 하나만 가집니다.
75
-
76
- - `asset`: package 안의 `assets/` GIF/JPEG/PNG/WebP 경로
77
- - `url`: `{{data.<source>...}}` 또는 목록 안의 `{{item...}}` 전체 binding 하나
78
-
79
- literal 외부 URL, `data:`, `file:`, `javascript:`는 사용할 수 없습니다. `url` binding에는 capability
80
- 결과의 이미지 URL만 넣습니다. 이 이미지를 쓰는 package는 Manifest `permissions`에 `network`를
81
- 요청해야 하며 활성 Instance에도 `network` grant가 있어야 합니다.
82
-
83
- 앱은 binding으로 해석한 URL을 직접 요청하지 않습니다. Host가 인증된 이미지 proxy를 통해 공개
84
- HTTPS URL만 가져오며 DNS/IP SSRF 검사와 redirect 차단을 적용합니다. 응답은 PNG/JPEG/GIF/WebP 중
85
- 하나여야 하고 MIME type, magic byte, 실제 이미지 포맷이 일치해야 합니다. 최대 크기는 512 KiB,
86
- 가로·세로는 각각 4096 px 이하입니다. 정적 이미지는 1 frame, 애니메이션은 최대 128 frame이며
87
- `width × height × frame 수`로 계산한 frame 합산 pixel이 16,000,000 이하이어야 합니다. 조건을
88
- 통과하지 못하면 해당 이미지에만 오류 fallback을 표시하며 다른 UI와 capability 결과는 유지합니다.
89
-
90
- 중요한 이미지에는 `semantic_label`을 쓰고 순수 장식 이미지는 `exclude_semantics: true`를 사용합니다.
91
-
92
- ### Icon token
93
-
94
- `icon`, App bar action, navigation item의 `icon`과 `selected_icon`은 아래 Host token만 사용합니다.
95
- 임의 Material icon 이름이나 code point는 허용하지 않습니다. 목록에 없는 값은 Host, CLI, MCP와 앱
96
- schema parser가 거부하며 다른 아이콘으로 조용히 바꾸지 않습니다. 모든 token은 현재 Android
97
- Host에서 같은 의미의 Material 3 아이콘으로 표시되고, iOS·Desktop Host도 이름과 의미를 그대로
98
- 유지해야 합니다.
99
-
100
- | 목적 | 지원 token |
101
- |---|---|
102
- | 추가·편집·삭제 | `add`, `edit`, `delete`, `close`, `check` |
103
- | 이동·메뉴 | `arrow_back`, `arrow_forward`, `menu`, `more`, `home`, `home_filled` |
104
- | 파일·공유 | `file`, `folder`, `description`, `download`, `upload`, `share`, `link` |
105
- | 일정·데이터 | `calendar`, `clock`, `event`, `list`, `analytics` |
106
- | 상태·안내 | `info`, `help`, `error`, `favorite`, `inbox`, `sparkle` |
107
- | 사람·기능 | `person`, `extension`, `search`, `settings`, `refresh` |
108
- | 알림 | `notification`, `notifications`, `bell` |
109
- | 교육·급식 | `school`, `education`, `meal` |
110
- | 기존 package 호환 alias | `arrow`, `schedule`, `school.settings` |
111
-
112
- 호환 alias는 각각 `arrow_forward`, `clock`, `settings`와 같은 의미입니다. 새 화면은 의미가 더
113
- 명확한 기본 token을 우선 사용하되 기존 서명 package의 alias도 계속 렌더링됩니다. 아이콘만 있는
114
- action에는 반드시 `label`, 일반 `icon` node에는 `semantic_label`을 함께 제공합니다.
115
-
116
- ## 행동과 입력
117
-
118
- | type | 목적 | 주요 props |
119
- |---|---|---|
120
- | `button` | 명시적인 주·보조 행동 | `label`, `icon`, `style`, `full_width`, `enabled` |
121
- | `chip` | 필터·짧은 선택 | `label`, `icon`, `selected`, `enabled` |
122
- | `field` | 텍스트·숫자·autocomplete 입력 | `state_key`, `label`, `placeholder`, `input_type`, `suggestions`/`suggestions_source`, `persist` |
123
- | `select` | 고정·비동기·검색 선택지 | `state_key`, `label`, `options`/`options_source`, `searchable`, `persist` |
124
- | `switch` | boolean 설정 | `state_key`, `label`, `persist` |
125
- | `form` | 관련 입력 묶음 | `spacing`, `submit_label`, children, 선택적 submit action |
126
- | `dialog` | 짧고 집중된 확인·편집 | `title`, `label`, children |
127
- | `sheet` | 모바일 중심의 보조 작업 | `title`, `label`, children |
128
-
129
- `field`, `select`, `switch`의 `state_key`는 `initial_state`에 먼저 선언해야 합니다. select options는
130
- 1~32개의 `{ "value": ..., "label": "..." }` 객체입니다. 정적과 source 옵션을 둘 다 선언할 수
131
- 없습니다. `required`, 문자열 길이, 숫자 범위와 `error_text`는 Host form validation에
132
- 사용됩니다. `enabled: false`는 이유를 주변 문구로 설명할 때만 사용합니다.
133
-
134
- action은 `surface`, `card`, `button`, `chip`, `form`, `field`, `select`, `switch`에만 붙일 수
135
- 있습니다. 입력 action 직전에 Host가 IME composition과 controller를 commit하며,
136
- `validate: true`는 잘못된 form의 Tool·navigation 실행을 차단합니다.
137
-
138
- ## 반복과 데이터 시각화
139
-
140
- | type | 목적 | 주요 props | 제약 |
141
- |---|---|---|---|
142
- | `list` | 일반 항목 반복 | `source`, `empty_text`, `limit`, `dense` | item template child 정확히 하나 |
143
- | `timeline` | 시간 순서 사건 | `source`, `empty_text`, `limit` | item template child 정확히 하나 |
144
- | `calendar` | 날짜별 데이터 | `source`, `date_key`, `title_key`, `state_key` | source 필수 |
145
- | `chart` | 수치 비교·추세·분포 | `source`, `chart_type`, `x_key`, `y_key`, `show_legend` | `bar`, `line`, `donut`, `scatter` |
146
- | `table` | 행·열 구조 데이터 | `source`, `empty_text`, `limit`, `dense` | 최대 6열, 가로 스크롤 |
147
-
148
- 반복 template 안에서는 `{{item.title}}`처럼 `item` binding을 사용합니다. 큰 목록을 한 번에 렌더링하지
149
- 말고 capability에서 페이지나 기간을 나눕니다.
150
-
151
- ## 공통 UX 프리셋
152
-
153
- 프리셋은 새 실행 타입이 아니라 검증된 primitive 조합입니다. 그래서 Preview와 Flutter Host가 같은
154
- node를 렌더링하고 기존 플러그인도 별도 migration 없이 사용합니다.
155
-
156
- | 프리셋 | 조합 | Host가 맡는 상태 |
157
- |---|---|---|
158
- | 목록·검색 | `field` + `button` + `list`/`timeline` | source별 loading/error/retry, 빈 목록 |
159
- | 상세 | `app_bar` + `section` + `card`/`metric` | route back, stale response 차단 |
160
- | 폼 | `form` + `field`/`select`/`switch` + submit `button` | focus, keyboard, disabled feedback |
161
- | 설정 | `section` + persisted controls | state 복원, permission/connection 관리 링크 |
162
- | 빈 상태 | `empty` + 다음 행동 `button` | 의미 있는 title/supporting/icon |
163
- | 반응형 dashboard | `grid.min_item_width` + `row.stack_at` + adaptive navigation | bar/rail/drawer 전환 |
164
-
165
- 목록·검색의 최소 tree:
166
-
167
- ```json
168
- {
169
- "type": "column",
170
- "props": {"spacing": 12},
171
- "children": [
172
- {"type": "field", "props": {"state_key": "query", "label": "검색", "placeholder": "검색어"}},
173
- {
174
- "type": "button",
175
- "props": {"label": "찾기", "icon": "search"},
176
- "action": {"type": "invoke", "capability": "com.example.search", "query": "{{state.query}}", "arguments": {}, "store": "results"}
177
- },
178
- {
179
- "type": "list",
180
- "props": {"source": "data.results.data.items", "empty_text": "검색 결과가 없어요.", "limit": 50},
181
- "children": [
182
- {"type": "card", "props": {"title": "{{item.title}}", "subtitle": "{{item.summary}}"}}
183
- ]
184
- }
185
- ]
186
- }
187
- ```
188
-
189
- 권한 요청, 연결 필요, 첫 loading, capability error와 retry는 플러그인이 비슷한 경고 카드를 다시
190
- 만들지 않고 Host 공통 상태를 사용합니다. Plugin 상세의 권한/Connection 화면으로 이동한 뒤 같은
191
- route state와 persisted control을 복원합니다. 데이터가 정상적으로 비었을 때만 `empty` 또는
192
- `empty_text`를 사용하며 오류를 빈 목록으로 숨기지 않습니다.
193
-
194
- 모든 프리셋은 light/dark, 320px 폭, tablet/desktop 폭, 큰 글자에서 확인합니다. node별 tooltip,
195
- semantic label, 최소 탭 영역과 색 이외의 상태 표시는 Host Material 3 규칙을 따릅니다.
196
-
197
- ## A2UI Response catalog와의 관계
198
-
199
- 위 node는 Plugin Runtime을 구성하는 primitive입니다. AI가 직접 선택하는 Response catalog는
200
- `weather`, `world_clock`, `chart`, `image_gallery`,
201
- `exchange_rate`, `article_list`, `article_card`, `file_result`, `image_preview`, `video_preview`와 활성
202
- Plugin이 등록한 `plugin.<extension-id>`로 구성됩니다. 최종 답변 모델은 같은 Registry Skill과 Native
203
- Tool에서 출처(`builtin`/`plugin`), mode(`provider`/`generated`/`result`), 용도와 Schema를 확인합니다.
204
- provider mode는 조회 조건만, generated mode는 답변에 근거한 표시 값만 전달하며 Host가 검증한 뒤
205
- Runtime tree에 주입합니다. 특정 키워드만으로 Tool을 강제하지 않습니다.
206
- 구조화 표는 A2UI 전용 컴포넌트 대신 Markdown table을 사용합니다. 위 표의
207
- Runtime `table` node는 플러그인 화면 호환성을 위한 primitive로 계속 지원합니다.
208
- 자세한 등록·fallback 규칙은 [Response UI와 Agent Timeline](ai-response-and-timeline.md)을
209
- 참고하세요.
210
-
211
- ## 공통 크기·정렬·접근성 props
212
-
213
- - 크기: `width`, `height`, `min_width`, `max_width`, `min_height`, `max_height`
214
- - 여백: `padding`, `margin`, `spacing`
215
- - 정렬: `alignment`, `main_axis_alignment`, `cross_axis_alignment`, `main_axis_size`
216
- - 시각: `color`, `background_color`, `foreground_color`, `border_color`, `border_width`,
217
- `border_radius`, `elevation`, `opacity`
218
- - 이미지: `aspect_ratio`, `fit`
219
- - 접근성: `tooltip`, `semantic_label`, `exclude_semantics`
220
-
221
- 텍스트 `align`의 실제 렌더 값은 `start`, `end`, `left`, `right`, `center`, `justify`입니다. 이전
222
- UI v2 package가 사용한 다른 안전한 identifier도 호환을 위해 검증 단계에서는 수용하지만 Host는
223
- 기본 정렬로 처리합니다. 새 manifest에는 위 열거 값만 사용하세요.
224
-
225
- 크기와 색상 범위는 [토큰과 반응형](design-tokens-responsive.md)에 정리되어 있습니다.
@@ -1,171 +0,0 @@
1
- # 토큰, 크기, 색, 여백, 반응형
2
-
3
- Runtime v2는 기본적으로 Morit의 Material 3 theme를 사용합니다. 플러그인은 전체 앱의 theme를
4
- 바꾸지 않고 자신의 extension 범위 안에서만 제한된 `theme`와 node props를 적용합니다.
5
-
6
- ## Extension theme
7
-
8
- ```json
9
- {
10
- "theme": {
11
- "radius": 18,
12
- "spacing": 12,
13
- "density": "standard",
14
- "surface": {"elevation": 1},
15
- "border": {"width": 1},
16
- "icon": {"size": 22},
17
- "typography": {"scale": 1.0, "body_weight": 400, "title_weight": 700},
18
- "states": {"disabled_opacity": 0.38},
19
- "light": {
20
- "color_scheme": {
21
- "primary": "#315DA8",
22
- "on_primary": "#FFFFFF",
23
- "surface": "#F9F9FF",
24
- "on_surface": "#1A1B20"
25
- }
26
- },
27
- "dark": {
28
- "color_scheme": {
29
- "primary": "#AFC6FF",
30
- "on_primary": "#002F66",
31
- "surface": "#111318",
32
- "on_surface": "#E2E2E9"
33
- }
34
- }
35
- }
36
- }
37
- ```
38
-
39
- - `radius`: 0~64
40
- - `spacing`: 0~32
41
- - `density`: `compact`, `standard`, `comfortable`
42
- - `color_scheme`: Material role과 `#RRGGBB` 또는 `#AARRGGBB`
43
- - `typography`: `scale` 0.8~1.4, body/title weight 100~900
44
- - `surface`: `color`, `container_color`, `elevation` 0~24
45
- - `border`: `color`, `width` 0~8, `radius` 0~64
46
- - `icon`: `color`, `size` 12~64
47
- - `states`: `disabled_opacity` 0.2~0.8, `selected_color`, `focus_color`
48
- - `light`, `dark`: 위 필드의 mode별 partial override
49
-
50
- 최상위 base → 현재 mode variant → Host Material 3 theme 순서로 병합됩니다. 누락한 role은 Host가
51
- 채우므로 기존 플러그인은 선언을 추가하지 않아도 앱의 라이트·다크 모드를 따릅니다. 명백히 같은
52
- literal 전경/배경은 validate 오류이며 계산 가능한 4.5:1 미만 조합은 경고입니다. 대비가 확인된 작은
53
- palette만 override하고 모든 role을 임의로 복제하지 않습니다.
54
-
55
- ## 색상 token
56
-
57
- node의 `color`, `background_color`, `foreground_color`, `border_color`는 다음 semantic token이나
58
- hex를 사용합니다.
59
-
60
- ```text
61
- primary, on_primary, primary_container, on_primary_container
62
- secondary, on_secondary, secondary_container, on_secondary_container
63
- tertiary, on_tertiary, tertiary_container, on_tertiary_container
64
- error, on_error, error_container, on_error_container
65
- surface, on_surface, surface_variant, on_surface_variant
66
- outline, outline_variant
67
- inverse_surface, inverse_on_surface, inverse_primary
68
- shadow, scrim, transparent
69
- ```
70
-
71
- semantic token을 우선합니다. hex는 브랜드 식별이나 데이터 범례처럼 의미가 명확하고 light/dark
72
- 대비를 직접 확인한 경우에만 사용합니다. 상태를 색 하나로만 표현하지 말고 아이콘·문구를 함께
73
- 제공합니다.
74
-
75
- ## 여백
76
-
77
- `padding`과 `margin`은 0~128 숫자 하나 또는 다음 세 형태 중 하나입니다.
78
-
79
- ```json
80
- 16
81
- ```
82
-
83
- ```json
84
- {"all": 16}
85
- ```
86
-
87
- ```json
88
- {"horizontal": 16, "vertical": 12}
89
- ```
90
-
91
- ```json
92
- {"left": 16, "top": 8, "right": 16, "bottom": 20}
93
- ```
94
-
95
- 형태를 섞을 수 없습니다. `spacing`은 children 사이 간격이며 0~128입니다. 화면 가장자리 여백은
96
- Host가 제공하므로 root에 과도한 padding을 중복하지 않습니다.
97
-
98
- ## 크기와 surface
99
-
100
- | prop | 범위 |
101
- |---|---|
102
- | `width`, `height`, min/max variants | 0~4096 |
103
- | `border_width` | 0~8 |
104
- | `border_radius` | 0~64 |
105
- | `elevation` | 0~24 |
106
- | `opacity` | 0~1 |
107
- | `aspect_ratio` | 0.1~20 |
108
- | `size`, `max_lines`, `columns`, `limit` | 정수 0~100 |
109
-
110
- `min_width <= max_width`, `min_height <= max_height`여야 합니다. 고정 `width`와 `height`는 아이콘,
111
- avatar, 썸네일처럼 크기 의미가 있는 항목에만 쓰고, 본문 카드에는 min/max 제약과 자연 크기를
112
- 사용합니다.
113
-
114
- ## 정렬
115
-
116
- `alignment`:
117
-
118
- ```text
119
- top_left, top_center, top_right
120
- center_left, center, center_right
121
- bottom_left, bottom_center, bottom_right
122
- ```
123
-
124
- `main_axis_alignment`은 `start`, `end`, `center`, `space_between`, `space_around`,
125
- `space_evenly`를 사용합니다. `cross_axis_alignment`은 `start`, `end`, `center`, `stretch`,
126
- `baseline`을 사용합니다. `main_axis_size`는 `min` 또는 `max`입니다.
127
-
128
- 텍스트 `align`은 `start`, `end`, `left`, `right`, `center`, `justify`입니다. 다국어 화면에는
129
- 물리 방향 `left`/`right`보다 논리 방향 `start`/`end`가 안전합니다.
130
-
131
- ## 반응형 규칙
132
-
133
- ### Row 전환
134
-
135
- ```json
136
- {
137
- "type": "row",
138
- "props": {"spacing": 12, "stack_at": 480},
139
- "children": []
140
- }
141
- ```
142
-
143
- 가용 폭이 `stack_at`보다 작으면 세로로 배치합니다. 입력과 버튼, 두 개 이상의 긴 텍스트가 있는
144
- row에는 480 전후를 시작점으로 사용하고 실제 큰 글자 크기에서 확인합니다.
145
-
146
- ### Grid 열 축소
147
-
148
- ```json
149
- {
150
- "type": "grid",
151
- "props": {"columns": 3, "min_item_width": 160, "spacing": 12},
152
- "children": []
153
- }
154
- ```
155
-
156
- `columns`는 희망 최대 열 수이고, Host는 `min_item_width` 96~600을 지키도록 열을 줄입니다. 모바일
157
- 가로 폭을 채우기 위해 글자와 숫자를 지나치게 압축하지 않습니다.
158
-
159
- ### Adaptive navigation
160
-
161
- `navigation.type: "adaptive"`는 좁은 화면의 navigation bar와 넓은 화면의 rail/drawer를 Host가
162
- 선택하게 합니다. `rail_breakpoint`는 480~1600이며 꼭 필요한 경우에만 기본값을 조정합니다.
163
-
164
- ## 접근성과 동적 크기
165
-
166
- - 중요한 텍스트는 `max_lines`로 잘라 의미를 잃지 않게 합니다.
167
- - 버튼은 icon만 두지 말고 `label`을 제공합니다.
168
- - 이미지와 의미 있는 icon에는 `semantic_label`을 제공합니다.
169
- - 작은 `dense` 목록은 스캔 중심 화면에만 사용합니다.
170
- - light/dark theme, 시스템 큰 글자, 320px급 폭, tablet/desktop 폭에서 확인합니다.
171
- - animation이나 색 변화가 없어도 현재 선택과 진행 상태를 알 수 있어야 합니다.