@ncds/ui-admin-mcp 1.0.0-alpha.5 → 1.0.0-alpha.6
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/definitions/js-api.json +165 -0
- package/bin/server.js +3 -0
- package/bin/tools/renderToHtml.d.ts +2 -1
- package/bin/tools/renderToHtml.js +18 -4
- package/bin/utils/dataLoader.d.ts +11 -0
- package/bin/utils/dataLoader.js +19 -1
- package/package.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Modal": {
|
|
3
|
+
"className": "Modal",
|
|
4
|
+
"constructor": "new window.ncua.Modal(options?)",
|
|
5
|
+
"constructorParams": {
|
|
6
|
+
"size": "'sm' | 'md' | 'lg' (default: 'md')",
|
|
7
|
+
"closeOnBackdropClick": "boolean (default: false)",
|
|
8
|
+
"closeOnEsc": "boolean (default: true)",
|
|
9
|
+
"onClose": "() => void"
|
|
10
|
+
},
|
|
11
|
+
"methods": ["open()", "close()", "destroy()", "setContent(html: string | HTMLElement)", "isModalOpen()"],
|
|
12
|
+
"example": "const modal = new window.ncua.Modal({ size: 'sm', onClose: () => {} });\nmodal.setContent('<p>Content</p>');\nmodal.open();"
|
|
13
|
+
},
|
|
14
|
+
"SelectBox": {
|
|
15
|
+
"className": "SelectBox",
|
|
16
|
+
"constructor": "new window.ncua.SelectBox(element, config?)",
|
|
17
|
+
"constructorParams": {
|
|
18
|
+
"element": "HTMLElement — target container",
|
|
19
|
+
"config.options": "Array<{ id: string, text: string, disabled?: boolean }>",
|
|
20
|
+
"config.placeholder": "string (default: '선택하세요')",
|
|
21
|
+
"config.value": "string | number",
|
|
22
|
+
"config.onChange": "(value) => void",
|
|
23
|
+
"config.size": "'xs' | 'sm' | 'md'",
|
|
24
|
+
"config.multiple": "boolean",
|
|
25
|
+
"config.disabled": "boolean"
|
|
26
|
+
},
|
|
27
|
+
"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
|
+
},
|
|
30
|
+
"ComboBox": {
|
|
31
|
+
"className": "ComboBox",
|
|
32
|
+
"constructor": "new window.ncua.ComboBox(element, config?)",
|
|
33
|
+
"constructorParams": {
|
|
34
|
+
"element": "HTMLElement — target container",
|
|
35
|
+
"config.options": "Array<{ id: string, label: string, disabled?: boolean }>",
|
|
36
|
+
"config.placeholder": "string (default: '검색하고 선택하세요')",
|
|
37
|
+
"config.value": "string | number",
|
|
38
|
+
"config.onChange": "(value) => void",
|
|
39
|
+
"config.onSearch": "(searchValue: string) => void",
|
|
40
|
+
"config.size": "'xs' | 'sm'",
|
|
41
|
+
"config.multiple": "boolean"
|
|
42
|
+
},
|
|
43
|
+
"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});"
|
|
45
|
+
},
|
|
46
|
+
"DatePicker": {
|
|
47
|
+
"className": "DatePicker",
|
|
48
|
+
"constructor": "new window.ncua.DatePicker(wrapper, options)",
|
|
49
|
+
"constructorParams": {
|
|
50
|
+
"wrapper": "HTMLElement — container element",
|
|
51
|
+
"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"
|
|
55
|
+
},
|
|
56
|
+
"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});"
|
|
58
|
+
},
|
|
59
|
+
"Slider": {
|
|
60
|
+
"className": "Slider",
|
|
61
|
+
"constructor": "new window.ncua.Slider(element, options)",
|
|
62
|
+
"constructorParams": {
|
|
63
|
+
"element": "HTMLElement — container",
|
|
64
|
+
"options.min": "number (default: 0)",
|
|
65
|
+
"options.max": "number (default: 100)",
|
|
66
|
+
"options.step": "number (default: 1)",
|
|
67
|
+
"options.value": "number | [number, number]",
|
|
68
|
+
"options.onChange": "(value) => void",
|
|
69
|
+
"options.disabled": "boolean"
|
|
70
|
+
},
|
|
71
|
+
"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});"
|
|
73
|
+
},
|
|
74
|
+
"Tab": {
|
|
75
|
+
"className": "Tab",
|
|
76
|
+
"constructor": "new window.ncua.Tab(element)",
|
|
77
|
+
"constructorParams": {
|
|
78
|
+
"element": "HTMLElement — container with swiper structure"
|
|
79
|
+
},
|
|
80
|
+
"methods": ["destroy()"],
|
|
81
|
+
"example": "const tab = new window.ncua.Tab(document.getElementById('my-tab'));"
|
|
82
|
+
},
|
|
83
|
+
"Tooltip": {
|
|
84
|
+
"className": "Tooltip",
|
|
85
|
+
"constructor": "window.ncua.Tooltip.createShort(title, content?, options?)",
|
|
86
|
+
"constructorParams": {
|
|
87
|
+
"title": "string",
|
|
88
|
+
"content": "string (HTML supported)",
|
|
89
|
+
"options.position": "'top' | 'bottom' | 'left' | 'right' (default: 'top')"
|
|
90
|
+
},
|
|
91
|
+
"methods": ["getElement()", "showTooltip()", "hideTooltip()", "destroy()"],
|
|
92
|
+
"example": "const tip = window.ncua.Tooltip.createShort('Help', 'Tooltip content');\ndocument.getElementById('container').appendChild(tip.getElement());"
|
|
93
|
+
},
|
|
94
|
+
"Notification": {
|
|
95
|
+
"className": "Notification",
|
|
96
|
+
"constructor": "window.ncua.Notification.success(title, supportingText?, options?)",
|
|
97
|
+
"constructorParams": {
|
|
98
|
+
"title": "string",
|
|
99
|
+
"supportingText": "string",
|
|
100
|
+
"options.autoClose": "number (ms, 0 = no auto-close)"
|
|
101
|
+
},
|
|
102
|
+
"methods": ["show(parent?)", "remove()", "destroy()"],
|
|
103
|
+
"staticMethods": ["success()", "error()", "warning()", "info()", "neutral()"],
|
|
104
|
+
"example": "window.ncua.Notification.success('Saved', 'Changes saved successfully');"
|
|
105
|
+
},
|
|
106
|
+
"ProgressBar": {
|
|
107
|
+
"className": "ProgressBar",
|
|
108
|
+
"constructor": "new window.ncua.ProgressBar(options?)",
|
|
109
|
+
"constructorParams": {
|
|
110
|
+
"options.value": "number (0-100)",
|
|
111
|
+
"options.label": "'right' | 'bottom' | 'top-float' | 'bottom-float'"
|
|
112
|
+
},
|
|
113
|
+
"methods": ["getElement()", "updateOptions(newOptions)", "toHTML()"],
|
|
114
|
+
"example": "const bar = new window.ncua.ProgressBar({ value: 65, label: 'right' });\ndocument.getElementById('container').appendChild(bar.getElement());"
|
|
115
|
+
},
|
|
116
|
+
"Tag": {
|
|
117
|
+
"className": "Tag",
|
|
118
|
+
"constructor": "new window.ncua.Tag(options)",
|
|
119
|
+
"constructorParams": {
|
|
120
|
+
"options.text": "string",
|
|
121
|
+
"options.size": "'sm' | 'md' (default: 'sm')",
|
|
122
|
+
"options.close": "boolean — show close button",
|
|
123
|
+
"options.onButtonClick": "() => void — close handler"
|
|
124
|
+
},
|
|
125
|
+
"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());"
|
|
127
|
+
},
|
|
128
|
+
"FileInput": {
|
|
129
|
+
"className": "FileInput",
|
|
130
|
+
"constructor": "new window.ncua.FileInput(options)",
|
|
131
|
+
"constructorParams": {
|
|
132
|
+
"options.container": "HTMLElement | string — auto-append target",
|
|
133
|
+
"options.onChange": "(files: File[]) => void",
|
|
134
|
+
"options.onFail": "(invalidFiles: File[]) => void",
|
|
135
|
+
"options.disabled": "boolean"
|
|
136
|
+
},
|
|
137
|
+
"methods": ["getElement()", "setDisabled(boolean)", "destroy()"],
|
|
138
|
+
"example": "const fi = new window.ncua.FileInput({\n container: 'file-container',\n onChange: (files) => console.log(files)\n});"
|
|
139
|
+
},
|
|
140
|
+
"ImageFileInput": {
|
|
141
|
+
"className": "ImageFileInput",
|
|
142
|
+
"constructor": "new window.ncua.ImageFileInput(options)",
|
|
143
|
+
"constructorParams": {
|
|
144
|
+
"options.container": "HTMLElement | string — auto-append target",
|
|
145
|
+
"options.onChange": "(files: File[]) => void",
|
|
146
|
+
"options.maxFileCount": "number",
|
|
147
|
+
"options.acceptedFileTypes": "string[]",
|
|
148
|
+
"options.maxFileSize": "number"
|
|
149
|
+
},
|
|
150
|
+
"methods": ["getElement()", "getFiles()", "clearFiles()", "setDisabled(boolean)", "destroy()"],
|
|
151
|
+
"example": "const img = new window.ncua.ImageFileInput({\n container: 'image-container',\n maxFileCount: 5,\n onChange: (files) => console.log(files)\n});"
|
|
152
|
+
},
|
|
153
|
+
"FeaturedIcon": {
|
|
154
|
+
"className": "FeaturedIcon",
|
|
155
|
+
"constructor": "new window.ncua.FeaturedIcon(options)",
|
|
156
|
+
"constructorParams": {
|
|
157
|
+
"options.svgString": "string — SVG content",
|
|
158
|
+
"options.theme": "'light-circle' | 'dark-circle' | 'outline-circle' | 'square-outline'",
|
|
159
|
+
"options.color": "'neutral' | 'error' | 'warning' | 'success'",
|
|
160
|
+
"options.size": "'sm' | 'md' | 'lg' | 'xl'"
|
|
161
|
+
},
|
|
162
|
+
"methods": ["getElement()", "updateColor(color)", "updateSize(size)", "destroy()"],
|
|
163
|
+
"example": "const icon = new window.ncua.FeaturedIcon({ svgString: '<svg>...</svg>', color: 'success', size: 'md' });\ndocument.getElementById('container').appendChild(icon.getElement());"
|
|
164
|
+
}
|
|
165
|
+
}
|
package/bin/server.js
CHANGED
|
@@ -81,6 +81,7 @@ const main = async () => {
|
|
|
81
81
|
const rules = loadRules(definitionsDir);
|
|
82
82
|
const instructions = (0, dataLoader_js_1.loadInstructions)(definitionsDir);
|
|
83
83
|
const complianceRules = (0, dataLoader_js_1.loadComplianceRules)(definitionsDir);
|
|
84
|
+
const jsApiMap = (0, dataLoader_js_1.loadJsApi)(definitionsDir);
|
|
84
85
|
// ── 데이터 로딩 ──
|
|
85
86
|
const componentMap = (0, dataLoader_js_1.loadComponentsFromDir)(dataLoader_js_1.DEFAULT_DATA_DIR);
|
|
86
87
|
const { cdn: cdnMeta, icon: iconMeta } = (0, dataLoader_js_1.loadMeta)(dataLoader_js_1.DEFAULT_DATA_DIR);
|
|
@@ -143,6 +144,7 @@ const main = async () => {
|
|
|
143
144
|
cdnMeta,
|
|
144
145
|
iconMeta,
|
|
145
146
|
reactRuntime,
|
|
147
|
+
jsApiMap,
|
|
146
148
|
name,
|
|
147
149
|
props: props,
|
|
148
150
|
}));
|
|
@@ -165,6 +167,7 @@ const main = async () => {
|
|
|
165
167
|
cdnMeta,
|
|
166
168
|
iconMeta,
|
|
167
169
|
reactRuntime,
|
|
170
|
+
jsApiMap,
|
|
168
171
|
name,
|
|
169
172
|
props: props,
|
|
170
173
|
}));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 이 파일에는 fs, path, require, let이 없다.
|
|
6
6
|
*/
|
|
7
7
|
import type { ComponentData, ReactRuntime } from '../types.js';
|
|
8
|
-
import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
|
|
8
|
+
import type { CdnMeta, IconMeta, JsApiInfo } from '../utils/dataLoader.js';
|
|
9
9
|
import { type McpToolResponse } from '../utils/response.js';
|
|
10
10
|
/** render_to_html tool 파라미터 */
|
|
11
11
|
export interface RenderToHtmlParams {
|
|
@@ -14,6 +14,7 @@ export interface RenderToHtmlParams {
|
|
|
14
14
|
cdnMeta: CdnMeta | null;
|
|
15
15
|
iconMeta: IconMeta | null;
|
|
16
16
|
reactRuntime: ReactRuntime;
|
|
17
|
+
jsApiMap: Map<string, JsApiInfo>;
|
|
17
18
|
name: string;
|
|
18
19
|
props?: Record<string, unknown>;
|
|
19
20
|
}
|
|
@@ -132,7 +132,7 @@ const buildDataVersion = (cdnMeta, iconMeta) => {
|
|
|
132
132
|
// ── 진입점 ────────────────────────────────────────────────────────
|
|
133
133
|
/** render_to_html tool — props로 React 컴포넌트를 렌더링하여 HTML + React 매핑 반환 */
|
|
134
134
|
const renderToHtml = (params) => {
|
|
135
|
-
const { componentMap, bundle, cdnMeta, iconMeta, reactRuntime, name, props } = params;
|
|
135
|
+
const { componentMap, bundle, cdnMeta, iconMeta, reactRuntime, jsApiMap, name, props } = params;
|
|
136
136
|
const normalized = (0, response_js_1.normalizeName)(name);
|
|
137
137
|
const componentData = componentMap.get(normalized);
|
|
138
138
|
if (!componentData)
|
|
@@ -155,6 +155,22 @@ const renderToHtml = (params) => {
|
|
|
155
155
|
const defaultsUsed = componentData.props ? calcDefaultsUsed(componentData.props, userProps) : {};
|
|
156
156
|
const react = buildReactOutput(componentData, userProps, iconMeta);
|
|
157
157
|
const dataVersion = buildDataVersion(cdnMeta, iconMeta);
|
|
158
|
+
const jsApi = jsApiMap.get(exportName);
|
|
159
|
+
const js = componentData.jsRequired
|
|
160
|
+
? {
|
|
161
|
+
required: true,
|
|
162
|
+
description: '이 컴포넌트는 인터랙션을 위해 CDN JS가 필요합니다',
|
|
163
|
+
...(jsApi && {
|
|
164
|
+
api: {
|
|
165
|
+
className: jsApi.className,
|
|
166
|
+
constructor: jsApi.constructor,
|
|
167
|
+
constructorParams: jsApi.constructorParams,
|
|
168
|
+
methods: jsApi.methods,
|
|
169
|
+
example: jsApi.example,
|
|
170
|
+
},
|
|
171
|
+
}),
|
|
172
|
+
}
|
|
173
|
+
: { required: false };
|
|
158
174
|
return (0, response_js_1.successResponse)({
|
|
159
175
|
html,
|
|
160
176
|
component: normalized,
|
|
@@ -162,9 +178,7 @@ const renderToHtml = (params) => {
|
|
|
162
178
|
importPath: componentData.importPath,
|
|
163
179
|
appliedProps: userProps,
|
|
164
180
|
...(Object.keys(defaultsUsed).length > 0 && { defaultsUsed }),
|
|
165
|
-
js
|
|
166
|
-
? { required: true, description: '이 컴포넌트는 인터랙션을 위해 CDN JS가 필요합니다' }
|
|
167
|
-
: { required: false },
|
|
181
|
+
js,
|
|
168
182
|
cdn: cdnMeta ?? undefined,
|
|
169
183
|
dataVersion,
|
|
170
184
|
react,
|
|
@@ -27,6 +27,17 @@ export declare const loadTokenData: (dataDir: string) => TokenData;
|
|
|
27
27
|
export declare const loadComplianceRules: (definitionsDir: string) => ComplianceRulesData | null;
|
|
28
28
|
/** definitions/instructions.md를 로드하여 문자열 반환 */
|
|
29
29
|
export declare const loadInstructions: (definitionsDir: string) => string;
|
|
30
|
+
/** JS API 정보 타입 */
|
|
31
|
+
export interface JsApiInfo {
|
|
32
|
+
className: string;
|
|
33
|
+
constructor: string;
|
|
34
|
+
constructorParams: Record<string, string>;
|
|
35
|
+
methods: string[];
|
|
36
|
+
staticMethods?: string[];
|
|
37
|
+
example: string;
|
|
38
|
+
}
|
|
39
|
+
/** definitions/js-api.json을 로드하여 exportName→JsApiInfo Map 반환 */
|
|
40
|
+
export declare const loadJsApi: (definitionsDir: string) => Map<string, JsApiInfo>;
|
|
30
41
|
/** componentMap에서 이름으로 단일 컴포넌트 조회 */
|
|
31
42
|
export declare const getComponent: (map: Map<string, ComponentData>, name: string) => ComponentData | undefined;
|
|
32
43
|
/** componentMap의 모든 컴포넌트를 배열로 반환 */
|
package/bin/utils/dataLoader.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getAllComponents = exports.getComponent = exports.loadInstructions = exports.loadComplianceRules = exports.loadTokenData = exports.loadMeta = exports.loadIconData = exports.loadComponentsFromDir = exports.DEFAULT_DATA_DIR = void 0;
|
|
6
|
+
exports.getAllComponents = exports.getComponent = exports.loadJsApi = exports.loadInstructions = exports.loadComplianceRules = exports.loadTokenData = exports.loadMeta = exports.loadIconData = exports.loadComponentsFromDir = exports.DEFAULT_DATA_DIR = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* dataLoader — mcp/data/*.json + definitions/ 파일을 읽어 반환
|
|
9
9
|
*
|
|
@@ -165,6 +165,24 @@ const loadInstructions = (definitionsDir) => {
|
|
|
165
165
|
return fs_1.default.readFileSync(instructionsPath, 'utf-8').trim();
|
|
166
166
|
};
|
|
167
167
|
exports.loadInstructions = loadInstructions;
|
|
168
|
+
/** definitions/js-api.json을 로드하여 exportName→JsApiInfo Map 반환 */
|
|
169
|
+
const loadJsApi = (definitionsDir) => {
|
|
170
|
+
const filePath = path_1.default.resolve(definitionsDir, 'js-api.json');
|
|
171
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
172
|
+
logger_js_1.logger.info('js-api.json이 없습니다 — JS API 힌트 비활성화');
|
|
173
|
+
return new Map();
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
177
|
+
const data = JSON.parse(raw);
|
|
178
|
+
return new Map(Object.entries(data));
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
logger_js_1.logger.error(`js-api.json 파싱 실패: ${(0, response_js_1.toErrorMessage)(err)}`);
|
|
182
|
+
return new Map();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
exports.loadJsApi = loadJsApi;
|
|
168
186
|
// ── 순수 함수 (외부 상태 없음, Map을 파라미터로 받음) ──────────────────────
|
|
169
187
|
/** componentMap에서 이름으로 단일 컴포넌트 조회 */
|
|
170
188
|
const getComponent = (map, name) => map.get(name);
|