@morit/cli 1.4.0 → 1.4.2
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.
- package/README.md +3 -5
- package/assets/plugin_contract.json +1 -0
- package/bin/morit.js +0 -0
- package/package.json +1 -1
- package/src/cli.js +5 -0
- package/src/workspace.js +60 -8
- package/assets/docs/README.md +0 -107
- package/assets/docs/ai-response-and-timeline.md +0 -211
- package/assets/docs/ai-skill-and-docx-workflow.md +0 -83
- package/assets/docs/app-builder.md +0 -56
- package/assets/docs/authentication.md +0 -140
- package/assets/docs/components.md +0 -225
- package/assets/docs/design-tokens-responsive.md +0 -171
- package/assets/docs/docs-index.json +0 -94
- package/assets/docs/examples-notion.md +0 -83
- package/assets/docs/examples-school-life.md +0 -79
- package/assets/docs/getting-started.md +0 -132
- package/assets/docs/information-hierarchy.md +0 -81
- package/assets/docs/instances-and-connectors.md +0 -93
- package/assets/docs/lifecycle-and-api.md +0 -169
- package/assets/docs/local-cli.md +0 -125
- package/assets/docs/manifest.md +0 -247
- package/assets/docs/packaging-and-testing.md +0 -131
- package/assets/docs/permissions-and-data.md +0 -149
- package/assets/docs/platform-compatibility.md +0 -62
- package/assets/docs/plugin-storage.md +0 -175
- package/assets/docs/project-structure.md +0 -102
- package/assets/docs/remote-mcp.md +0 -158
- package/assets/docs/school-life-privacy.md +0 -55
- package/assets/docs/screens-layout-navigation.md +0 -95
- package/assets/docs/sdk-and-mcp.md +0 -199
- package/assets/docs/tool-and-skill.md +0 -188
- package/assets/docs/troubleshooting.md +0 -121
- package/assets/docs/ui-extensions.md +0 -75
- package/assets/docs/ui-runtime-v2.md +0 -406
- package/assets/docs/verification.md +0 -133
|
@@ -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이나 색 변화가 없어도 현재 선택과 진행 상태를 알 수 있어야 합니다.
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schema_version": 1,
|
|
3
|
-
"service": {
|
|
4
|
-
"name": "Morit Plugin",
|
|
5
|
-
"description": "Morit 플러그인을 설계하고 구현해 검증·배포하는 공식 개발 문서"
|
|
6
|
-
},
|
|
7
|
-
"categories": [
|
|
8
|
-
{
|
|
9
|
-
"slug": "start",
|
|
10
|
-
"name": "1. 시작과 개발 흐름",
|
|
11
|
-
"description": "개발 방식을 고르고 첫 패키지를 만드는 순서",
|
|
12
|
-
"documents": [
|
|
13
|
-
{ "file": "README.md", "slug": "index", "title": "개발 문서 인덱스" },
|
|
14
|
-
{ "file": "getting-started.md", "slug": "getting-started", "title": "시작하기와 개발 흐름" },
|
|
15
|
-
{ "file": "app-builder.md", "slug": "app-builder", "title": "앱에서 플러그인 만들기" }
|
|
16
|
-
]
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"slug": "project",
|
|
20
|
-
"name": "2. 프로젝트와 계약",
|
|
21
|
-
"description": "프로젝트 구조, manifest, instance와 연결 모델",
|
|
22
|
-
"documents": [
|
|
23
|
-
{ "file": "project-structure.md", "slug": "project-structure", "title": "프로젝트 구조와 fragment" },
|
|
24
|
-
{ "file": "manifest.md", "slug": "manifest", "title": "Manifest 레퍼런스" },
|
|
25
|
-
{ "file": "instances-and-connectors.md", "slug": "instances-and-connectors", "title": "Instance, Connector, 복합 패키지" }
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"slug": "ui",
|
|
30
|
-
"name": "3. 화면과 사용자 경험",
|
|
31
|
-
"description": "화면 구조에서 컴포넌트, 반응형, Response UI까지",
|
|
32
|
-
"documents": [
|
|
33
|
-
{ "file": "screens-layout-navigation.md", "slug": "screens-layout-navigation", "title": "화면, 레이아웃, 내비게이션" },
|
|
34
|
-
{ "file": "components.md", "slug": "components", "title": "기본·커스텀 컴포넌트" },
|
|
35
|
-
{ "file": "design-tokens-responsive.md", "slug": "design-tokens-responsive", "title": "토큰, 크기, 색, 여백, 반응형" },
|
|
36
|
-
{ "file": "information-hierarchy.md", "slug": "information-hierarchy", "title": "화면 분리와 정보 계층" },
|
|
37
|
-
{ "file": "ui-extensions.md", "slug": "ui-extensions", "title": "UI extension point" },
|
|
38
|
-
{ "file": "ui-runtime-v2.md", "slug": "ui-runtime-v2", "title": "UI Runtime v2 레퍼런스" },
|
|
39
|
-
{ "file": "ai-response-and-timeline.md", "slug": "response-ui", "title": "Response UI와 Agent Timeline" }
|
|
40
|
-
]
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"slug": "capabilities",
|
|
44
|
-
"name": "4. 기능, 데이터, 사용자 제어",
|
|
45
|
-
"description": "Tool, Skill, 권한, 설정, 알림과 외부 인증",
|
|
46
|
-
"documents": [
|
|
47
|
-
{ "file": "tool-and-skill.md", "slug": "tool-and-skill", "title": "Tool, Skill, Search, Slash Command" },
|
|
48
|
-
{ "file": "permissions-and-data.md", "slug": "permissions-settings-notifications", "title": "권한, 설정, 저장소, 알림" },
|
|
49
|
-
{ "file": "plugin-storage.md", "slug": "plugin-storage", "title": "Plugin Local Storage와 AI 접근" },
|
|
50
|
-
{ "file": "authentication.md", "slug": "authentication", "title": "외부 서비스 인증과 Cloud Secrets" },
|
|
51
|
-
{ "file": "ai-skill-and-docx-workflow.md", "slug": "ai-skill-artifacts", "title": "AI Skill과 파일 산출물" }
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"slug": "platforms",
|
|
56
|
-
"name": "5. 플랫폼",
|
|
57
|
-
"description": "Android 우선 구현과 iOS·Desktop 호환 원칙",
|
|
58
|
-
"documents": [
|
|
59
|
-
{ "file": "platform-compatibility.md", "slug": "platform-compatibility", "title": "Android, iOS, Desktop 호환" }
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"slug": "delivery",
|
|
64
|
-
"name": "6. 검증과 배포",
|
|
65
|
-
"description": "validate, preview, build, deploy와 오류 해결",
|
|
66
|
-
"documents": [
|
|
67
|
-
{ "file": "local-cli.md", "slug": "local-cli", "title": "공식 CLI 개발 흐름" },
|
|
68
|
-
{ "file": "packaging-and-testing.md", "slug": "packaging-and-testing", "title": "패키징과 테스트" },
|
|
69
|
-
{ "file": "troubleshooting.md", "slug": "troubleshooting", "title": "오류 해결" },
|
|
70
|
-
{ "file": "verification.md", "slug": "verification", "title": "예제 검증 기록과 경계" }
|
|
71
|
-
]
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"slug": "automation",
|
|
75
|
-
"name": "7. SDK, MCP, API",
|
|
76
|
-
"description": "AI 에이전트 연결과 Host API 운영",
|
|
77
|
-
"documents": [
|
|
78
|
-
{ "file": "sdk-and-mcp.md", "slug": "sdk-and-mcp", "title": "CLI와 AI 에이전트 MCP" },
|
|
79
|
-
{ "file": "remote-mcp.md", "slug": "remote-mcp", "title": "원격 Plugin MCP" },
|
|
80
|
-
{ "file": "lifecycle-and-api.md", "slug": "lifecycle-and-api", "title": "수명주기와 HTTP API" }
|
|
81
|
-
]
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"slug": "examples",
|
|
85
|
-
"name": "8. 실제 예제",
|
|
86
|
-
"description": "완성된 플러그인의 구조와 실사용 검증",
|
|
87
|
-
"documents": [
|
|
88
|
-
{ "file": "examples-school-life.md", "slug": "school-life", "title": "학교 생활 플러그인" },
|
|
89
|
-
{ "file": "school-life-privacy.md", "slug": "school-life-privacy", "title": "학교 생활 개인정보 처리" },
|
|
90
|
-
{ "file": "examples-notion.md", "slug": "notion", "title": "Notion 플러그인" }
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
}
|