@ncds/ui-admin-mcp 1.0.0-alpha.13 → 1.0.0-alpha.15
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/bin/components.bundle.js +1 -1
- package/bin/definitions/external/editor.d.ts +50 -0
- package/bin/definitions/external/editor.js +53 -0
- package/bin/definitions/js-api.json +139 -21
- package/bin/server.d.ts +5 -0
- package/bin/server.js +21 -15
- package/bin/tools/external/editor.d.ts +17 -0
- package/bin/tools/external/editor.js +88 -0
- package/bin/tools/renderToHtml.d.ts +22 -7
- package/bin/tools/renderToHtml.js +232 -11
- package/bin/types.d.ts +10 -0
- package/data/badge-group.json +4 -4
- package/data/badge.json +3 -3
- package/data/bread-crumb.json +1 -1
- package/data/button-group.json +10 -0
- package/data/button.json +2 -2
- package/data/carousel-arrow.json +1 -1
- package/data/carousel-number-group.json +1 -1
- package/data/checkbox.json +1 -1
- package/data/combo-box.json +4 -4
- package/data/date-picker.json +2 -2
- package/data/divider.json +1 -1
- package/data/dot.json +1 -1
- package/data/dropdown.json +38 -18
- package/data/editor.json +85 -0
- package/data/empty-state.json +4 -4
- package/data/featured-icon.json +2 -2
- package/data/file-input.json +6 -1
- package/data/horizontal-tab.json +4 -4
- package/data/image-file-input.json +6 -1
- package/data/input-base.json +1 -1
- package/data/modal.json +51 -7
- package/data/notification.json +7 -23
- package/data/number-input.json +1 -1
- package/data/password-input.json +1 -1
- package/data/progress-bar.json +2 -2
- package/data/progress-circle.json +1 -1
- package/data/radio.json +1 -1
- package/data/range-date-picker-with-buttons.json +5 -5
- package/data/range-date-picker.json +5 -5
- package/data/select-box.json +4 -4
- package/data/select.json +8 -3
- package/data/slider.json +1 -1
- package/data/spinner.json +1 -1
- package/data/switch.json +1 -1
- package/data/table.json +3 -1
- package/data/tag.json +2 -2
- package/data/textarea.json +1 -1
- package/data/toggle.json +1 -1
- package/data/tooltip.json +1 -1
- package/data/vertical-tab.json +4 -4
- package/package.json +3 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 5.10: editor 외부 분기 정의
|
|
3
|
+
*
|
|
4
|
+
* froala 기반 에디터(`@ncds/editor-html`)의 응답 메타·CDN URL·props·cdnDefaults·methods.
|
|
5
|
+
* `tools/external/editor.ts` 의 `buildEditorResponse` 가 본 정의를 읽어 응답 조립.
|
|
6
|
+
* (json 대신 ts — tsconfig `module: Node16` 환경에서 import 단순화. 빌드 시 자동 포함.)
|
|
7
|
+
*/
|
|
8
|
+
export declare const editorDefinition: {
|
|
9
|
+
readonly name: "editor";
|
|
10
|
+
readonly className: "NcdsEditor";
|
|
11
|
+
readonly category: "form";
|
|
12
|
+
readonly description: "froala 기반 WYSIWYG 에디터. textarea 를 in-place 변환.";
|
|
13
|
+
readonly cdn: {
|
|
14
|
+
readonly version: "0.0";
|
|
15
|
+
readonly css: "https://fe-sdk.cdn-nhncommerce.com/@ncds/editor/0.0/ncds-editor.css";
|
|
16
|
+
readonly js: "https://fe-sdk.cdn-nhncommerce.com/@ncds/editor/0.0/ncds-editor.js";
|
|
17
|
+
};
|
|
18
|
+
readonly props: readonly [{
|
|
19
|
+
readonly name: "heightMin";
|
|
20
|
+
readonly type: "number";
|
|
21
|
+
readonly default: 300;
|
|
22
|
+
readonly description: "최소 높이 (px)";
|
|
23
|
+
}, {
|
|
24
|
+
readonly name: "heightMax";
|
|
25
|
+
readonly type: "number";
|
|
26
|
+
readonly default: 600;
|
|
27
|
+
readonly description: "최대 높이 (px)";
|
|
28
|
+
}, {
|
|
29
|
+
readonly name: "heightResize";
|
|
30
|
+
readonly type: "boolean";
|
|
31
|
+
readonly default: false;
|
|
32
|
+
readonly description: "높이 조정 가능 여부";
|
|
33
|
+
}, {
|
|
34
|
+
readonly name: "placeholderText";
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly default: "내용을 입력하세요.";
|
|
37
|
+
readonly description: "플레이스홀더 텍스트";
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "imageUploadCallback";
|
|
40
|
+
readonly type: "function";
|
|
41
|
+
readonly description: "이미지 업로드 콜백 함수명 (전역 함수). 외부 분기에서 stripFunctionProps 로 자동 제외 — data-* 직렬화 대상 아님";
|
|
42
|
+
}];
|
|
43
|
+
readonly cdnDefaults: {
|
|
44
|
+
heightMin: number;
|
|
45
|
+
heightMax: number;
|
|
46
|
+
heightResize: boolean;
|
|
47
|
+
placeholderText: string;
|
|
48
|
+
};
|
|
49
|
+
readonly methods: readonly ["getHTML()", "setHTML(html)", "getText()", "focus()", "blur()", "enable()", "disable()", "destroy()", "insertImage(src, alt, link)", "insertLink(href, text)", "insertTable(rows, cols)", "on(event, handler)", "off(event)"];
|
|
50
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Story 5.10: editor 외부 분기 정의
|
|
4
|
+
*
|
|
5
|
+
* froala 기반 에디터(`@ncds/editor-html`)의 응답 메타·CDN URL·props·cdnDefaults·methods.
|
|
6
|
+
* `tools/external/editor.ts` 의 `buildEditorResponse` 가 본 정의를 읽어 응답 조립.
|
|
7
|
+
* (json 대신 ts — tsconfig `module: Node16` 환경에서 import 단순화. 빌드 시 자동 포함.)
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.editorDefinition = void 0;
|
|
11
|
+
exports.editorDefinition = {
|
|
12
|
+
name: 'editor',
|
|
13
|
+
className: 'NcdsEditor',
|
|
14
|
+
category: 'form',
|
|
15
|
+
description: 'froala 기반 WYSIWYG 에디터. textarea 를 in-place 변환.',
|
|
16
|
+
cdn: {
|
|
17
|
+
version: '0.0',
|
|
18
|
+
css: 'https://fe-sdk.cdn-nhncommerce.com/@ncds/editor/0.0/ncds-editor.css',
|
|
19
|
+
js: 'https://fe-sdk.cdn-nhncommerce.com/@ncds/editor/0.0/ncds-editor.js',
|
|
20
|
+
},
|
|
21
|
+
props: [
|
|
22
|
+
{ name: 'heightMin', type: 'number', default: 300, description: '최소 높이 (px)' },
|
|
23
|
+
{ name: 'heightMax', type: 'number', default: 600, description: '최대 높이 (px)' },
|
|
24
|
+
{ name: 'heightResize', type: 'boolean', default: false, description: '높이 조정 가능 여부' },
|
|
25
|
+
{ name: 'placeholderText', type: 'string', default: '내용을 입력하세요.', description: '플레이스홀더 텍스트' },
|
|
26
|
+
{
|
|
27
|
+
name: 'imageUploadCallback',
|
|
28
|
+
type: 'function',
|
|
29
|
+
description: '이미지 업로드 콜백 함수명 (전역 함수). 외부 분기에서 stripFunctionProps 로 자동 제외 — data-* 직렬화 대상 아님',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
cdnDefaults: {
|
|
33
|
+
heightMin: 300,
|
|
34
|
+
heightMax: 600,
|
|
35
|
+
heightResize: false,
|
|
36
|
+
placeholderText: '내용을 입력하세요.',
|
|
37
|
+
},
|
|
38
|
+
methods: [
|
|
39
|
+
'getHTML()',
|
|
40
|
+
'setHTML(html)',
|
|
41
|
+
'getText()',
|
|
42
|
+
'focus()',
|
|
43
|
+
'blur()',
|
|
44
|
+
'enable()',
|
|
45
|
+
'disable()',
|
|
46
|
+
'destroy()',
|
|
47
|
+
'insertImage(src, alt, link)',
|
|
48
|
+
'insertLink(href, text)',
|
|
49
|
+
'insertTable(rows, cols)',
|
|
50
|
+
'on(event, handler)',
|
|
51
|
+
'off(event)',
|
|
52
|
+
],
|
|
53
|
+
};
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"SelectBox": {
|
|
15
15
|
"className": "SelectBox",
|
|
16
|
+
"cdnPattern": "A",
|
|
16
17
|
"constructor": "new window.ncua.SelectBox(element, config?)",
|
|
17
18
|
"constructorParams": {
|
|
18
19
|
"element": "HTMLElement — target container",
|
|
@@ -25,10 +26,15 @@
|
|
|
25
26
|
"config.disabled": "boolean"
|
|
26
27
|
},
|
|
27
28
|
"methods": ["getValues()", "setValues(options[])", "getSelectedCount()"],
|
|
28
|
-
"example": "const select = new window.ncua.SelectBox(document.getElementById('my-select'), {\n options: [{ id: '1', text: 'Option 1' }],\n onChange: (value) => console.log(value)\n});"
|
|
29
|
+
"example": "const select = new window.ncua.SelectBox(document.getElementById('my-select'), {\n options: [{ id: '1', text: 'Option 1' }],\n onChange: (value) => console.log(value)\n});",
|
|
30
|
+
"cdnDefaults": {
|
|
31
|
+
"size": "xs",
|
|
32
|
+
"placeholder": "선택하세요"
|
|
33
|
+
}
|
|
29
34
|
},
|
|
30
35
|
"ComboBox": {
|
|
31
36
|
"className": "ComboBox",
|
|
37
|
+
"cdnPattern": "A",
|
|
32
38
|
"constructor": "new window.ncua.ComboBox(element, config?)",
|
|
33
39
|
"constructorParams": {
|
|
34
40
|
"element": "HTMLElement — target container",
|
|
@@ -41,23 +47,45 @@
|
|
|
41
47
|
"config.multiple": "boolean"
|
|
42
48
|
},
|
|
43
49
|
"methods": ["getValues()", "setValues(options[])", "clearInput()", "getSelectedCount()"],
|
|
44
|
-
"example": "const combo = new window.ncua.ComboBox(document.getElementById('my-combo'), {\n options: [{ id: '1', label: 'React' }],\n onSearch: (q) => console.log(q)\n});"
|
|
50
|
+
"example": "const combo = new window.ncua.ComboBox(document.getElementById('my-combo'), {\n options: [{ id: '1', label: 'React' }],\n onSearch: (q) => console.log(q)\n});",
|
|
51
|
+
"cdnDefaults": {
|
|
52
|
+
"size": "xs",
|
|
53
|
+
"placeholder": "검색하세요"
|
|
54
|
+
}
|
|
45
55
|
},
|
|
46
56
|
"DatePicker": {
|
|
47
57
|
"className": "DatePicker",
|
|
58
|
+
"cdnPattern": "A",
|
|
48
59
|
"constructor": "new window.ncua.DatePicker(wrapper, options)",
|
|
49
60
|
"constructorParams": {
|
|
50
61
|
"wrapper": "HTMLElement — container element",
|
|
51
62
|
"options.size": "'xs' | 'sm'",
|
|
52
|
-
"options.datePickerOptions": "Array<{ element: string, placeholder?: string, options: FlatpickrOptions }>",
|
|
53
|
-
"options.buttons": "Array<{ text: string, period: number, unit: 'days'|'months', isCurrent: boolean }>",
|
|
54
|
-
"options.onValidationError": "(error) => void"
|
|
63
|
+
"options.datePickerOptions": "Array<{ element: string, attrName?: string, placeholder?: string, options: FlatpickrOptions }>",
|
|
64
|
+
"options.buttons": "Array<{ text: string, period: number, unit: 'days'|'weeks'|'months'|'years', isCurrent: boolean }>",
|
|
65
|
+
"options.onValidationError": "(error: ValidationError) => void",
|
|
66
|
+
"options.autoComplete": "'on' | 'off'"
|
|
55
67
|
},
|
|
56
68
|
"methods": ["getDates()", "setDate(dates: string[])", "setMultipleDates(dates: string[])"],
|
|
57
|
-
"example": "const dp = new window.ncua.DatePicker(document.getElementById('my-dp'), {\n size: 'xs',\n datePickerOptions: [{ element: 'date-input', placeholder: 'Select date', options: { dateFormat: 'Y-m-d' } }]\n});"
|
|
69
|
+
"example": "const dp = new window.ncua.DatePicker(document.getElementById('my-dp'), {\n size: 'xs',\n datePickerOptions: [{ element: 'date-input', placeholder: 'Select date', options: { dateFormat: 'Y-m-d' } }]\n});",
|
|
70
|
+
"cdnDefaults": {
|
|
71
|
+
"size": "xs",
|
|
72
|
+
"datePickerOptions": [
|
|
73
|
+
{
|
|
74
|
+
"options": {
|
|
75
|
+
"mode": "single",
|
|
76
|
+
"static": true,
|
|
77
|
+
"dateFormat": "Y-m-d",
|
|
78
|
+
"clickOpens": true,
|
|
79
|
+
"allowInvalidPreload": true,
|
|
80
|
+
"locale": "ko"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
58
85
|
},
|
|
59
86
|
"Slider": {
|
|
60
87
|
"className": "Slider",
|
|
88
|
+
"cdnPattern": "A",
|
|
61
89
|
"constructor": "new window.ncua.Slider(element, options)",
|
|
62
90
|
"constructorParams": {
|
|
63
91
|
"element": "HTMLElement — container",
|
|
@@ -66,10 +94,17 @@
|
|
|
66
94
|
"options.step": "number (default: 1)",
|
|
67
95
|
"options.value": "number | [number, number]",
|
|
68
96
|
"options.onChange": "(value) => void",
|
|
69
|
-
"options.disabled": "boolean"
|
|
97
|
+
"options.disabled": "boolean",
|
|
98
|
+
"options.labelPosition": "'bottom' | 'top-floating'"
|
|
70
99
|
},
|
|
71
100
|
"methods": ["getValue()", "setValue(value)", "enable()", "disable()", "destroy()"],
|
|
72
|
-
"example": "const slider = new window.ncua.Slider(document.getElementById('my-slider'), {\n min: 0, max: 100, value: 50,\n onChange: (v) => console.log(v)\n});"
|
|
101
|
+
"example": "const slider = new window.ncua.Slider(document.getElementById('my-slider'), {\n min: 0, max: 100, value: 50,\n onChange: (v) => console.log(v)\n});",
|
|
102
|
+
"cdnDefaults": {
|
|
103
|
+
"min": 0,
|
|
104
|
+
"max": 100,
|
|
105
|
+
"step": 1,
|
|
106
|
+
"labelPosition": "top-floating"
|
|
107
|
+
}
|
|
73
108
|
},
|
|
74
109
|
"Tab": {
|
|
75
110
|
"className": "Tab",
|
|
@@ -82,22 +117,51 @@
|
|
|
82
117
|
},
|
|
83
118
|
"Tooltip": {
|
|
84
119
|
"className": "Tooltip",
|
|
120
|
+
"cdnPattern": "C",
|
|
85
121
|
"constructor": "window.ncua.Tooltip.createShort(title, content?, options?)",
|
|
86
122
|
"constructorParams": {
|
|
87
123
|
"title": "string",
|
|
88
124
|
"content": "string (HTML supported)",
|
|
89
|
-
"options.position": "'top' | 'bottom' | 'left' | 'right' (default: '
|
|
125
|
+
"options.position": "'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'auto' (default: 'auto')",
|
|
126
|
+
"options.type": "'long' | 'short'",
|
|
127
|
+
"options.tooltipType": "'white' | 'black'",
|
|
128
|
+
"options.iconType": "'stroke' | 'fill'",
|
|
129
|
+
"options.size": "'sm' | 'md'",
|
|
130
|
+
"options.hideArrow": "boolean",
|
|
131
|
+
"options.zIndex": "number",
|
|
132
|
+
"options.useShadowDOM": "boolean",
|
|
133
|
+
"options.className": "string"
|
|
90
134
|
},
|
|
91
135
|
"methods": ["getElement()", "showTooltip()", "hideTooltip()", "destroy()"],
|
|
92
|
-
"example": "const tip = window.ncua.Tooltip.createShort('Help', 'Tooltip content');\ndocument.getElementById('container').appendChild(tip.getElement());"
|
|
136
|
+
"example": "const tip = window.ncua.Tooltip.createShort('Help', 'Tooltip content');\ndocument.getElementById('container').appendChild(tip.getElement());",
|
|
137
|
+
"cdnDefaults": {
|
|
138
|
+
"type": "short",
|
|
139
|
+
"tooltipType": "black",
|
|
140
|
+
"iconType": "stroke",
|
|
141
|
+
"position": "auto",
|
|
142
|
+
"size": "sm"
|
|
143
|
+
}
|
|
93
144
|
},
|
|
94
145
|
"Notification": {
|
|
95
146
|
"className": "Notification",
|
|
147
|
+
"cdnPattern": "C",
|
|
96
148
|
"constructor": "window.ncua.Notification.success(title, supportingText?, options?)",
|
|
149
|
+
"cdnDefaults": {
|
|
150
|
+
"type": "floating",
|
|
151
|
+
"color": "neutral",
|
|
152
|
+
"autoClose": 0
|
|
153
|
+
},
|
|
97
154
|
"constructorParams": {
|
|
98
155
|
"title": "string",
|
|
99
156
|
"supportingText": "string",
|
|
100
|
-
"options.autoClose": "number (ms, 0 = no auto-close)"
|
|
157
|
+
"options.autoClose": "number (ms, 0 = no auto-close)",
|
|
158
|
+
"options.type": "'floating' | 'message' | 'full-width'",
|
|
159
|
+
"options.color": "'neutral' | 'success' | 'error' | 'warning' | 'info'",
|
|
160
|
+
"options.actions": "Array<NotificationAction>",
|
|
161
|
+
"options.onClose": "() => void",
|
|
162
|
+
"options.className": "string",
|
|
163
|
+
"options.supportTextLink": "string — full-width 전용 supportingText 링크 URL",
|
|
164
|
+
"options.onHidePermanently": "() => void — full-width/message 다시보지 않기 핸들러"
|
|
101
165
|
},
|
|
102
166
|
"methods": ["show(parent?)", "remove()", "destroy()"],
|
|
103
167
|
"staticMethods": ["success()", "error()", "warning()", "info()", "neutral()"],
|
|
@@ -105,47 +169,100 @@
|
|
|
105
169
|
},
|
|
106
170
|
"ProgressBar": {
|
|
107
171
|
"className": "ProgressBar",
|
|
172
|
+
"cdnPattern": "C",
|
|
108
173
|
"constructor": "new window.ncua.ProgressBar(options?)",
|
|
109
174
|
"constructorParams": {
|
|
110
175
|
"options.value": "number (0-100)",
|
|
111
|
-
"options.label": "'right' | 'bottom' | 'top-float' | 'bottom-float'"
|
|
176
|
+
"options.label": "'right' | 'bottom' | 'top-float' | 'bottom-float'",
|
|
177
|
+
"options.segments": "ProgressSegment[] — 멀티 세그먼트 진행률",
|
|
178
|
+
"options.showZeroLabel": "boolean — 0% 라벨 표시 여부",
|
|
179
|
+
"options.valueToPercent": "boolean — value 를 % 환산 표시"
|
|
112
180
|
},
|
|
113
181
|
"methods": ["getElement()", "updateOptions(newOptions)", "toHTML()"],
|
|
114
|
-
"example": "const bar = new window.ncua.ProgressBar({ value: 65, label: 'right' });\ndocument.getElementById('container').appendChild(bar.getElement());"
|
|
182
|
+
"example": "const bar = new window.ncua.ProgressBar({ value: 65, label: 'right' });\ndocument.getElementById('container').appendChild(bar.getElement());",
|
|
183
|
+
"cdnDefaults": {
|
|
184
|
+
"label": "right"
|
|
185
|
+
}
|
|
115
186
|
},
|
|
116
187
|
"Tag": {
|
|
117
188
|
"className": "Tag",
|
|
189
|
+
"cdnPattern": "C",
|
|
118
190
|
"constructor": "new window.ncua.Tag(options)",
|
|
119
191
|
"constructorParams": {
|
|
120
192
|
"options.text": "string",
|
|
121
193
|
"options.size": "'sm' | 'md' (default: 'sm')",
|
|
122
194
|
"options.close": "boolean — show close button",
|
|
123
|
-
"options.onButtonClick": "() => void — close handler"
|
|
195
|
+
"options.onButtonClick": "(event: MouseEvent) => void — close handler",
|
|
196
|
+
"options.count": "string — 카운트 표시",
|
|
197
|
+
"options.icon": "SideSlotType — 좌측 아이콘 슬롯",
|
|
198
|
+
"options.maxLength": "number — 최대 텍스트 길이 (default: 20)"
|
|
124
199
|
},
|
|
125
200
|
"methods": ["getElement()", "setText(text)", "setCount(count)", "destroy()"],
|
|
126
|
-
"example": "const tag = new window.ncua.Tag({ text: 'Label', close: true, onButtonClick: () => tag.destroy() });\ndocument.getElementById('container').appendChild(tag.getElement());"
|
|
201
|
+
"example": "const tag = new window.ncua.Tag({ text: 'Label', close: true, onButtonClick: () => tag.destroy() });\ndocument.getElementById('container').appendChild(tag.getElement());",
|
|
202
|
+
"cdnDefaults": {
|
|
203
|
+
"size": "sm"
|
|
204
|
+
}
|
|
127
205
|
},
|
|
128
206
|
"FileInput": {
|
|
129
207
|
"className": "FileInput",
|
|
208
|
+
"cdnPattern": "B",
|
|
130
209
|
"constructor": "new window.ncua.FileInput(options)",
|
|
131
210
|
"constructorParams": {
|
|
132
211
|
"options.container": "HTMLElement | string — auto-append target",
|
|
133
212
|
"options.onChange": "(files: File[]) => void",
|
|
134
213
|
"options.onFail": "(invalidFiles: File[]) => void",
|
|
135
|
-
"options.disabled": "boolean"
|
|
214
|
+
"options.disabled": "boolean",
|
|
215
|
+
"options.size": "'xs' | 'sm'",
|
|
216
|
+
"options.accept": "string — e.g. '.jpg,.png' or 'image/*'",
|
|
217
|
+
"options.multiple": "boolean",
|
|
218
|
+
"options.maxFileCount": "number",
|
|
219
|
+
"options.maxFileSize": "number — bytes",
|
|
220
|
+
"options.buttonLabel": "string",
|
|
221
|
+
"options.label": "string",
|
|
222
|
+
"options.hintItems": "string[]",
|
|
223
|
+
"options.isRequired": "boolean",
|
|
224
|
+
"options.showHelpIcon": "boolean",
|
|
225
|
+
"options.hintText": "string",
|
|
226
|
+
"options.validation": "boolean",
|
|
227
|
+
"options.destructive": "boolean",
|
|
228
|
+
"options.className": "string"
|
|
136
229
|
},
|
|
137
230
|
"methods": ["getElement()", "setDisabled(boolean)", "destroy()"],
|
|
138
|
-
"example": "const fi = new window.ncua.FileInput({\n container: 'file-container',\n onChange: (files) => console.log(files)\n});"
|
|
231
|
+
"example": "const fi = new window.ncua.FileInput({\n container: 'file-container',\n onChange: (files) => console.log(files)\n});",
|
|
232
|
+
"cdnDefaults": {
|
|
233
|
+
"buttonLabel": "파일 찾기"
|
|
234
|
+
}
|
|
139
235
|
},
|
|
140
236
|
"ImageFileInput": {
|
|
141
237
|
"className": "ImageFileInput",
|
|
238
|
+
"cdnPattern": "B",
|
|
142
239
|
"constructor": "new window.ncua.ImageFileInput(options)",
|
|
143
240
|
"constructorParams": {
|
|
144
241
|
"options.container": "HTMLElement | string — auto-append target",
|
|
145
|
-
"options.onChange": "(files: File[]) => void",
|
|
242
|
+
"options.onChange": "(files: File[]) => void — controlled mode",
|
|
243
|
+
"options.onFileSelect": "(files: File[]) => void — uncontrolled mode",
|
|
244
|
+
"options.onFail": "(files: InvalidFile[]) => void",
|
|
245
|
+
"options.value": "File[] — initial files (controlled mode)",
|
|
246
|
+
"options.disabled": "boolean",
|
|
247
|
+
"options.size": "'xs' | 'sm'",
|
|
248
|
+
"options.accept": "string — e.g. '.jpg,.png' or 'image/*'",
|
|
249
|
+
"options.multiple": "boolean",
|
|
146
250
|
"options.maxFileCount": "number",
|
|
147
|
-
"options.
|
|
148
|
-
"options.
|
|
251
|
+
"options.maxFileSize": "number — bytes",
|
|
252
|
+
"options.buttonLabel": "string",
|
|
253
|
+
"options.imagePreviewTooltipLabel": "string",
|
|
254
|
+
"options.label": "string",
|
|
255
|
+
"options.hintItems": "string[]",
|
|
256
|
+
"options.isRequired": "boolean",
|
|
257
|
+
"options.showHelpIcon": "boolean",
|
|
258
|
+
"options.hintText": "string",
|
|
259
|
+
"options.validation": "boolean",
|
|
260
|
+
"options.destructive": "boolean",
|
|
261
|
+
"options.fileInputName": "string — name attr (PHP form 제출용)",
|
|
262
|
+
"options.className": "string"
|
|
263
|
+
},
|
|
264
|
+
"cdnDefaults": {
|
|
265
|
+
"buttonLabel": "파일 찾기"
|
|
149
266
|
},
|
|
150
267
|
"methods": ["getElement()", "getFiles()", "clearFiles()", "setDisabled(boolean)", "destroy()"],
|
|
151
268
|
"example": "const img = new window.ncua.ImageFileInput({\n container: 'image-container',\n maxFileCount: 5,\n onChange: (files) => console.log(files)\n});"
|
|
@@ -157,7 +274,8 @@
|
|
|
157
274
|
"options.svgString": "string — SVG content",
|
|
158
275
|
"options.theme": "'light-circle' | 'dark-circle' | 'outline-circle' | 'square-outline'",
|
|
159
276
|
"options.color": "'neutral' | 'error' | 'warning' | 'success'",
|
|
160
|
-
"options.size": "'sm' | 'md' | 'lg' | 'xl'"
|
|
277
|
+
"options.size": "'sm' | 'md' | 'lg' | 'xl'",
|
|
278
|
+
"options.className": "string"
|
|
161
279
|
},
|
|
162
280
|
"methods": ["getElement()", "updateColor(color)", "updateSize(size)", "destroy()"],
|
|
163
281
|
"example": "const icon = new window.ncua.FeaturedIcon({ svgString: '<svg>...</svg>', color: 'success', size: 'md' });\ndocument.getElementById('container').appendChild(icon.getElement());"
|
package/bin/server.d.ts
CHANGED
package/bin/server.js
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
2
|
/**
|
|
7
3
|
* MCP 서버 진입점 — 모든 데이터 소유, tool 등록, transport 연결
|
|
8
4
|
*
|
|
9
5
|
* 이 파일만 Action(부수효과)을 포함한다. tool 핸들러는 모두 Calculation.
|
|
10
6
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
// biome-ignore-all lint/complexity/useLiteralKeys: descriptions['key'] 형태가 tool 이름과 일관성 (Story 5.8 scope 외)
|
|
8
|
+
// biome-ignore-all lint/style/noMagicNumbers: 기존 코드의 zod .max(N) 등 (Story 5.8 scope 외)
|
|
9
|
+
// biome-ignore-all lint/complexity/noExcessiveCognitiveComplexity: main() 진입점 (Story 5.8 scope 외, 별도 refactor 스토리)
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
15
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
14
16
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
15
19
|
const zod_1 = require("zod");
|
|
16
|
-
const dataLoader_js_1 = require("./utils/dataLoader.js");
|
|
17
|
-
const logger_js_1 = require("./utils/logger.js");
|
|
18
|
-
const listComponents_js_1 = require("./tools/listComponents.js");
|
|
19
|
-
const searchComponent_js_1 = require("./tools/searchComponent.js");
|
|
20
20
|
const getComponentProps_js_1 = require("./tools/getComponentProps.js");
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
const getDesignTokens_js_1 = require("./tools/getDesignTokens.js");
|
|
22
|
+
const listComponents_js_1 = require("./tools/listComponents.js");
|
|
23
23
|
const listIcons_js_1 = require("./tools/listIcons.js");
|
|
24
|
-
const
|
|
24
|
+
const ping_js_1 = require("./tools/ping.js");
|
|
25
25
|
const renderToHtml_js_1 = require("./tools/renderToHtml.js");
|
|
26
|
+
const searchComponent_js_1 = require("./tools/searchComponent.js");
|
|
27
|
+
const searchIcon_js_1 = require("./tools/searchIcon.js");
|
|
28
|
+
const validateHtml_js_1 = require("./tools/validateHtml.js");
|
|
29
|
+
const dataLoader_js_1 = require("./utils/dataLoader.js");
|
|
30
|
+
const domEnvironment_js_1 = require("./utils/domEnvironment.js");
|
|
31
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
26
32
|
const response_js_1 = require("./utils/response.js");
|
|
27
|
-
const getDesignTokens_js_1 = require("./tools/getDesignTokens.js");
|
|
28
33
|
const version_js_1 = require("./version.js");
|
|
29
|
-
const domEnvironment_js_1 = require("./utils/domEnvironment.js");
|
|
30
34
|
// ── definitions/ 로딩 헬퍼 (Action) ─────────────────────────────────────────
|
|
31
35
|
/** tool-definitions.json을 1회 로딩하여 descriptions + capabilities를 분리 반환 */
|
|
32
36
|
const loadToolDefinitions = (definitionsDir) => {
|
|
@@ -167,6 +171,7 @@ const main = async () => {
|
|
|
167
171
|
jsApiMap,
|
|
168
172
|
name,
|
|
169
173
|
props: props,
|
|
174
|
+
instanceId: (0, node_crypto_1.randomUUID)().slice(0, 6),
|
|
170
175
|
})));
|
|
171
176
|
server.registerTool('render_to_html_batch', {
|
|
172
177
|
description: descriptions['render_to_html_batch'],
|
|
@@ -191,6 +196,7 @@ const main = async () => {
|
|
|
191
196
|
jsApiMap,
|
|
192
197
|
name,
|
|
193
198
|
props: props,
|
|
199
|
+
instanceId: (0, node_crypto_1.randomUUID)().slice(0, 6),
|
|
194
200
|
}));
|
|
195
201
|
const parsed = results.map((r) => {
|
|
196
202
|
const text = r.content[0].text;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 5.10: editor 외부 분기 모듈
|
|
3
|
+
*
|
|
4
|
+
* froala 기반 에디터(`@ncds/editor-html`)는 ui-admin 외부 패키지라
|
|
5
|
+
* ui-admin 흐름(componentMap / data/{name}.json / cdnAssets) 을 거치지 않고
|
|
6
|
+
* 본 모듈에서 응답을 직접 조립한다. ui-admin 코드 미수정 → 5-8/5-9 회귀 위험 0.
|
|
7
|
+
*
|
|
8
|
+
* 호출 진입점: `renderToHtml` 함수 시작부 (Task 1 결정).
|
|
9
|
+
*/
|
|
10
|
+
import { type McpToolResponse } from '../../utils/response.js';
|
|
11
|
+
/** `normalizeName` 적용된 컴포넌트 이름이 editor alias 인지 검사 */
|
|
12
|
+
export declare const isEditorAlias: (normalizedName: string) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* editor 응답 조립 — ui-admin 흐름 우회.
|
|
15
|
+
* 5-9 helper(`mergeCdnDefaults` / `stripFunctionProps`) import 재사용 (코드 중복 0).
|
|
16
|
+
*/
|
|
17
|
+
export declare const buildEditorResponse: (props: Record<string, unknown>, instanceId: string) => McpToolResponse;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Story 5.10: editor 외부 분기 모듈
|
|
4
|
+
*
|
|
5
|
+
* froala 기반 에디터(`@ncds/editor-html`)는 ui-admin 외부 패키지라
|
|
6
|
+
* ui-admin 흐름(componentMap / data/{name}.json / cdnAssets) 을 거치지 않고
|
|
7
|
+
* 본 모듈에서 응답을 직접 조립한다. ui-admin 코드 미수정 → 5-8/5-9 회귀 위험 0.
|
|
8
|
+
*
|
|
9
|
+
* 호출 진입점: `renderToHtml` 함수 시작부 (Task 1 결정).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.buildEditorResponse = exports.isEditorAlias = void 0;
|
|
13
|
+
const editor_js_1 = require("../../definitions/external/editor.js");
|
|
14
|
+
const response_js_1 = require("../../utils/response.js");
|
|
15
|
+
const renderToHtml_js_1 = require("../renderToHtml.js");
|
|
16
|
+
/** editor 컴포넌트로 매칭할 alias 집합 (Task 1 결정 — 보수적 5개) */
|
|
17
|
+
const EDITOR_ALIASES = new Set(['editor', 'ncds-editor', 'ncdseditor', 'ncuaeditor', 'editor-html']);
|
|
18
|
+
/** `normalizeName` 적용된 컴포넌트 이름이 editor alias 인지 검사 */
|
|
19
|
+
const isEditorAlias = (normalizedName) => EDITOR_ALIASES.has(normalizedName);
|
|
20
|
+
exports.isEditorAlias = isEditorAlias;
|
|
21
|
+
/** camelCase → kebab-case (heightMin → height-min) */
|
|
22
|
+
const camelToKebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
23
|
+
/** HTML attribute 값 escape (` " ` / ` & ` / ` < ` / ` > `) */
|
|
24
|
+
const escapeHtmlAttr = (s) => s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
25
|
+
/**
|
|
26
|
+
* options → data-* 속성 직렬화.
|
|
27
|
+
* - camelCase → kebab-case
|
|
28
|
+
* - 값 String(v) (boolean → "true"/"false", number → 숫자 문자열)
|
|
29
|
+
* - null / undefined / function skip (함수는 stripFunctionProps 로 사전 제거되지만 방어적 추가 필터)
|
|
30
|
+
*/
|
|
31
|
+
const serializeDataAttributes = (options) => Object.entries(options)
|
|
32
|
+
.filter(([, v]) => v !== null && v !== undefined && typeof v !== 'function')
|
|
33
|
+
.map(([k, v]) => `data-${camelToKebab(k)}="${escapeHtmlAttr(String(v))}"`)
|
|
34
|
+
.join(' ');
|
|
35
|
+
/**
|
|
36
|
+
* patternD: textarea + data-* + element-only init script.
|
|
37
|
+
* 5-8 의 buildPatternA/B/C 와 동일 시그니처 `(id, className, options) => string`.
|
|
38
|
+
* patternA/B/C 와 차이:
|
|
39
|
+
* - 컨테이너: `<div>` → `<textarea>` (froala 가 textarea 를 in-place 변환)
|
|
40
|
+
* - options 위치: `new X(wrapper, options)` 두번째 인자 → textarea 의 `data-*` 속성
|
|
41
|
+
* - 글로벌: `ncua.X` → `window.NcdsEditor`
|
|
42
|
+
* - 호출 위치: ui-admin 의 `PATTERN_BY_COMPONENT` 분기 → 외부 분기 모듈 안 (이 함수)
|
|
43
|
+
*/
|
|
44
|
+
const buildPatternD = (id, className, options) => {
|
|
45
|
+
const dataAttrs = serializeDataAttributes(options);
|
|
46
|
+
const tagBody = dataAttrs ? `id="${id}" ${dataAttrs}` : `id="${id}"`;
|
|
47
|
+
return [
|
|
48
|
+
`<textarea ${tagBody}></textarea>`,
|
|
49
|
+
'<script>',
|
|
50
|
+
" document.addEventListener('DOMContentLoaded', function () {",
|
|
51
|
+
` new window.${className}(document.querySelector('#${id}'));`,
|
|
52
|
+
' });',
|
|
53
|
+
'</script>',
|
|
54
|
+
].join('\n');
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* editor 응답 조립 — ui-admin 흐름 우회.
|
|
58
|
+
* 5-9 helper(`mergeCdnDefaults` / `stripFunctionProps`) import 재사용 (코드 중복 0).
|
|
59
|
+
*/
|
|
60
|
+
const buildEditorResponse = (props, instanceId) => {
|
|
61
|
+
// 5-9: 함수 prop 사전 제거 + cdnDefaults deep merge (사용자 props 우선)
|
|
62
|
+
const cleanedProps = (0, renderToHtml_js_1.stripFunctionProps)(props);
|
|
63
|
+
const { merged, defaultsApplied } = (0, renderToHtml_js_1.mergeCdnDefaults)('editor', editor_js_1.editorDefinition.cdnDefaults, cleanedProps);
|
|
64
|
+
const id = `ed-${instanceId}`;
|
|
65
|
+
const html = buildPatternD(id, editor_js_1.editorDefinition.className, merged);
|
|
66
|
+
// cdnDefaults 적용된 키 → defaultsUsed 응답
|
|
67
|
+
const defaultsUsed = Object.fromEntries(defaultsApplied.map((key) => [key, editor_js_1.editorDefinition.cdnDefaults[key]]));
|
|
68
|
+
return (0, response_js_1.successResponse)({
|
|
69
|
+
html,
|
|
70
|
+
component: editor_js_1.editorDefinition.name,
|
|
71
|
+
exportName: editor_js_1.editorDefinition.className,
|
|
72
|
+
importPath: '@ncds/editor-html',
|
|
73
|
+
appliedProps: merged,
|
|
74
|
+
...(Object.keys(defaultsUsed).length > 0 && { defaultsUsed }),
|
|
75
|
+
js: {
|
|
76
|
+
required: true,
|
|
77
|
+
description: '이 컴포넌트는 인터랙션을 위해 CDN JS가 필요합니다',
|
|
78
|
+
api: {
|
|
79
|
+
className: editor_js_1.editorDefinition.className,
|
|
80
|
+
methods: editor_js_1.editorDefinition.methods,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
cdn: editor_js_1.editorDefinition.cdn,
|
|
84
|
+
dataVersion: { '@ncds/editor': editor_js_1.editorDefinition.cdn.version },
|
|
85
|
+
react: { notes: 'React not supported in editor (vanilla JS only)' },
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
exports.buildEditorResponse = buildEditorResponse;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* render_to_html tool — 컴포넌트 속성을 전달하면 정확한 HTML + React 매핑을 반환 (순수 함수)
|
|
3
|
-
*
|
|
4
|
-
* DOM 환경과 React/ReactDOM은 server.ts에서 초기화하고 파라미터로 전달한다.
|
|
5
|
-
* 이 파일에는 fs, path, require, let이 없다.
|
|
6
|
-
*/
|
|
7
|
-
import type { ComponentData, ReactRuntime, JsApiInfo } from '../types.js';
|
|
1
|
+
import type { ComponentData, JsApiInfo, ReactRuntime } from '../types.js';
|
|
8
2
|
import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
|
|
9
3
|
import { type McpToolResponse } from '../utils/response.js';
|
|
10
4
|
/** render_to_html tool 파라미터 */
|
|
@@ -18,6 +12,27 @@ export interface RenderToHtmlParams {
|
|
|
18
12
|
jsApiMap: Map<string, JsApiInfo>;
|
|
19
13
|
name: string;
|
|
20
14
|
props?: Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* CDN init 패턴(Story 5.8) 생성 시 `<div id>` 접미사로 사용되는 고유 식별자.
|
|
17
|
+
* server.ts가 매 호출마다 `crypto.randomUUID().slice(0, 6)` 방식으로 생성해 주입한다.
|
|
18
|
+
* renderToHtml.ts는 순수 함수 원칙(파일 헤더 주석)을 위해 내부에서 랜덤을 생성하지 않는다.
|
|
19
|
+
*/
|
|
20
|
+
instanceId: string;
|
|
21
21
|
}
|
|
22
|
+
/** Story 5.9: 사용자 props 에서 함수 prop 재귀 제거 (JSON 직렬화·deep merge 안전).
|
|
23
|
+
* cdnDefaults 는 JSON 이라 함수 없음 → 사용자 props 만 정리.
|
|
24
|
+
* Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
|
|
25
|
+
export declare const stripFunctionProps: (obj: unknown) => unknown;
|
|
26
|
+
/** Story 5.9: cdnDefaults 와 사용자 props 를 deep merge.
|
|
27
|
+
* - 사용자 값 우선 (lodash merge 의 두 번째 인자가 우선)
|
|
28
|
+
* - 함수 prop 은 사용자 props 에서 stripFunctionProps 로 사전 제거
|
|
29
|
+
* - DatePicker `datePickerOptions` 배열 한정: 각 항목의 `options` 에 cdnDefaults 의 첫 항목 options 를 per-element 부분 merge
|
|
30
|
+
* - 다른 배열(`buttons` 등) 은 사용자 값 그대로
|
|
31
|
+
* 반환: { merged, defaultsApplied } — defaultsApplied 는 cdnDefaults 에 있고 사용자 props 에 없는 top-level 키 (defaultsUsed 응답용)
|
|
32
|
+
* Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
|
|
33
|
+
export declare const mergeCdnDefaults: (componentName: string, cdnDefaults: Record<string, unknown> | undefined, userProps: Record<string, unknown>) => {
|
|
34
|
+
merged: Record<string, unknown>;
|
|
35
|
+
defaultsApplied: string[];
|
|
36
|
+
};
|
|
22
37
|
/** render_to_html tool — props로 React 컴포넌트를 렌더링하여 HTML + React 매핑 반환 */
|
|
23
38
|
export declare const renderToHtml: (params: RenderToHtmlParams) => McpToolResponse;
|