@ncds/ui-admin-mcp 1.0.0-alpha.16 → 1.0.0-alpha.18
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 +17 -1
- package/bin/definitions/external/step-guide.d.ts +61 -0
- package/bin/definitions/external/step-guide.js +52 -0
- package/bin/definitions/js-api.json +47 -13
- package/bin/tools/external/editor.d.ts +3 -2
- package/bin/tools/external/editor.js +1 -1
- package/bin/tools/external/step-guide.d.ts +19 -0
- package/bin/tools/external/step-guide.js +79 -0
- package/bin/tools/getComponentProps.d.ts +3 -0
- package/bin/tools/getComponentProps.js +2 -2
- package/bin/tools/ping.d.ts +1 -1
- package/bin/tools/renderToHtml.js +9 -1
- package/bin/tools/searchComponent.d.ts +5 -0
- package/bin/tools/searchComponent.js +3 -3
- package/bin/tools/validateHtml.d.ts +8 -6
- package/bin/tools/validateHtml.js +6 -6
- package/bin/utils/bemValidator.d.ts +8 -6
- package/bin/utils/bemValidator.js +16 -4
- package/bin/utils/compliance.d.ts +7 -6
- package/bin/utils/compliance.js +5 -3
- package/bin/utils/dataLoader.d.ts +16 -14
- package/bin/utils/dataLoader.js +3 -2
- package/bin/utils/fuzzyMatch.js +1 -0
- package/bin/utils/response.js +1 -1
- package/bin/utils/tokenValidator.d.ts +4 -3
- package/bin/utils/tokenValidator.js +13 -11
- package/bin/version.d.ts +4 -2
- package/bin/version.js +4 -2
- package/data/_icons.json +317 -2
- package/data/block-container.json +95 -0
- package/data/block-header.json +208 -0
- package/data/combo-box.json +1 -2
- package/data/data-grid.json +33 -5
- package/data/dropdown.json +11 -1
- package/data/file-input.json +6 -5
- package/data/image-file-input.json +6 -5
- package/data/input-base.json +1 -1
- package/data/modal.json +5 -2
- package/data/notification.json +30 -22
- package/data/number-input.json +1 -1
- package/data/page-title.json +135 -0
- package/data/pagination.json +8 -4
- package/data/password-input.json +25 -10
- package/data/progress-bar.json +6 -5
- package/data/progress-circle.json +8 -5
- package/data/radio.json +3 -2
- package/data/select-box.json +17 -10
- package/data/step-guide.json +130 -0
- package/data/table.json +50 -6
- package/data/tag.json +7 -4
- package/package.json +13 -11
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 6.1: step-guide 외부 분기 정의
|
|
3
|
+
*
|
|
4
|
+
* vanilla JS 함수 기반 step-by-step 가이드 투어(`step-guide`)의 응답 메타·CDN URL·props·cdnDefaults·methods.
|
|
5
|
+
* `tools/external/step-guide.ts`의 `buildStepGuideResponse`가 본 정의를 읽어 응답 조립.
|
|
6
|
+
* 5-10 editor 패턴 답습 — 단, step-guide는 함수 호출(`window.stepGuide({el, options})`) 형식.
|
|
7
|
+
* (json 대신 ts — tsconfig `module: Node16` 환경에서 import 단순화. 빌드 시 자동 포함.)
|
|
8
|
+
*/
|
|
9
|
+
export declare const stepGuideDefinition: {
|
|
10
|
+
readonly name: "step-guide";
|
|
11
|
+
readonly exportName: "stepGuide";
|
|
12
|
+
readonly category: "overlays";
|
|
13
|
+
readonly description: "vanilla JS 함수 기반 step-by-step 가이드 투어. CDN 로드 후 window.stepGuide({el, options}) 호출.";
|
|
14
|
+
readonly cdn: {
|
|
15
|
+
readonly version: "2.0";
|
|
16
|
+
readonly css: "https://fe-sdk.cdn-nhncommerce.com/@ncds/step-guide/2.0/step-guide.min.css";
|
|
17
|
+
readonly js: "https://fe-sdk.cdn-nhncommerce.com/@ncds/step-guide/2.0/step-guide.min.js";
|
|
18
|
+
};
|
|
19
|
+
readonly props: readonly [{
|
|
20
|
+
readonly name: "steps";
|
|
21
|
+
readonly type: "Step[]";
|
|
22
|
+
readonly required: true;
|
|
23
|
+
readonly description: "단계 배열";
|
|
24
|
+
}, {
|
|
25
|
+
readonly name: "useAnimation";
|
|
26
|
+
readonly type: "boolean";
|
|
27
|
+
readonly default: true;
|
|
28
|
+
readonly description: "애니메이션 사용 여부";
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "buttonLabel";
|
|
31
|
+
readonly type: "{ prev, next, done, skip }";
|
|
32
|
+
readonly description: "버튼 라벨 (v2 default: 이전/다음/완료/나중에 하기)";
|
|
33
|
+
}, {
|
|
34
|
+
readonly name: "hideSkip";
|
|
35
|
+
readonly type: "boolean";
|
|
36
|
+
readonly default: true;
|
|
37
|
+
readonly description: "스킵 버튼 숨김 여부";
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "hideStepCount";
|
|
40
|
+
readonly type: "boolean";
|
|
41
|
+
readonly default: true;
|
|
42
|
+
readonly description: "스텝 카운트 숨김 여부";
|
|
43
|
+
}, {
|
|
44
|
+
readonly name: "hideStepPrefix";
|
|
45
|
+
readonly type: "boolean";
|
|
46
|
+
readonly default: false;
|
|
47
|
+
readonly description: "STEP N prefix 숨김 여부. 단계 1개일 때는 옵션과 무관하게 자동 생략";
|
|
48
|
+
}, {
|
|
49
|
+
readonly name: "exitOnOverlayClick";
|
|
50
|
+
readonly type: "boolean";
|
|
51
|
+
readonly default: true;
|
|
52
|
+
readonly description: "오버레이 클릭 시 종료 여부";
|
|
53
|
+
}];
|
|
54
|
+
readonly cdnDefaults: {
|
|
55
|
+
useAnimation: boolean;
|
|
56
|
+
hideSkip: boolean;
|
|
57
|
+
hideStepCount: boolean;
|
|
58
|
+
exitOnOverlayClick: boolean;
|
|
59
|
+
};
|
|
60
|
+
readonly methods: readonly ["goToStep(index)", "previousStep(e)", "nextStep(e)", "onChange(callback)", "onExit(callback)", "exit(e)"];
|
|
61
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Story 6.1: step-guide 외부 분기 정의
|
|
4
|
+
*
|
|
5
|
+
* vanilla JS 함수 기반 step-by-step 가이드 투어(`step-guide`)의 응답 메타·CDN URL·props·cdnDefaults·methods.
|
|
6
|
+
* `tools/external/step-guide.ts`의 `buildStepGuideResponse`가 본 정의를 읽어 응답 조립.
|
|
7
|
+
* 5-10 editor 패턴 답습 — 단, step-guide는 함수 호출(`window.stepGuide({el, options})`) 형식.
|
|
8
|
+
* (json 대신 ts — tsconfig `module: Node16` 환경에서 import 단순화. 빌드 시 자동 포함.)
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.stepGuideDefinition = void 0;
|
|
12
|
+
exports.stepGuideDefinition = {
|
|
13
|
+
name: 'step-guide',
|
|
14
|
+
exportName: 'stepGuide', // CDN UMD default export = 함수 (소문자, class 아님)
|
|
15
|
+
category: 'overlays',
|
|
16
|
+
description: 'vanilla JS 함수 기반 step-by-step 가이드 투어. CDN 로드 후 window.stepGuide({el, options}) 호출.',
|
|
17
|
+
cdn: {
|
|
18
|
+
version: '2.0',
|
|
19
|
+
css: 'https://fe-sdk.cdn-nhncommerce.com/@ncds/step-guide/2.0/step-guide.min.css',
|
|
20
|
+
js: 'https://fe-sdk.cdn-nhncommerce.com/@ncds/step-guide/2.0/step-guide.min.js',
|
|
21
|
+
},
|
|
22
|
+
props: [
|
|
23
|
+
{ name: 'steps', type: 'Step[]', required: true, description: '단계 배열' },
|
|
24
|
+
{ name: 'useAnimation', type: 'boolean', default: true, description: '애니메이션 사용 여부' },
|
|
25
|
+
{
|
|
26
|
+
name: 'buttonLabel',
|
|
27
|
+
type: '{ prev, next, done, skip }',
|
|
28
|
+
description: '버튼 라벨 (v2 default: 이전/다음/완료/나중에 하기)',
|
|
29
|
+
},
|
|
30
|
+
{ name: 'hideSkip', type: 'boolean', default: true, description: '스킵 버튼 숨김 여부' },
|
|
31
|
+
{ name: 'hideStepCount', type: 'boolean', default: true, description: '스텝 카운트 숨김 여부' },
|
|
32
|
+
{
|
|
33
|
+
name: 'hideStepPrefix',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false,
|
|
36
|
+
description: 'STEP N prefix 숨김 여부. 단계 1개일 때는 옵션과 무관하게 자동 생략',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'exitOnOverlayClick',
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: true,
|
|
42
|
+
description: '오버레이 클릭 시 종료 여부',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
cdnDefaults: {
|
|
46
|
+
useAnimation: true,
|
|
47
|
+
hideSkip: true,
|
|
48
|
+
hideStepCount: true,
|
|
49
|
+
exitOnOverlayClick: true,
|
|
50
|
+
},
|
|
51
|
+
methods: ['goToStep(index)', 'previousStep(e)', 'nextStep(e)', 'onChange(callback)', 'onExit(callback)', 'exit(e)'],
|
|
52
|
+
};
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
"onClose": "() => void"
|
|
10
10
|
},
|
|
11
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();"
|
|
12
|
+
"example": "const modal = new window.ncua.Modal({ size: 'sm', onClose: () => {} });\nmodal.setContent('<p>Content</p>');\nmodal.open();",
|
|
13
|
+
"cdnDefaults": {
|
|
14
|
+
"size": "md",
|
|
15
|
+
"closeOnBackdropClick": false,
|
|
16
|
+
"closeOnEsc": true
|
|
17
|
+
}
|
|
13
18
|
},
|
|
14
19
|
"SelectBox": {
|
|
15
20
|
"className": "SelectBox",
|
|
@@ -28,8 +33,10 @@
|
|
|
28
33
|
"methods": ["getValues()", "setValues(options[])", "getSelectedCount()"],
|
|
29
34
|
"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
35
|
"cdnDefaults": {
|
|
31
|
-
"size": "
|
|
32
|
-
"placeholder": "선택하세요"
|
|
36
|
+
"size": "md",
|
|
37
|
+
"placeholder": "선택하세요",
|
|
38
|
+
"disabled": false,
|
|
39
|
+
"multiple": false
|
|
33
40
|
}
|
|
34
41
|
},
|
|
35
42
|
"ComboBox": {
|
|
@@ -49,8 +56,11 @@
|
|
|
49
56
|
"methods": ["getValues()", "setValues(options[])", "clearInput()", "getSelectedCount()"],
|
|
50
57
|
"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
58
|
"cdnDefaults": {
|
|
52
|
-
"size": "
|
|
53
|
-
"placeholder": "
|
|
59
|
+
"size": "md",
|
|
60
|
+
"placeholder": "검색하고 선택하세요",
|
|
61
|
+
"disabled": false,
|
|
62
|
+
"multiple": false,
|
|
63
|
+
"showFooterButtons": false
|
|
54
64
|
}
|
|
55
65
|
},
|
|
56
66
|
"DatePicker": {
|
|
@@ -103,7 +113,9 @@
|
|
|
103
113
|
"min": 0,
|
|
104
114
|
"max": 100,
|
|
105
115
|
"step": 1,
|
|
106
|
-
"labelPosition": "top-floating"
|
|
116
|
+
"labelPosition": "top-floating",
|
|
117
|
+
"disabled": false,
|
|
118
|
+
"value": 0
|
|
107
119
|
}
|
|
108
120
|
},
|
|
109
121
|
"Tab": {
|
|
@@ -136,10 +148,11 @@
|
|
|
136
148
|
"example": "const tip = window.ncua.Tooltip.createShort('Help', 'Tooltip content');\ndocument.getElementById('container').appendChild(tip.getElement());",
|
|
137
149
|
"cdnDefaults": {
|
|
138
150
|
"type": "short",
|
|
139
|
-
"tooltipType": "
|
|
151
|
+
"tooltipType": "white",
|
|
140
152
|
"iconType": "stroke",
|
|
141
153
|
"position": "auto",
|
|
142
|
-
"size": "sm"
|
|
154
|
+
"size": "sm",
|
|
155
|
+
"hideArrow": false
|
|
143
156
|
}
|
|
144
157
|
},
|
|
145
158
|
"Notification": {
|
|
@@ -181,7 +194,9 @@
|
|
|
181
194
|
"methods": ["getElement()", "updateOptions(newOptions)", "toHTML()"],
|
|
182
195
|
"example": "const bar = new window.ncua.ProgressBar({ value: 65, label: 'right' });\ndocument.getElementById('container').appendChild(bar.getElement());",
|
|
183
196
|
"cdnDefaults": {
|
|
184
|
-
"label": "right"
|
|
197
|
+
"label": "right",
|
|
198
|
+
"showZeroLabel": false,
|
|
199
|
+
"valueToPercent": true
|
|
185
200
|
}
|
|
186
201
|
},
|
|
187
202
|
"Tag": {
|
|
@@ -200,7 +215,8 @@
|
|
|
200
215
|
"methods": ["getElement()", "setText(text)", "setCount(count)", "destroy()"],
|
|
201
216
|
"example": "const tag = new window.ncua.Tag({ text: 'Label', close: true, onButtonClick: () => tag.destroy() });\ndocument.getElementById('container').appendChild(tag.getElement());",
|
|
202
217
|
"cdnDefaults": {
|
|
203
|
-
"size": "sm"
|
|
218
|
+
"size": "sm",
|
|
219
|
+
"maxLength": 20
|
|
204
220
|
}
|
|
205
221
|
},
|
|
206
222
|
"FileInput": {
|
|
@@ -230,7 +246,9 @@
|
|
|
230
246
|
"methods": ["getElement()", "setDisabled(boolean)", "destroy()"],
|
|
231
247
|
"example": "const fi = new window.ncua.FileInput({\n container: 'file-container',\n onChange: (files) => console.log(files)\n});",
|
|
232
248
|
"cdnDefaults": {
|
|
233
|
-
"buttonLabel": "파일 찾기"
|
|
249
|
+
"buttonLabel": "파일 찾기",
|
|
250
|
+
"multiple": false,
|
|
251
|
+
"size": "xs"
|
|
234
252
|
}
|
|
235
253
|
},
|
|
236
254
|
"ImageFileInput": {
|
|
@@ -262,7 +280,10 @@
|
|
|
262
280
|
"options.className": "string"
|
|
263
281
|
},
|
|
264
282
|
"cdnDefaults": {
|
|
265
|
-
"buttonLabel": "파일 찾기"
|
|
283
|
+
"buttonLabel": "파일 찾기",
|
|
284
|
+
"imagePreviewTooltipLabel": "이미지 업로드",
|
|
285
|
+
"multiple": false,
|
|
286
|
+
"size": "sm"
|
|
266
287
|
},
|
|
267
288
|
"methods": ["getElement()", "getFiles()", "clearFiles()", "setDisabled(boolean)", "destroy()"],
|
|
268
289
|
"example": "const img = new window.ncua.ImageFileInput({\n container: 'image-container',\n maxFileCount: 5,\n onChange: (files) => console.log(files)\n});"
|
|
@@ -278,6 +299,19 @@
|
|
|
278
299
|
"options.className": "string"
|
|
279
300
|
},
|
|
280
301
|
"methods": ["getElement()", "updateColor(color)", "updateSize(size)", "destroy()"],
|
|
281
|
-
"example": "const icon = new window.ncua.FeaturedIcon({ svgString: '<svg>...</svg>', color: 'success', size: 'md' });\ndocument.getElementById('container').appendChild(icon.getElement());"
|
|
302
|
+
"example": "const icon = new window.ncua.FeaturedIcon({ svgString: '<svg>...</svg>', color: 'success', size: 'md' });\ndocument.getElementById('container').appendChild(icon.getElement());",
|
|
303
|
+
"cdnDefaults": {
|
|
304
|
+
"theme": "light-circle",
|
|
305
|
+
"color": "neutral",
|
|
306
|
+
"size": "sm"
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
"Table": {
|
|
310
|
+
"cdnDefaults": {
|
|
311
|
+
"type": "horizontal",
|
|
312
|
+
"hoverable": true,
|
|
313
|
+
"selectable": false,
|
|
314
|
+
"fixedHeader": false
|
|
315
|
+
}
|
|
282
316
|
}
|
|
283
317
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type McpToolResponse } from '../../utils/response.js';
|
|
11
11
|
/** `normalizeName` 적용된 컴포넌트 이름이 editor alias 인지 검사 */
|
|
12
|
-
|
|
12
|
+
declare const isEditorAlias: (normalizedName: string) => boolean;
|
|
13
13
|
/**
|
|
14
14
|
* editor 응답 조립 — ui-admin 흐름 우회.
|
|
15
15
|
* 5-9 helper(`mergeCdnDefaults` / `stripFunctionProps`) import 재사용 (코드 중복 0).
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
declare const buildEditorResponse: (props: Record<string, unknown>, instanceId: string) => McpToolResponse;
|
|
18
|
+
export { buildEditorResponse, isEditorAlias };
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 호출 진입점: `renderToHtml` 함수 시작부 (Task 1 결정).
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.isEditorAlias = exports.buildEditorResponse = void 0;
|
|
13
13
|
const editor_js_1 = require("../../definitions/external/editor.js");
|
|
14
14
|
const response_js_1 = require("../../utils/response.js");
|
|
15
15
|
const renderToHtml_js_1 = require("../renderToHtml.js");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 6.1: step-guide 외부 분기 모듈
|
|
3
|
+
*
|
|
4
|
+
* vanilla JS 함수 기반 가이드 투어(`step-guide`)는 ui-admin 외부 패키지이며
|
|
5
|
+
* 5-10 editor와 동일 패턴으로 ui-admin 흐름을 우회하고 본 모듈에서 응답을 직접 조립한다.
|
|
6
|
+
* 핵심 차이 — step-guide는 함수 호출(`window.stepGuide({el, options})`) 형식이라
|
|
7
|
+
* 5-10 patternD(생성자 호출 `new window.NcdsEditor(querySelector)`)와 다른 init 스니펫 패턴 신설.
|
|
8
|
+
*
|
|
9
|
+
* 호출 진입점: `renderToHtml` 함수 시작부 (editor 분기 다음).
|
|
10
|
+
*/
|
|
11
|
+
import { type McpToolResponse } from '../../utils/response.js';
|
|
12
|
+
/** `normalizeName` 적용된 컴포넌트 이름이 step-guide alias 인지 검사 */
|
|
13
|
+
declare const isStepGuideAlias: (normalizedName: string) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* step-guide 응답 조립 — ui-admin 흐름 우회.
|
|
16
|
+
* 5-9 helper(`mergeCdnDefaults` / `stripFunctionProps`) import 재사용 (코드 중복 0).
|
|
17
|
+
*/
|
|
18
|
+
declare const buildStepGuideResponse: (props: Record<string, unknown>, _instanceId: string) => McpToolResponse;
|
|
19
|
+
export { buildStepGuideResponse, isStepGuideAlias };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Story 6.1: step-guide 외부 분기 모듈
|
|
4
|
+
*
|
|
5
|
+
* vanilla JS 함수 기반 가이드 투어(`step-guide`)는 ui-admin 외부 패키지이며
|
|
6
|
+
* 5-10 editor와 동일 패턴으로 ui-admin 흐름을 우회하고 본 모듈에서 응답을 직접 조립한다.
|
|
7
|
+
* 핵심 차이 — step-guide는 함수 호출(`window.stepGuide({el, options})`) 형식이라
|
|
8
|
+
* 5-10 patternD(생성자 호출 `new window.NcdsEditor(querySelector)`)와 다른 init 스니펫 패턴 신설.
|
|
9
|
+
*
|
|
10
|
+
* 호출 진입점: `renderToHtml` 함수 시작부 (editor 분기 다음).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isStepGuideAlias = exports.buildStepGuideResponse = void 0;
|
|
14
|
+
const step_guide_js_1 = require("../../definitions/external/step-guide.js");
|
|
15
|
+
const response_js_1 = require("../../utils/response.js");
|
|
16
|
+
const renderToHtml_js_1 = require("../renderToHtml.js");
|
|
17
|
+
/** step-guide 컴포넌트로 매칭할 alias 집합 (render_to_html 진입점용, 보수적 4 영문) */
|
|
18
|
+
const STEP_GUIDE_ALIASES = new Set(['step-guide', 'stepguide', 'ncdsstepguide', 'step_guide']);
|
|
19
|
+
/** `normalizeName` 적용된 컴포넌트 이름이 step-guide alias 인지 검사 */
|
|
20
|
+
const isStepGuideAlias = (normalizedName) => STEP_GUIDE_ALIASES.has(normalizedName);
|
|
21
|
+
exports.isStepGuideAlias = isStepGuideAlias;
|
|
22
|
+
/**
|
|
23
|
+
* init 스니펫 — 함수 호출 형식.
|
|
24
|
+
* 5-10 patternD(생성자 호출 `new window.NcdsEditor(querySelector)`)와 다른 새 패턴.
|
|
25
|
+
* - 컨테이너 div 없음 — step-guide는 document.body 에 overlay를 그리는 도구라 격리 div 의미 없음
|
|
26
|
+
* - `el: document.body` — step-guide의 _fetchSteps 가 _targetElement 안에서만 step.element selector를 검색하므로
|
|
27
|
+
* 사용자가 step.element에 외부 카드 selector를 명시할 수 있도록 document.body를 target으로 지정
|
|
28
|
+
* - 함수 호출: `window.stepGuide({ el: document.body, options: ... })`
|
|
29
|
+
* - JSON.stringify로 options 직렬화 (data-* 미사용)
|
|
30
|
+
*/
|
|
31
|
+
const buildInitSnippet = (options) => {
|
|
32
|
+
const optionsJson = JSON.stringify(options);
|
|
33
|
+
return [
|
|
34
|
+
'<script>',
|
|
35
|
+
" document.addEventListener('DOMContentLoaded', function () {",
|
|
36
|
+
` window.stepGuide({ el: document.body, options: ${optionsJson} });`,
|
|
37
|
+
' });',
|
|
38
|
+
'</script>',
|
|
39
|
+
].join('\n');
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* step-guide 응답 조립 — ui-admin 흐름 우회.
|
|
43
|
+
* 5-9 helper(`mergeCdnDefaults` / `stripFunctionProps`) import 재사용 (코드 중복 0).
|
|
44
|
+
*/
|
|
45
|
+
const buildStepGuideResponse = (props, _instanceId // 격리 div 제거로 인해 unused — 외부 분기 시그니처 일관성 위해 인자는 유지
|
|
46
|
+
) => {
|
|
47
|
+
// 5-9: 함수 prop 사전 제거 + cdnDefaults deep merge (사용자 props 우선)
|
|
48
|
+
const cleanedProps = (0, renderToHtml_js_1.stripFunctionProps)(props);
|
|
49
|
+
const { merged, defaultsApplied } = (0, renderToHtml_js_1.mergeCdnDefaults)('step-guide', step_guide_js_1.stepGuideDefinition.cdnDefaults, cleanedProps);
|
|
50
|
+
const html = buildInitSnippet(merged);
|
|
51
|
+
// cdnDefaults 적용된 키 → defaultsUsed 응답
|
|
52
|
+
const defaultsUsed = Object.fromEntries(defaultsApplied.map((key) => [
|
|
53
|
+
key,
|
|
54
|
+
step_guide_js_1.stepGuideDefinition.cdnDefaults[key],
|
|
55
|
+
]));
|
|
56
|
+
return (0, response_js_1.successResponse)({
|
|
57
|
+
html,
|
|
58
|
+
component: step_guide_js_1.stepGuideDefinition.name,
|
|
59
|
+
exportName: step_guide_js_1.stepGuideDefinition.exportName,
|
|
60
|
+
importPath: 'step-guide',
|
|
61
|
+
appliedProps: merged,
|
|
62
|
+
...(Object.keys(defaultsUsed).length > 0 && { defaultsUsed }),
|
|
63
|
+
js: {
|
|
64
|
+
required: true,
|
|
65
|
+
description: '이 컴포넌트는 인터랙션을 위해 CDN JS가 필요합니다',
|
|
66
|
+
api: {
|
|
67
|
+
exportName: step_guide_js_1.stepGuideDefinition.exportName,
|
|
68
|
+
methods: step_guide_js_1.stepGuideDefinition.methods,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
cdn: step_guide_js_1.stepGuideDefinition.cdn,
|
|
72
|
+
dataVersion: { '@ncds/step-guide': step_guide_js_1.stepGuideDefinition.cdn.version },
|
|
73
|
+
react: {
|
|
74
|
+
notes: 'React 환경: useEffect 내 window.stepGuide({el, options}) 호출 + cleanup에서 instance.exit() 호출. ' +
|
|
75
|
+
'StrictMode 이중 마운트 회피 — cleanup 멱등성 보장 필요 (exit()가 이미 호출된 후 다시 호출되어도 안전).',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
exports.buildStepGuideResponse = buildStepGuideResponse;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getComponentProps = void 0;
|
|
4
2
|
/**
|
|
5
3
|
* get_component_props tool — 컴포넌트 props 스펙 반환 (순수 함수)
|
|
6
4
|
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getComponentProps = void 0;
|
|
7
7
|
const dataLoader_js_1 = require("../utils/dataLoader.js");
|
|
8
8
|
const response_js_1 = require("../utils/response.js");
|
|
9
9
|
/** get_component_props tool — 컴포넌트의 props 스펙을 JSON으로 반환 */
|
package/bin/tools/ping.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ping tool — NCUA MCP 서버 health check + capabilities/rules 제공 (순수 함수)
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { Capability, ComponentData } from '../types.js';
|
|
5
5
|
import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
|
|
6
6
|
import { type McpToolResponse } from '../utils/response.js';
|
|
7
7
|
/** ping tool 파라미터 — server.ts에서 모든 데이터를 소유하고 전달한다 */
|
|
@@ -15,6 +15,7 @@ exports.renderToHtml = exports.mergeCdnDefaults = exports.stripFunctionProps = v
|
|
|
15
15
|
const lodash_1 = require("lodash");
|
|
16
16
|
const response_js_1 = require("../utils/response.js");
|
|
17
17
|
const editor_js_1 = require("./external/editor.js");
|
|
18
|
+
const step_guide_js_1 = require("./external/step-guide.js");
|
|
18
19
|
// ── 상수 ──────────────────────────────────────────────────────────
|
|
19
20
|
/** React 특수 props 차단 — injection 방지 */
|
|
20
21
|
const BLOCKED_PROPS = new Set(['dangerouslySetInnerHTML', 'ref', '__self', '__source']);
|
|
@@ -657,6 +658,10 @@ const renderToHtml = (params) => {
|
|
|
657
658
|
if ((0, editor_js_1.isEditorAlias)(normalized)) {
|
|
658
659
|
return (0, editor_js_1.buildEditorResponse)(props ?? {}, instanceId);
|
|
659
660
|
}
|
|
661
|
+
// Story 6.1: step-guide alias 검출 시 ui-admin 흐름 우회 — 외부 분기 (함수 호출 형식)
|
|
662
|
+
if ((0, step_guide_js_1.isStepGuideAlias)(normalized)) {
|
|
663
|
+
return (0, step_guide_js_1.buildStepGuideResponse)(props ?? {}, instanceId);
|
|
664
|
+
}
|
|
660
665
|
const componentData = componentMap.get(normalized);
|
|
661
666
|
if (!componentData)
|
|
662
667
|
return (0, response_js_1.componentNotFoundResponse)(normalized);
|
|
@@ -709,7 +714,10 @@ const renderToHtml = (params) => {
|
|
|
709
714
|
const html = cdnInitScript !== null
|
|
710
715
|
? cdnInitScript
|
|
711
716
|
: `<!-- ncua:${normalized} start -->\n${reactRuntime.renderToStaticMarkup(reactRuntime.createElement(Component, resolvedProps))}\n<!-- ncua:${normalized} end -->`;
|
|
712
|
-
|
|
717
|
+
// Story 5.1-8: jsRequired 컴포넌트는 React data 의 default 무시 (vanilla 정의에 없는 React-only default 누수 차단).
|
|
718
|
+
// React 컴포넌트는 기존 동작 유지.
|
|
719
|
+
const isJsRequired = componentData.jsRequired === true;
|
|
720
|
+
const hasDefaults = !isJsRequired && componentData.props ? calcDefaultsUsed(componentData.props, corrected) : {};
|
|
713
721
|
// Story 5.9: cdnDefaults 적용된 키도 defaultsUsed 에 노출 (에이전트 학습용)
|
|
714
722
|
const cdnDefaultsApplied = isCdnInput && jsApi?.cdnDefaults
|
|
715
723
|
? Object.fromEntries(cdnMergeResult.defaultsApplied.map((key) => [key, jsApi.cdnDefaults?.[key]]))
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_component tool — name/description/aliases 퍼지 키워드 검색 (순수 함수)
|
|
3
|
+
*
|
|
4
|
+
* Calculator: searchComponent — 입력(componentMap, query) → 출력(매칭 결과, 점수순 정렬)
|
|
5
|
+
*/
|
|
1
6
|
import type { ComponentData } from '../types.js';
|
|
2
7
|
import { type McpToolResponse } from '../utils/response.js';
|
|
3
8
|
/** search_component tool — name/description/aliases 퍼지 검색 후 점수순 반환 */
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.searchComponent = void 0;
|
|
4
2
|
/**
|
|
5
3
|
* search_component tool — name/description/aliases 퍼지 키워드 검색 (순수 함수)
|
|
6
4
|
*
|
|
7
5
|
* Calculator: searchComponent — 입력(componentMap, query) → 출력(매칭 결과, 점수순 정렬)
|
|
8
6
|
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.searchComponent = void 0;
|
|
9
9
|
const dataLoader_js_1 = require("../utils/dataLoader.js");
|
|
10
|
-
const response_js_1 = require("../utils/response.js");
|
|
11
10
|
const fuzzyMatch_js_1 = require("../utils/fuzzyMatch.js");
|
|
11
|
+
const response_js_1 = require("../utils/response.js");
|
|
12
12
|
/** search_component tool — name/description/aliases 퍼지 검색 후 점수순 반환 */
|
|
13
13
|
const searchComponent = (componentMap, query) => {
|
|
14
14
|
if (!query.trim())
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ComplianceRulesData, ComponentData, TokenData } from '../types.js';
|
|
2
2
|
import type { CdnMeta } from '../utils/dataLoader.js';
|
|
3
3
|
import { type McpToolResponse } from '../utils/response.js';
|
|
4
|
-
export type { ValidationError } from '../utils/bemValidator.js';
|
|
5
|
-
export { buildRootClassMap } from '../utils/bemValidator.js';
|
|
6
|
-
export { buildTokenValueMap } from '../utils/tokenValidator.js';
|
|
7
4
|
/** validate_html tool 파라미터 */
|
|
8
|
-
|
|
5
|
+
interface ValidateHtmlParams {
|
|
9
6
|
componentMap: Map<string, ComponentData>;
|
|
10
7
|
rootClassMap: Map<string, string>;
|
|
11
8
|
html: string;
|
|
@@ -15,4 +12,9 @@ export interface ValidateHtmlParams {
|
|
|
15
12
|
tokenValueMap?: Map<string, string[]> | null;
|
|
16
13
|
}
|
|
17
14
|
/** validate_html tool — HTML의 NCUA BEM 클래스 정합성 + 디자인 시스템 준수도 검증 */
|
|
18
|
-
|
|
15
|
+
declare const validateHtml: (params: ValidateHtmlParams) => McpToolResponse;
|
|
16
|
+
export { validateHtml };
|
|
17
|
+
export type { ValidateHtmlParams };
|
|
18
|
+
export type { ValidationError } from '../utils/bemValidator.js';
|
|
19
|
+
export { buildRootClassMap } from '../utils/bemValidator.js';
|
|
20
|
+
export { buildTokenValueMap } from '../utils/tokenValidator.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildTokenValueMap = exports.buildRootClassMap = exports.validateHtml = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* validate_html tool — NCUA HTML BEM 검증 + 디자인 시스템 준수도 검증 진입점
|
|
6
6
|
*
|
|
@@ -9,13 +9,9 @@ exports.validateHtml = exports.buildTokenValueMap = exports.buildRootClassMap =
|
|
|
9
9
|
* 이 파일은 오케스트레이션만 담당한다.
|
|
10
10
|
*/
|
|
11
11
|
const node_html_parser_1 = require("node-html-parser");
|
|
12
|
-
const response_js_1 = require("../utils/response.js");
|
|
13
12
|
const bemValidator_js_1 = require("../utils/bemValidator.js");
|
|
14
13
|
const compliance_js_1 = require("../utils/compliance.js");
|
|
15
|
-
|
|
16
|
-
Object.defineProperty(exports, "buildRootClassMap", { enumerable: true, get: function () { return bemValidator_js_2.buildRootClassMap; } });
|
|
17
|
-
var tokenValidator_js_1 = require("../utils/tokenValidator.js");
|
|
18
|
-
Object.defineProperty(exports, "buildTokenValueMap", { enumerable: true, get: function () { return tokenValidator_js_1.buildTokenValueMap; } });
|
|
14
|
+
const response_js_1 = require("../utils/response.js");
|
|
19
15
|
/** validate_html tool — HTML의 NCUA BEM 클래스 정합성 + 디자인 시스템 준수도 검증 */
|
|
20
16
|
const validateHtml = (params) => {
|
|
21
17
|
const { componentMap, rootClassMap, html, cdnMeta, tokenData, complianceRules, tokenValueMap } = params;
|
|
@@ -83,3 +79,7 @@ const buildResponse = (params) => {
|
|
|
83
79
|
...(compliance && { compliance }),
|
|
84
80
|
});
|
|
85
81
|
};
|
|
82
|
+
var bemValidator_js_2 = require("../utils/bemValidator.js");
|
|
83
|
+
Object.defineProperty(exports, "buildRootClassMap", { enumerable: true, get: function () { return bemValidator_js_2.buildRootClassMap; } });
|
|
84
|
+
var tokenValidator_js_1 = require("../utils/tokenValidator.js");
|
|
85
|
+
Object.defineProperty(exports, "buildTokenValueMap", { enumerable: true, get: function () { return tokenValidator_js_1.buildTokenValueMap; } });
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { parse } from 'node-html-parser';
|
|
7
7
|
import type { ComponentData } from '../types.js';
|
|
8
8
|
import type { CdnMeta } from './dataLoader.js';
|
|
9
|
-
|
|
9
|
+
interface ValidationError {
|
|
10
10
|
type: 'invalid_modifier' | 'invalid_element' | 'unknown_component' | 'missing_root_class' | 'mixed_namespace' | 'missing_cdn';
|
|
11
11
|
class: string;
|
|
12
12
|
component: string;
|
|
@@ -14,13 +14,13 @@ export interface ValidationError {
|
|
|
14
14
|
suggestion: string;
|
|
15
15
|
}
|
|
16
16
|
/** 엘리먼트에서 class 목록을 파싱 */
|
|
17
|
-
|
|
17
|
+
declare const getClassList: (el: {
|
|
18
18
|
getAttribute: (name: string) => string | undefined;
|
|
19
19
|
}) => string[];
|
|
20
20
|
/** root class → component name 매핑 빌드 — server.ts에서 1회 호출 */
|
|
21
|
-
|
|
21
|
+
declare const buildRootClassMap: (componentMap: Map<string, ComponentData>) => Map<string, string>;
|
|
22
22
|
/** 엘리먼트별 BEM 클래스 검증 → errors + warnings + invalidClasses 수집 */
|
|
23
|
-
|
|
23
|
+
declare const collectBemErrors: (params: {
|
|
24
24
|
elements: ReturnType<ReturnType<typeof parse>["querySelectorAll"]>;
|
|
25
25
|
rootClassMap: Map<string, string>;
|
|
26
26
|
componentMap: Map<string, ComponentData>;
|
|
@@ -31,6 +31,8 @@ export declare const collectBemErrors: (params: {
|
|
|
31
31
|
hasNcuaClasses: boolean;
|
|
32
32
|
};
|
|
33
33
|
/** 잘못된 클래스를 제거하여 fixed_html 생성 */
|
|
34
|
-
|
|
34
|
+
declare const buildFixedHtml: (root: ReturnType<typeof parse>, invalidClassesSet: Set<string>) => string;
|
|
35
35
|
/** CDN CSS/JS 링크 포함 여부 검증 */
|
|
36
|
-
|
|
36
|
+
declare const checkCdnInclusion: (html: string, cdnMeta: CdnMeta) => ValidationError[];
|
|
37
|
+
export { buildFixedHtml, buildRootClassMap, checkCdnInclusion, collectBemErrors, getClassList };
|
|
38
|
+
export type { ValidationError };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getClassList = exports.collectBemErrors = exports.checkCdnInclusion = exports.buildRootClassMap = exports.buildFixedHtml = void 0;
|
|
4
4
|
// ── 상수 ─────────────────────────────────────────────────────────────────
|
|
5
5
|
const MAX_SIMILAR_SUGGESTIONS = 3;
|
|
6
6
|
// ── 순수 헬퍼 ────────────────────────────────────────────────────────────
|
|
@@ -102,7 +102,7 @@ const collectBemErrors = (params) => {
|
|
|
102
102
|
const validSetCache = new Map();
|
|
103
103
|
let foundNcua = false;
|
|
104
104
|
for (const el of elements) {
|
|
105
|
-
const classes =
|
|
105
|
+
const classes = getClassList(el);
|
|
106
106
|
const ncuaClasses = classes.filter((c) => c.startsWith('ncua-'));
|
|
107
107
|
if (ncuaClasses.length === 0)
|
|
108
108
|
continue;
|
|
@@ -119,11 +119,20 @@ const collectBemErrors = (params) => {
|
|
|
119
119
|
reportUnknownClasses(ncuaClasses, errors, invalidClassesSet);
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
|
+
// biome-ignore lint/style/noNonNullAssertion: 직전 가드(rootClassMap.has)로 존재 보장
|
|
122
123
|
const componentName = rootClassMap.get(rootClass);
|
|
123
124
|
const component = componentMap.get(componentName);
|
|
125
|
+
if (!component) {
|
|
126
|
+
// rootClassMap 에는 있는데 componentMap 에 없는 비정상 상태 (호출자가 두 Map 을 분리 빌드한 경우).
|
|
127
|
+
// graceful skip — rootClass 가 알 수 없는 컴포넌트인 경로와 동일 처리.
|
|
128
|
+
reportUnknownClasses(ncuaClasses, errors, invalidClassesSet);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
124
131
|
if (!validSetCache.has(componentName))
|
|
125
132
|
validSetCache.set(componentName, new Set(component.bemClasses));
|
|
126
|
-
validateBemClasses(ncuaClasses,
|
|
133
|
+
validateBemClasses(ncuaClasses,
|
|
134
|
+
// biome-ignore lint/style/noNonNullAssertion: 직전 has/set 보장
|
|
135
|
+
validSetCache.get(componentName), componentName, component.bemClasses, errors, invalidClassesSet);
|
|
127
136
|
}
|
|
128
137
|
return { errors, warnings, invalidClassesSet, hasNcuaClasses: foundNcua };
|
|
129
138
|
};
|
|
@@ -133,6 +142,7 @@ const collectMixedNamespaceWarning = (classes, ncuaClasses, rootClass, rootClass
|
|
|
133
142
|
const customClasses = classes.filter((c) => !c.startsWith('ncua-'));
|
|
134
143
|
if (customClasses.length === 0)
|
|
135
144
|
return;
|
|
145
|
+
// biome-ignore lint/style/noNonNullAssertion: 삼항 가드로 rootClassMap.has(rootClass) 보장
|
|
136
146
|
const compName = rootClass && rootClassMap.has(rootClass) ? rootClassMap.get(rootClass) : 'unknown';
|
|
137
147
|
warnings.push({
|
|
138
148
|
type: 'mixed_namespace',
|
|
@@ -151,7 +161,9 @@ const collectMissingRootClassError = (ncuaClasses, rootClass, rootClassMap, erro
|
|
|
151
161
|
return;
|
|
152
162
|
errors.push({
|
|
153
163
|
type: 'missing_root_class',
|
|
164
|
+
// biome-ignore lint/style/noNonNullAssertion: isModifierWithoutRoot 가드에 rootClass != null 포함
|
|
154
165
|
class: rootClass,
|
|
166
|
+
// biome-ignore lint/style/noNonNullAssertion: 위와 동일 가드 (rootClass != null + has)
|
|
155
167
|
component: rootClassMap.get(rootClass),
|
|
156
168
|
message: `Root class '${rootClass}' is missing. Modifiers/elements require their root class.`,
|
|
157
169
|
suggestion: `Add '${rootClass}' class to the same element.`,
|
|
@@ -160,7 +172,7 @@ const collectMissingRootClassError = (ncuaClasses, rootClass, rootClassMap, erro
|
|
|
160
172
|
/** 잘못된 클래스를 제거하여 fixed_html 생성 */
|
|
161
173
|
const buildFixedHtml = (root, invalidClassesSet) => {
|
|
162
174
|
for (const el of root.querySelectorAll('[class]')) {
|
|
163
|
-
const classes =
|
|
175
|
+
const classes = getClassList(el);
|
|
164
176
|
const filtered = classes.filter((c) => !invalidClassesSet.has(c));
|
|
165
177
|
if (filtered.length > 0) {
|
|
166
178
|
el.setAttribute('class', filtered.join(' '));
|