@nine-lab/nine-fab 0.1.7 → 0.1.8
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/dist/nine-fab.js +117 -120
- package/dist/nine-fab.js.map +1 -1
- package/dist/nine-fab.umd.js +1 -1
- package/dist/nine-fab.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/NineChat.js +84 -138
- package/src/services/NineFabService.js +55 -0
package/dist/nine-fab.js
CHANGED
|
@@ -6,158 +6,155 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
6
6
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
7
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
8
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
-
var
|
|
9
|
+
var _service, _routes, _NineChat_instances, initInteractions_fn, initActions_fn, render_fn;
|
|
10
10
|
import { trace, nine } from "@nine-lab/nine-util";
|
|
11
|
+
class NineFabService {
|
|
12
|
+
constructor(connectorUrl) {
|
|
13
|
+
this.connectorUrl = connectorUrl;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* AI 소스 생성 실행
|
|
17
|
+
* @param {string} command - 사용자 입력 명령
|
|
18
|
+
* @param {Array} targets - 체크된 생성 대상 (MyBatis, Service 등)
|
|
19
|
+
* @param {Array} currentRoutes - 현재 프로젝트 경로 정보
|
|
20
|
+
*/
|
|
21
|
+
async generate(command, targets, currentRoutes) {
|
|
22
|
+
try {
|
|
23
|
+
trace.log(`🚀 Fab 공정 시작: "${command}" [대상: ${targets.join(", ")}]`);
|
|
24
|
+
const response = await fetch(`${this.connectorUrl}/api/fab/generate`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
command,
|
|
29
|
+
targets,
|
|
30
|
+
projectType: "spring-react",
|
|
31
|
+
currentRoutes
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
const result = await response.json();
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
throw new Error(result.error || "소스 생성 중 서버 오류가 발생했습니다.");
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
trace.error("❌ Fab Service Error:", error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 초기 경로 정보 로드
|
|
46
|
+
*/
|
|
47
|
+
async fetchRoutes(routeUrl) {
|
|
48
|
+
if (!routeUrl) return [];
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(routeUrl);
|
|
51
|
+
return await res.json();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
trace.error("Route load fail", err);
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
11
58
|
class NineChat extends HTMLElement {
|
|
12
59
|
constructor() {
|
|
13
60
|
super();
|
|
14
61
|
__privateAdd(this, _NineChat_instances);
|
|
15
|
-
__privateAdd(this,
|
|
16
|
-
__privateAdd(this, _tipPopup, null);
|
|
62
|
+
__privateAdd(this, _service, null);
|
|
17
63
|
__privateAdd(this, _routes, []);
|
|
18
|
-
__privateAdd(this, _toggleCollapseHandler, () => {
|
|
19
|
-
this.classList.toggle("collapse");
|
|
20
|
-
});
|
|
21
|
-
__privateAdd(this, _menuClickHandler, (e) => {
|
|
22
|
-
this.shadowRoot.querySelectorAll(".menu-icon").forEach((el) => el.classList.remove("active"));
|
|
23
|
-
const clickedIcon = e.target.closest(".menu-icon");
|
|
24
|
-
if (clickedIcon) clickedIcon.classList.add("active");
|
|
25
|
-
if (this.$settings) {
|
|
26
|
-
this.$settings.classList.toggle("expand", !!e.target.closest(".menu-setting"));
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
64
|
this.attachShadow({ mode: "open" });
|
|
30
65
|
}
|
|
31
66
|
async connectedCallback() {
|
|
32
|
-
|
|
67
|
+
const connectorUrl = this.getAttribute("connector-url") || "http://localhost:3002";
|
|
68
|
+
__privateSet(this, _service, new NineFabService(connectorUrl));
|
|
33
69
|
__privateMethod(this, _NineChat_instances, render_fn).call(this);
|
|
34
|
-
|
|
70
|
+
__privateMethod(this, _NineChat_instances, initInteractions_fn).call(this);
|
|
71
|
+
__privateMethod(this, _NineChat_instances, initActions_fn).call(this);
|
|
72
|
+
__privateSet(this, _routes, await __privateGet(this, _service).fetchRoutes(this.getAttribute("route-url")));
|
|
35
73
|
}
|
|
36
74
|
}
|
|
37
|
-
|
|
38
|
-
_tipPopup = new WeakMap();
|
|
75
|
+
_service = new WeakMap();
|
|
39
76
|
_routes = new WeakMap();
|
|
40
77
|
_NineChat_instances = new WeakSet();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.shadowRoot.
|
|
45
|
-
this.
|
|
46
|
-
this.shadowRoot.
|
|
47
|
-
this
|
|
78
|
+
// --- [그룹 1: Interaction] 화면 토글 및 메뉴 클릭 ---
|
|
79
|
+
initInteractions_fn = function() {
|
|
80
|
+
const $settings = this.shadowRoot.querySelector("nine-ai-settings");
|
|
81
|
+
const $menuIcons = this.shadowRoot.querySelectorAll(".menu-icon");
|
|
82
|
+
const toggleUI = () => this.classList.toggle("collapse");
|
|
83
|
+
this.shadowRoot.querySelector(".expand-icon").addEventListener("click", toggleUI);
|
|
84
|
+
this.shadowRoot.querySelector(".collapse-icon").addEventListener("click", toggleUI);
|
|
85
|
+
$menuIcons.forEach((el) => {
|
|
86
|
+
el.addEventListener("click", (e) => {
|
|
87
|
+
$menuIcons.forEach((icon) => icon.classList.remove("active"));
|
|
88
|
+
const clicked = e.target.closest(".menu-icon");
|
|
89
|
+
clicked.classList.add("active");
|
|
90
|
+
$settings.classList.toggle("expand", clicked.classList.contains("menu-setting"));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
// --- [그룹 2: Action] 엔터키 입력 시 서비스 호출 ---
|
|
95
|
+
initActions_fn = function() {
|
|
96
|
+
const $textarea = this.shadowRoot.querySelector("#q");
|
|
97
|
+
const $status = this.shadowRoot.querySelector("#status-tag");
|
|
98
|
+
$textarea.addEventListener("keypress", async (e) => {
|
|
48
99
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
49
100
|
e.preventDefault();
|
|
50
101
|
const command = e.target.value.trim();
|
|
51
|
-
if (command)
|
|
52
|
-
|
|
53
|
-
|
|
102
|
+
if (!command) return;
|
|
103
|
+
$status.textContent = "⚙️ 공정 분석 중...";
|
|
104
|
+
e.target.value = "";
|
|
105
|
+
const targets = Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked')).map((el) => el.value);
|
|
106
|
+
try {
|
|
107
|
+
const result = await __privateGet(this, _service).generate(command, targets, __privateGet(this, _routes));
|
|
108
|
+
$status.textContent = "✅ 완료";
|
|
109
|
+
nine.alert("소스 생성 성공").rgb();
|
|
110
|
+
this.dispatchEvent(new CustomEvent("nine-fab-completed", { detail: result, bubbles: true }));
|
|
111
|
+
} catch (err) {
|
|
112
|
+
$status.textContent = "❌ 실패";
|
|
113
|
+
nine.alert(err.message).rgb().shake();
|
|
54
114
|
}
|
|
55
115
|
}
|
|
56
116
|
});
|
|
57
|
-
const routeUrl = this.getAttribute("route-url");
|
|
58
|
-
if (routeUrl) {
|
|
59
|
-
try {
|
|
60
|
-
const res = await fetch(routeUrl);
|
|
61
|
-
__privateSet(this, _routes, await res.json());
|
|
62
|
-
trace.log("✅ 현재 프로젝트 경로 분석 완료", __privateGet(this, _routes));
|
|
63
|
-
} catch (err) {
|
|
64
|
-
trace.error("❌ 경로 로드 실패:", err.message);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
117
|
};
|
|
68
118
|
render_fn = function() {
|
|
69
119
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
70
|
-
const
|
|
71
|
-
const customImport = customPath ? `@import "${customPath}";` : "";
|
|
72
|
-
const version = "0.1.6";
|
|
120
|
+
const customImport = this.getAttribute("css-path") ? `@import "${this.getAttribute("css-path")}";` : "";
|
|
73
121
|
this.shadowRoot.innerHTML = `
|
|
74
122
|
<style>
|
|
75
|
-
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${
|
|
123
|
+
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${"0.1.7"}/dist/css/nine-fab.css";
|
|
76
124
|
${customImport}
|
|
77
125
|
</style>
|
|
78
|
-
|
|
79
126
|
<div class="wrapper">
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<div class="logo"><span></span></div>
|
|
127
|
+
<nine-ai-settings></nine-ai-settings>
|
|
128
|
+
<div class="container">
|
|
129
|
+
<div class="head">
|
|
130
|
+
<div class="logo"><span></span></div>
|
|
85
131
|
<div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">Fab Engine Ready</div>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
<div class="menu">
|
|
111
|
-
<div class="collapse-icon"></div>
|
|
112
|
-
<div class="menu-icon menu-filter active"></div>
|
|
113
|
-
<div class="menu-icon menu-general"></div>
|
|
114
|
-
<div class="menu-icon menu-setting"></div>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
<div class="expand-icon"></div>
|
|
132
|
+
</div>
|
|
133
|
+
<nx-ai-chat></nx-ai-chat>
|
|
134
|
+
<div class="foot">
|
|
135
|
+
<div class="apply-src">
|
|
136
|
+
${["MyBatis", "Service", "Controller", "JavaScript"].map((t) => `
|
|
137
|
+
<div>
|
|
138
|
+
<input type="checkbox" id="${t.toLowerCase()}" name="gen_target" value="${t}" checked />
|
|
139
|
+
<label for="${t.toLowerCase()}">${t}</label>
|
|
140
|
+
</div>
|
|
141
|
+
`).join("")}
|
|
142
|
+
</div>
|
|
143
|
+
<textarea id="q" rows="4" placeholder="${placeholder}"></textarea>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="menu">
|
|
147
|
+
<div class="collapse-icon"></div>
|
|
148
|
+
<div class="menu-icon menu-filter active"></div>
|
|
149
|
+
<div class="menu-icon menu-general"></div>
|
|
150
|
+
<div class="menu-icon menu-setting"></div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="expand-icon"></div>
|
|
118
154
|
`;
|
|
119
155
|
};
|
|
120
|
-
handleFabCommand_fn = async function(command) {
|
|
121
|
-
const statusEl = this.shadowRoot.getElementById("status-tag");
|
|
122
|
-
const targets = Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked')).map((el) => el.value);
|
|
123
|
-
try {
|
|
124
|
-
statusEl.textContent = "⚙️ 공정 분석 중...";
|
|
125
|
-
trace.log(`🚀 Fab 명령 실행: "${command}" [대상: ${targets.join(", ")}]`);
|
|
126
|
-
const response = await fetch(`${__privateGet(this, _connectorUrl)}/api/fab/generate`, {
|
|
127
|
-
method: "POST",
|
|
128
|
-
headers: { "Content-Type": "application/json" },
|
|
129
|
-
body: JSON.stringify({
|
|
130
|
-
command,
|
|
131
|
-
targets,
|
|
132
|
-
// MyBatis, Service 등 선택된 항목 전송
|
|
133
|
-
projectType: "spring-react",
|
|
134
|
-
currentRoutes: __privateGet(this, _routes)
|
|
135
|
-
})
|
|
136
|
-
});
|
|
137
|
-
const result = await response.json();
|
|
138
|
-
if (!result.success) throw new Error(result.error || "생성 실패");
|
|
139
|
-
trace.log("✅ 소스 생성 완료", result.files);
|
|
140
|
-
statusEl.textContent = "✅ 파일 주입 완료";
|
|
141
|
-
nine.alert("소스가 성공적으로 생성되었습니다.").rgb();
|
|
142
|
-
this.dispatchEvent(new CustomEvent("nine-fab-completed", {
|
|
143
|
-
detail: result,
|
|
144
|
-
bubbles: true,
|
|
145
|
-
composed: true
|
|
146
|
-
}));
|
|
147
|
-
} catch (error) {
|
|
148
|
-
trace.error("Fab 공정 실패:", error);
|
|
149
|
-
statusEl.textContent = "❌ 에러 발생";
|
|
150
|
-
nine.alert(error.message).rgb().shake();
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
_toggleCollapseHandler = new WeakMap();
|
|
154
|
-
_menuClickHandler = new WeakMap();
|
|
155
|
-
trace.init("nine-fab", "#FF5722");
|
|
156
|
-
if (!customElements.get("nine-chat")) {
|
|
157
|
-
customElements.define("nine-chat", NineChat);
|
|
158
|
-
}
|
|
159
156
|
const NineFab = {
|
|
160
|
-
version: "0.1.
|
|
157
|
+
version: "0.1.7",
|
|
161
158
|
init: (config) => {
|
|
162
159
|
trace.log("🛠️ Nine-Fab Engine initialized", config);
|
|
163
160
|
}
|
package/dist/nine-fab.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nine-fab.js","sources":["../src/components/NineChat.js","../src/index.js"],"sourcesContent":["import { nine, trace } from '@nine-lab/nine-util';\n// import { TipPopup } from '@nine-lab/nine-ai'; // 필요시 주석 해제\n\nexport class NineChat extends HTMLElement {\n #connectorUrl = '';\n #tipPopup = null;\n #routes = [];\n\n static {\n trace.init(\"nine-fab\", \"#FF5722\");\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n async connectedCallback() {\n this.#connectorUrl = this.getAttribute('connector-url') || 'http://localhost:3002';\n this.#render();\n await this.#init();\n }\n\n async #init() {\n // UI 요소 참조\n this.$textarea = this.shadowRoot.querySelector('#q');\n this.$settings = this.shadowRoot.querySelector('nine-ai-settings');\n\n // 아이콘 이벤트 연결\n this.shadowRoot.querySelector(\".expand-icon\").addEventListener(\"click\", this.#toggleCollapseHandler);\n this.shadowRoot.querySelector(\".collapse-icon\").addEventListener(\"click\", this.#toggleCollapseHandler);\n this.shadowRoot.querySelectorAll(\".menu-icon\").forEach(el => el.addEventListener(\"click\", this.#menuClickHandler));\n\n // [핵심] 텍스트 영역 엔터 이벤트 (Command 전송)\n this.$textarea.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n const command = e.target.value.trim();\n if (command) {\n this.#handleFabCommand(command);\n e.target.value = '';\n }\n }\n });\n\n // 기존 경로 정보 로드\n const routeUrl = this.getAttribute('route-url');\n if (routeUrl) {\n try {\n const res = await fetch(routeUrl);\n this.#routes = await res.json();\n trace.log(\"✅ 현재 프로젝트 경로 분석 완료\", this.#routes);\n } catch (err) {\n trace.error(\"❌ 경로 로드 실패:\", err.message);\n }\n }\n }\n\n #render() {\n const placeholder = this.getAttribute(\"placeholder\") || \"나에게 무엇이든 물어봐...\";\n const customPath = this.getAttribute(\"css-path\");\n const customImport = customPath ? `@import \"${customPath}\";` : \"\";\n const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'latest';\n\n this.shadowRoot.innerHTML = `\n <style>\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${version}/dist/css/nine-fab.css\";\n ${customImport}\n </style>\n \n <div class=\"wrapper\">\n\t\t\t\t<nine-ai-settings></nine-ai-settings>\n\t\t\t\n\t\t\t\t<div class=\"container\">\n\t\t\t\t\t<div class=\"head\">\n\t\t\t\t\t\t<div class=\"logo\"><span></span></div>\n <div id=\"status-tag\" style=\"font-size:10px; color:#ff5722; padding:5px;\">Fab Engine Ready</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<nx-ai-chat></nx-ai-chat>\n\t\t\t\t\t<div class=\"foot\">\n\t\t\t\t\t\t<div class=\"apply-src\">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"mybatis\" name=\"gen_target\" value=\"MyBatis\" checked />\n\t\t\t\t\t\t\t <label for=\"mybatis\">MyBatis</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"service\" name=\"gen_target\" value=\"Service\" checked />\n\t\t\t\t\t\t\t <label for=\"service\">Service</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"controller\" name=\"gen_target\" value=\"Controller\" checked />\n\t\t\t\t\t\t\t <label for=\"controller\">Controller</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"javascript\" name=\"gen_target\" value=\"JavaScript\" checked />\n\t\t\t\t\t\t\t <label for=\"javascript\">JavaScript</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<textarea id=\"q\" rows=\"4\" placeholder=\"${placeholder}\"></textarea>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"menu\">\n\t\t\t\t\t<div class=\"collapse-icon\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-filter active\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-general\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-setting\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"expand-icon\"></div>\n `;\n }\n\n async #handleFabCommand(command) {\n const statusEl = this.shadowRoot.getElementById('status-tag');\n\n // 체크된 생성 대상 수집\n const targets = Array.from(this.shadowRoot.querySelectorAll('input[name=\"gen_target\"]:checked'))\n .map(el => el.value);\n\n try {\n statusEl.textContent = \"⚙️ 공정 분석 중...\";\n trace.log(`🚀 Fab 명령 실행: \"${command}\" [대상: ${targets.join(', ')}]`);\n\n const response = await fetch(`${this.#connectorUrl}/api/fab/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n command,\n targets, // MyBatis, Service 등 선택된 항목 전송\n projectType: 'spring-react',\n currentRoutes: this.#routes\n })\n });\n\n const result = await response.json();\n\n if (!result.success) throw new Error(result.error || \"생성 실패\");\n\n trace.log(\"✅ 소스 생성 완료\", result.files);\n statusEl.textContent = \"✅ 파일 주입 완료\";\n nine.alert(\"소스가 성공적으로 생성되었습니다.\").rgb();\n\n this.dispatchEvent(new CustomEvent('nine-fab-completed', {\n detail: result,\n bubbles: true,\n composed: true\n }));\n\n } catch (error) {\n trace.error(\"Fab 공정 실패:\", error);\n statusEl.textContent = \"❌ 에러 발생\";\n nine.alert(error.message).rgb().shake();\n }\n }\n\n #toggleCollapseHandler = () => {\n this.classList.toggle(\"collapse\");\n };\n\n #menuClickHandler = (e) => {\n this.shadowRoot.querySelectorAll(\".menu-icon\").forEach(el => el.classList.remove(\"active\"));\n const clickedIcon = e.target.closest(\".menu-icon\");\n if (clickedIcon) clickedIcon.classList.add(\"active\");\n\n // nx-ai-settings 토글 처리\n if (this.$settings) {\n this.$settings.classList.toggle(\"expand\", !!e.target.closest(\".menu-setting\"));\n }\n };\n}\n\nif (!customElements.get('nine-chat')) {\n customElements.define('nine-chat', NineChat);\n}","import { trace } from '@nine-lab/nine-util';\nimport { NineChat } from './components/NineChat.js';\n\n/**\n * Nine-Fab 엔진 메인 클래스\n */\nexport const NineFab = {\n version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.1.0',\n init: (config) => {\n trace.log(\"🛠️ Nine-Fab Engine initialized\", config);\n // 향후 커넥터 URL 전역 설정이나 인증 토큰 처리 로직 추가 가능\n }\n};\n\n// 기본 export 및 컴포넌트 export\nexport default NineFab;\nexport { NineChat };"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nine-fab.js","sources":["../src/services/NineFabService.js","../src/components/NineChat.js","../src/index.js"],"sourcesContent":["import { nine, trace } from '@nine-lab/nine-util';\n\nexport class NineFabService {\n constructor(connectorUrl) {\n this.connectorUrl = connectorUrl;\n }\n\n /**\n * AI 소스 생성 실행\n * @param {string} command - 사용자 입력 명령\n * @param {Array} targets - 체크된 생성 대상 (MyBatis, Service 등)\n * @param {Array} currentRoutes - 현재 프로젝트 경로 정보\n */\n async generate(command, targets, currentRoutes) {\n try {\n trace.log(`🚀 Fab 공정 시작: \"${command}\" [대상: ${targets.join(', ')}]`);\n\n const response = await fetch(`${this.connectorUrl}/api/fab/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n command,\n targets,\n projectType: 'spring-react',\n currentRoutes\n })\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || \"소스 생성 중 서버 오류가 발생했습니다.\");\n }\n\n return result;\n } catch (error) {\n trace.error(\"❌ Fab Service Error:\", error);\n throw error;\n }\n }\n\n /**\n * 초기 경로 정보 로드\n */\n async fetchRoutes(routeUrl) {\n if (!routeUrl) return [];\n try {\n const res = await fetch(routeUrl);\n return await res.json();\n } catch (err) {\n trace.error(\"Route load fail\", err);\n return [];\n }\n }\n}","import { nine, trace } from '@nine-lab/nine-util';\nimport { NineFabService } from '../services/NineFabService.js';\n\nexport class NineChat extends HTMLElement {\n #service = null;\n #routes = [];\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n async connectedCallback() {\n // 서비스 초기화\n const connectorUrl = this.getAttribute('connector-url') || 'http://localhost:3002';\n this.#service = new NineFabService(connectorUrl);\n\n this.#render();\n this.#initInteractions(); // UI 토글 로직\n this.#initActions(); // 엔터 시 실행 로직\n\n // 경로 데이터 로드\n this.#routes = await this.#service.fetchRoutes(this.getAttribute('route-url'));\n }\n\n // --- [그룹 1: Interaction] 화면 토글 및 메뉴 클릭 ---\n #initInteractions() {\n const $settings = this.shadowRoot.querySelector('nine-ai-settings');\n const $menuIcons = this.shadowRoot.querySelectorAll(\".menu-icon\");\n\n const toggleUI = () => this.classList.toggle(\"collapse\");\n this.shadowRoot.querySelector(\".expand-icon\").addEventListener(\"click\", toggleUI);\n this.shadowRoot.querySelector(\".collapse-icon\").addEventListener(\"click\", toggleUI);\n\n $menuIcons.forEach(el => {\n el.addEventListener(\"click\", (e) => {\n $menuIcons.forEach(icon => icon.classList.remove(\"active\"));\n const clicked = e.target.closest(\".menu-icon\");\n clicked.classList.add(\"active\");\n $settings.classList.toggle(\"expand\", clicked.classList.contains('menu-setting'));\n });\n });\n }\n\n // --- [그룹 2: Action] 엔터키 입력 시 서비스 호출 ---\n #initActions() {\n const $textarea = this.shadowRoot.querySelector('#q');\n const $status = this.shadowRoot.querySelector('#status-tag');\n\n $textarea.addEventListener('keypress', async (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n const command = e.target.value.trim();\n if (!command) return;\n\n // 1. UI 피드백\n $status.textContent = \"⚙️ 공정 분석 중...\";\n e.target.value = '';\n\n // 2. 체크된 타겟 수집\n const targets = Array.from(this.shadowRoot.querySelectorAll('input[name=\"gen_target\"]:checked'))\n .map(el => el.value);\n\n // 3. 분리된 서비스 호출\n try {\n const result = await this.#service.generate(command, targets, this.#routes);\n\n // 4. 성공 처리\n $status.textContent = \"✅ 완료\";\n nine.alert(\"소스 생성 성공\").rgb();\n this.dispatchEvent(new CustomEvent('nine-fab-completed', { detail: result, bubbles: true }));\n } catch (err) {\n // 5. 실패 처리\n $status.textContent = \"❌ 실패\";\n nine.alert(err.message).rgb().shake();\n }\n }\n });\n }\n\n #render() {\n const placeholder = this.getAttribute(\"placeholder\") || \"나에게 무엇이든 물어봐...\";\n const customImport = this.getAttribute(\"css-path\") ? `@import \"${this.getAttribute(\"css-path\")}\";` : \"\";\n\n this.shadowRoot.innerHTML = `\n <style>\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_VERSION__}/dist/css/nine-fab.css\";\n ${customImport}\n </style>\n <div class=\"wrapper\">\n <nine-ai-settings></nine-ai-settings>\n <div class=\"container\">\n <div class=\"head\">\n <div class=\"logo\"><span></span></div>\n <div id=\"status-tag\" style=\"font-size:10px; color:#ff5722; padding:5px;\">Fab Engine Ready</div>\n </div>\n <nx-ai-chat></nx-ai-chat>\n <div class=\"foot\">\n <div class=\"apply-src\">\n ${['MyBatis', 'Service', 'Controller', 'JavaScript'].map(t => `\n <div>\n <input type=\"checkbox\" id=\"${t.toLowerCase()}\" name=\"gen_target\" value=\"${t}\" checked />\n <label for=\"${t.toLowerCase()}\">${t}</label>\n </div>\n `).join('')}\n </div>\n <textarea id=\"q\" rows=\"4\" placeholder=\"${placeholder}\"></textarea>\n </div>\n </div>\n <div class=\"menu\">\n <div class=\"collapse-icon\"></div>\n <div class=\"menu-icon menu-filter active\"></div>\n <div class=\"menu-icon menu-general\"></div>\n <div class=\"menu-icon menu-setting\"></div>\n </div>\n </div>\n <div class=\"expand-icon\"></div>\n `;\n }\n}","import { trace } from '@nine-lab/nine-util';\nimport { NineChat } from './components/NineChat.js';\n\n/**\n * Nine-Fab 엔진 메인 클래스\n */\nexport const NineFab = {\n version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.1.0',\n init: (config) => {\n trace.log(\"🛠️ Nine-Fab Engine initialized\", config);\n // 향후 커넥터 URL 전역 설정이나 인증 토큰 처리 로직 추가 가능\n }\n};\n\n// 기본 export 및 컴포넌트 export\nexport default NineFab;\nexport { NineChat };"],"names":[],"mappings":"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"}
|
package/dist/nine-fab.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(t
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@nine-lab/nine-util")):"function"==typeof define&&define.amd?define(["exports","@nine-lab/nine-util"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NineFab={},e.NineUtil)}(this,function(e,t){"use strict";var n,i,a,s,o,r,c=e=>{throw TypeError(e)},l=(e,t,n)=>t.has(e)||c("Cannot "+n),d=(e,t,n)=>(l(e,t,"read from private field"),n?n.call(e):t.get(e)),h=(e,t,n)=>t.has(e)?c("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),u=(e,t,n,i)=>(l(e,t,"write to private field"),i?i.call(e,n):t.set(e,n),n),p=(e,t,n)=>(l(e,t,"access private method"),n);class v{constructor(e){this.connectorUrl=e}async generate(e,n,i){try{t.trace.log(`🚀 Fab 공정 시작: "${e}" [대상: ${n.join(", ")}]`);const a=await fetch(`${this.connectorUrl}/api/fab/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({command:e,targets:n,projectType:"spring-react",currentRoutes:i})}),s=await a.json();if(!s.success)throw new Error(s.error||"소스 생성 중 서버 오류가 발생했습니다.");return s}catch(a){throw t.trace.error("❌ Fab Service Error:",a),a}}async fetchRoutes(e){if(!e)return[];try{const t=await fetch(e);return await t.json()}catch(n){return t.trace.error("Route load fail",n),[]}}}class f extends HTMLElement{constructor(){super(),h(this,a),h(this,n,null),h(this,i,[]),this.attachShadow({mode:"open"})}async connectedCallback(){const e=this.getAttribute("connector-url")||"http://localhost:3002";u(this,n,new v(e)),p(this,a,r).call(this),p(this,a,s).call(this),p(this,a,o).call(this),u(this,i,await d(this,n).fetchRoutes(this.getAttribute("route-url")))}}n=new WeakMap,i=new WeakMap,a=new WeakSet,s=function(){const e=this.shadowRoot.querySelector("nine-ai-settings"),t=this.shadowRoot.querySelectorAll(".menu-icon"),n=()=>this.classList.toggle("collapse");this.shadowRoot.querySelector(".expand-icon").addEventListener("click",n),this.shadowRoot.querySelector(".collapse-icon").addEventListener("click",n),t.forEach(n=>{n.addEventListener("click",n=>{t.forEach(e=>e.classList.remove("active"));const i=n.target.closest(".menu-icon");i.classList.add("active"),e.classList.toggle("expand",i.classList.contains("menu-setting"))})})},o=function(){const e=this.shadowRoot.querySelector("#q"),a=this.shadowRoot.querySelector("#status-tag");e.addEventListener("keypress",async e=>{if("Enter"===e.key&&!e.shiftKey){e.preventDefault();const o=e.target.value.trim();if(!o)return;a.textContent="⚙️ 공정 분석 중...",e.target.value="";const r=Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked')).map(e=>e.value);try{const e=await d(this,n).generate(o,r,d(this,i));a.textContent="✅ 완료",t.nine.alert("소스 생성 성공").rgb(),this.dispatchEvent(new CustomEvent("nine-fab-completed",{detail:e,bubbles:!0}))}catch(s){a.textContent="❌ 실패",t.nine.alert(s.message).rgb().shake()}}})},r=function(){const e=this.getAttribute("placeholder")||"나에게 무엇이든 물어봐...",t=this.getAttribute("css-path")?`@import "${this.getAttribute("css-path")}";`:"";this.shadowRoot.innerHTML=`\n <style>\n @import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@0.1.7/dist/css/nine-fab.css";\n ${t}\n </style>\n <div class="wrapper">\n <nine-ai-settings></nine-ai-settings>\n <div class="container">\n <div class="head">\n <div class="logo"><span></span></div>\n <div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">Fab Engine Ready</div>\n </div>\n <nx-ai-chat></nx-ai-chat>\n <div class="foot">\n <div class="apply-src">\n ${["MyBatis","Service","Controller","JavaScript"].map(e=>`\n <div>\n <input type="checkbox" id="${e.toLowerCase()}" name="gen_target" value="${e}" checked />\n <label for="${e.toLowerCase()}">${e}</label>\n </div>\n `).join("")}\n </div>\n <textarea id="q" rows="4" placeholder="${e}"></textarea>\n </div>\n </div>\n <div class="menu">\n <div class="collapse-icon"></div>\n <div class="menu-icon menu-filter active"></div>\n <div class="menu-icon menu-general"></div>\n <div class="menu-icon menu-setting"></div>\n </div>\n </div>\n <div class="expand-icon"></div>\n `};const g={version:"0.1.7",init:e=>{t.trace.log("🛠️ Nine-Fab Engine initialized",e)}};e.NineChat=f,e.NineFab=g,e.default=g,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
2
2
|
//# sourceMappingURL=nine-fab.umd.js.map
|
package/dist/nine-fab.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nine-fab.umd.js","sources":["../src/components/NineChat.js","../src/index.js"],"sourcesContent":["import { nine, trace } from '@nine-lab/nine-util';\n// import { TipPopup } from '@nine-lab/nine-ai'; // 필요시 주석 해제\n\nexport class NineChat extends HTMLElement {\n #connectorUrl = '';\n #tipPopup = null;\n #routes = [];\n\n static {\n trace.init(\"nine-fab\", \"#FF5722\");\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n async connectedCallback() {\n this.#connectorUrl = this.getAttribute('connector-url') || 'http://localhost:3002';\n this.#render();\n await this.#init();\n }\n\n async #init() {\n // UI 요소 참조\n this.$textarea = this.shadowRoot.querySelector('#q');\n this.$settings = this.shadowRoot.querySelector('nine-ai-settings');\n\n // 아이콘 이벤트 연결\n this.shadowRoot.querySelector(\".expand-icon\").addEventListener(\"click\", this.#toggleCollapseHandler);\n this.shadowRoot.querySelector(\".collapse-icon\").addEventListener(\"click\", this.#toggleCollapseHandler);\n this.shadowRoot.querySelectorAll(\".menu-icon\").forEach(el => el.addEventListener(\"click\", this.#menuClickHandler));\n\n // [핵심] 텍스트 영역 엔터 이벤트 (Command 전송)\n this.$textarea.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n const command = e.target.value.trim();\n if (command) {\n this.#handleFabCommand(command);\n e.target.value = '';\n }\n }\n });\n\n // 기존 경로 정보 로드\n const routeUrl = this.getAttribute('route-url');\n if (routeUrl) {\n try {\n const res = await fetch(routeUrl);\n this.#routes = await res.json();\n trace.log(\"✅ 현재 프로젝트 경로 분석 완료\", this.#routes);\n } catch (err) {\n trace.error(\"❌ 경로 로드 실패:\", err.message);\n }\n }\n }\n\n #render() {\n const placeholder = this.getAttribute(\"placeholder\") || \"나에게 무엇이든 물어봐...\";\n const customPath = this.getAttribute(\"css-path\");\n const customImport = customPath ? `@import \"${customPath}\";` : \"\";\n const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'latest';\n\n this.shadowRoot.innerHTML = `\n <style>\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${version}/dist/css/nine-fab.css\";\n ${customImport}\n </style>\n \n <div class=\"wrapper\">\n\t\t\t\t<nine-ai-settings></nine-ai-settings>\n\t\t\t\n\t\t\t\t<div class=\"container\">\n\t\t\t\t\t<div class=\"head\">\n\t\t\t\t\t\t<div class=\"logo\"><span></span></div>\n <div id=\"status-tag\" style=\"font-size:10px; color:#ff5722; padding:5px;\">Fab Engine Ready</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<nx-ai-chat></nx-ai-chat>\n\t\t\t\t\t<div class=\"foot\">\n\t\t\t\t\t\t<div class=\"apply-src\">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"mybatis\" name=\"gen_target\" value=\"MyBatis\" checked />\n\t\t\t\t\t\t\t <label for=\"mybatis\">MyBatis</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"service\" name=\"gen_target\" value=\"Service\" checked />\n\t\t\t\t\t\t\t <label for=\"service\">Service</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"controller\" name=\"gen_target\" value=\"Controller\" checked />\n\t\t\t\t\t\t\t <label for=\"controller\">Controller</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t <input type=\"checkbox\" id=\"javascript\" name=\"gen_target\" value=\"JavaScript\" checked />\n\t\t\t\t\t\t\t <label for=\"javascript\">JavaScript</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<textarea id=\"q\" rows=\"4\" placeholder=\"${placeholder}\"></textarea>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"menu\">\n\t\t\t\t\t<div class=\"collapse-icon\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-filter active\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-general\"></div>\n\t\t\t\t\t<div class=\"menu-icon menu-setting\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"expand-icon\"></div>\n `;\n }\n\n async #handleFabCommand(command) {\n const statusEl = this.shadowRoot.getElementById('status-tag');\n\n // 체크된 생성 대상 수집\n const targets = Array.from(this.shadowRoot.querySelectorAll('input[name=\"gen_target\"]:checked'))\n .map(el => el.value);\n\n try {\n statusEl.textContent = \"⚙️ 공정 분석 중...\";\n trace.log(`🚀 Fab 명령 실행: \"${command}\" [대상: ${targets.join(', ')}]`);\n\n const response = await fetch(`${this.#connectorUrl}/api/fab/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n command,\n targets, // MyBatis, Service 등 선택된 항목 전송\n projectType: 'spring-react',\n currentRoutes: this.#routes\n })\n });\n\n const result = await response.json();\n\n if (!result.success) throw new Error(result.error || \"생성 실패\");\n\n trace.log(\"✅ 소스 생성 완료\", result.files);\n statusEl.textContent = \"✅ 파일 주입 완료\";\n nine.alert(\"소스가 성공적으로 생성되었습니다.\").rgb();\n\n this.dispatchEvent(new CustomEvent('nine-fab-completed', {\n detail: result,\n bubbles: true,\n composed: true\n }));\n\n } catch (error) {\n trace.error(\"Fab 공정 실패:\", error);\n statusEl.textContent = \"❌ 에러 발생\";\n nine.alert(error.message).rgb().shake();\n }\n }\n\n #toggleCollapseHandler = () => {\n this.classList.toggle(\"collapse\");\n };\n\n #menuClickHandler = (e) => {\n this.shadowRoot.querySelectorAll(\".menu-icon\").forEach(el => el.classList.remove(\"active\"));\n const clickedIcon = e.target.closest(\".menu-icon\");\n if (clickedIcon) clickedIcon.classList.add(\"active\");\n\n // nx-ai-settings 토글 처리\n if (this.$settings) {\n this.$settings.classList.toggle(\"expand\", !!e.target.closest(\".menu-setting\"));\n }\n };\n}\n\nif (!customElements.get('nine-chat')) {\n customElements.define('nine-chat', NineChat);\n}","import { trace } from '@nine-lab/nine-util';\nimport { NineChat } from './components/NineChat.js';\n\n/**\n * Nine-Fab 엔진 메인 클래스\n */\nexport const NineFab = {\n version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.1.0',\n init: (config) => {\n trace.log(\"🛠️ Nine-Fab Engine initialized\", config);\n // 향후 커넥터 URL 전역 설정이나 인증 토큰 처리 로직 추가 가능\n }\n};\n\n// 기본 export 및 컴포넌트 export\nexport default NineFab;\nexport { NineChat };"],"names":["NineChat","HTMLElement","constructor","super","__privateAdd","this","_NineChat_instances","_connectorUrl","_tipPopup","_routes","_toggleCollapseHandler","classList","toggle","_menuClickHandler","e","shadowRoot","querySelectorAll","forEach","el","remove","clickedIcon","target","closest","add","$settings","attachShadow","mode","connectedCallback","__privateSet","getAttribute","__privateMethod","render_fn","call","init_fn","WeakMap","WeakSet","async","$textarea","querySelector","addEventListener","__privateGet","key","shiftKey","preventDefault","command","value","trim","routeUrl","res","fetch","json","trace","log","err","error","message","placeholder","customPath","customImport","innerHTML","handleFabCommand_fn","statusEl","getElementById","targets","Array","from","map","textContent","join","response","method","headers","body","JSON","stringify","projectType","currentRoutes","result","success","Error","files","nine","alert","rgb","dispatchEvent","CustomEvent","detail","bubbles","composed","shake","init","customElements","get","define","NineFab","version","config"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nine-fab.umd.js","sources":["../src/services/NineFabService.js","../src/components/NineChat.js","../src/index.js"],"sourcesContent":["import { nine, trace } from '@nine-lab/nine-util';\n\nexport class NineFabService {\n constructor(connectorUrl) {\n this.connectorUrl = connectorUrl;\n }\n\n /**\n * AI 소스 생성 실행\n * @param {string} command - 사용자 입력 명령\n * @param {Array} targets - 체크된 생성 대상 (MyBatis, Service 등)\n * @param {Array} currentRoutes - 현재 프로젝트 경로 정보\n */\n async generate(command, targets, currentRoutes) {\n try {\n trace.log(`🚀 Fab 공정 시작: \"${command}\" [대상: ${targets.join(', ')}]`);\n\n const response = await fetch(`${this.connectorUrl}/api/fab/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n command,\n targets,\n projectType: 'spring-react',\n currentRoutes\n })\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || \"소스 생성 중 서버 오류가 발생했습니다.\");\n }\n\n return result;\n } catch (error) {\n trace.error(\"❌ Fab Service Error:\", error);\n throw error;\n }\n }\n\n /**\n * 초기 경로 정보 로드\n */\n async fetchRoutes(routeUrl) {\n if (!routeUrl) return [];\n try {\n const res = await fetch(routeUrl);\n return await res.json();\n } catch (err) {\n trace.error(\"Route load fail\", err);\n return [];\n }\n }\n}","import { nine, trace } from '@nine-lab/nine-util';\nimport { NineFabService } from '../services/NineFabService.js';\n\nexport class NineChat extends HTMLElement {\n #service = null;\n #routes = [];\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n async connectedCallback() {\n // 서비스 초기화\n const connectorUrl = this.getAttribute('connector-url') || 'http://localhost:3002';\n this.#service = new NineFabService(connectorUrl);\n\n this.#render();\n this.#initInteractions(); // UI 토글 로직\n this.#initActions(); // 엔터 시 실행 로직\n\n // 경로 데이터 로드\n this.#routes = await this.#service.fetchRoutes(this.getAttribute('route-url'));\n }\n\n // --- [그룹 1: Interaction] 화면 토글 및 메뉴 클릭 ---\n #initInteractions() {\n const $settings = this.shadowRoot.querySelector('nine-ai-settings');\n const $menuIcons = this.shadowRoot.querySelectorAll(\".menu-icon\");\n\n const toggleUI = () => this.classList.toggle(\"collapse\");\n this.shadowRoot.querySelector(\".expand-icon\").addEventListener(\"click\", toggleUI);\n this.shadowRoot.querySelector(\".collapse-icon\").addEventListener(\"click\", toggleUI);\n\n $menuIcons.forEach(el => {\n el.addEventListener(\"click\", (e) => {\n $menuIcons.forEach(icon => icon.classList.remove(\"active\"));\n const clicked = e.target.closest(\".menu-icon\");\n clicked.classList.add(\"active\");\n $settings.classList.toggle(\"expand\", clicked.classList.contains('menu-setting'));\n });\n });\n }\n\n // --- [그룹 2: Action] 엔터키 입력 시 서비스 호출 ---\n #initActions() {\n const $textarea = this.shadowRoot.querySelector('#q');\n const $status = this.shadowRoot.querySelector('#status-tag');\n\n $textarea.addEventListener('keypress', async (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n const command = e.target.value.trim();\n if (!command) return;\n\n // 1. UI 피드백\n $status.textContent = \"⚙️ 공정 분석 중...\";\n e.target.value = '';\n\n // 2. 체크된 타겟 수집\n const targets = Array.from(this.shadowRoot.querySelectorAll('input[name=\"gen_target\"]:checked'))\n .map(el => el.value);\n\n // 3. 분리된 서비스 호출\n try {\n const result = await this.#service.generate(command, targets, this.#routes);\n\n // 4. 성공 처리\n $status.textContent = \"✅ 완료\";\n nine.alert(\"소스 생성 성공\").rgb();\n this.dispatchEvent(new CustomEvent('nine-fab-completed', { detail: result, bubbles: true }));\n } catch (err) {\n // 5. 실패 처리\n $status.textContent = \"❌ 실패\";\n nine.alert(err.message).rgb().shake();\n }\n }\n });\n }\n\n #render() {\n const placeholder = this.getAttribute(\"placeholder\") || \"나에게 무엇이든 물어봐...\";\n const customImport = this.getAttribute(\"css-path\") ? `@import \"${this.getAttribute(\"css-path\")}\";` : \"\";\n\n this.shadowRoot.innerHTML = `\n <style>\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_VERSION__}/dist/css/nine-fab.css\";\n ${customImport}\n </style>\n <div class=\"wrapper\">\n <nine-ai-settings></nine-ai-settings>\n <div class=\"container\">\n <div class=\"head\">\n <div class=\"logo\"><span></span></div>\n <div id=\"status-tag\" style=\"font-size:10px; color:#ff5722; padding:5px;\">Fab Engine Ready</div>\n </div>\n <nx-ai-chat></nx-ai-chat>\n <div class=\"foot\">\n <div class=\"apply-src\">\n ${['MyBatis', 'Service', 'Controller', 'JavaScript'].map(t => `\n <div>\n <input type=\"checkbox\" id=\"${t.toLowerCase()}\" name=\"gen_target\" value=\"${t}\" checked />\n <label for=\"${t.toLowerCase()}\">${t}</label>\n </div>\n `).join('')}\n </div>\n <textarea id=\"q\" rows=\"4\" placeholder=\"${placeholder}\"></textarea>\n </div>\n </div>\n <div class=\"menu\">\n <div class=\"collapse-icon\"></div>\n <div class=\"menu-icon menu-filter active\"></div>\n <div class=\"menu-icon menu-general\"></div>\n <div class=\"menu-icon menu-setting\"></div>\n </div>\n </div>\n <div class=\"expand-icon\"></div>\n `;\n }\n}","import { trace } from '@nine-lab/nine-util';\nimport { NineChat } from './components/NineChat.js';\n\n/**\n * Nine-Fab 엔진 메인 클래스\n */\nexport const NineFab = {\n version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.1.0',\n init: (config) => {\n trace.log(\"🛠️ Nine-Fab Engine initialized\", config);\n // 향후 커넥터 URL 전역 설정이나 인증 토큰 처리 로직 추가 가능\n }\n};\n\n// 기본 export 및 컴포넌트 export\nexport default NineFab;\nexport { NineChat };"],"names":["NineFabService","constructor","connectorUrl","this","generate","command","targets","currentRoutes","trace","log","join","response","fetch","method","headers","body","JSON","stringify","projectType","result","json","success","Error","error","fetchRoutes","routeUrl","res","err","NineChat","HTMLElement","super","__privateAdd","_NineChat_instances","_service","_routes","attachShadow","mode","connectedCallback","getAttribute","__privateSet","__privateMethod","render_fn","call","initInteractions_fn","initActions_fn","__privateGet","WeakMap","WeakSet","$settings","shadowRoot","querySelector","$menuIcons","querySelectorAll","toggleUI","classList","toggle","addEventListener","forEach","el","e","icon","remove","clicked","target","closest","add","contains","$textarea","$status","async","key","shiftKey","preventDefault","value","trim","textContent","Array","from","map","nine","alert","rgb","dispatchEvent","CustomEvent","detail","bubbles","message","shake","placeholder","customImport","innerHTML","t","toLowerCase","NineFab","version","init","config"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,174 +1,120 @@
|
|
|
1
1
|
import { nine, trace } from '@nine-lab/nine-util';
|
|
2
|
-
|
|
2
|
+
import { NineFabService } from '../services/NineFabService.js';
|
|
3
3
|
|
|
4
4
|
export class NineChat extends HTMLElement {
|
|
5
|
-
#
|
|
6
|
-
#tipPopup = null;
|
|
5
|
+
#service = null;
|
|
7
6
|
#routes = [];
|
|
8
7
|
|
|
9
|
-
static {
|
|
10
|
-
trace.init("nine-fab", "#FF5722");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
8
|
constructor() {
|
|
14
9
|
super();
|
|
15
10
|
this.attachShadow({ mode: 'open' });
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
async connectedCallback() {
|
|
19
|
-
|
|
14
|
+
// 서비스 초기화
|
|
15
|
+
const connectorUrl = this.getAttribute('connector-url') || 'http://localhost:3002';
|
|
16
|
+
this.#service = new NineFabService(connectorUrl);
|
|
17
|
+
|
|
20
18
|
this.#render();
|
|
21
|
-
|
|
19
|
+
this.#initInteractions(); // UI 토글 로직
|
|
20
|
+
this.#initActions(); // 엔터 시 실행 로직
|
|
21
|
+
|
|
22
|
+
// 경로 데이터 로드
|
|
23
|
+
this.#routes = await this.#service.fetchRoutes(this.getAttribute('route-url'));
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// --- [그룹 1: Interaction] 화면 토글 및 메뉴 클릭 ---
|
|
27
|
+
#initInteractions() {
|
|
28
|
+
const $settings = this.shadowRoot.querySelector('nine-ai-settings');
|
|
29
|
+
const $menuIcons = this.shadowRoot.querySelectorAll(".menu-icon");
|
|
30
|
+
|
|
31
|
+
const toggleUI = () => this.classList.toggle("collapse");
|
|
32
|
+
this.shadowRoot.querySelector(".expand-icon").addEventListener("click", toggleUI);
|
|
33
|
+
this.shadowRoot.querySelector(".collapse-icon").addEventListener("click", toggleUI);
|
|
34
|
+
|
|
35
|
+
$menuIcons.forEach(el => {
|
|
36
|
+
el.addEventListener("click", (e) => {
|
|
37
|
+
$menuIcons.forEach(icon => icon.classList.remove("active"));
|
|
38
|
+
const clicked = e.target.closest(".menu-icon");
|
|
39
|
+
clicked.classList.add("active");
|
|
40
|
+
$settings.classList.toggle("expand", clicked.classList.contains('menu-setting'));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.shadowRoot.querySelector(
|
|
32
|
-
this.shadowRoot.
|
|
45
|
+
// --- [그룹 2: Action] 엔터키 입력 시 서비스 호출 ---
|
|
46
|
+
#initActions() {
|
|
47
|
+
const $textarea = this.shadowRoot.querySelector('#q');
|
|
48
|
+
const $status = this.shadowRoot.querySelector('#status-tag');
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
this.$textarea.addEventListener('keypress', (e) => {
|
|
50
|
+
$textarea.addEventListener('keypress', async (e) => {
|
|
36
51
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
37
52
|
e.preventDefault();
|
|
38
53
|
const command = e.target.value.trim();
|
|
39
|
-
if (command)
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
if (!command) return;
|
|
55
|
+
|
|
56
|
+
// 1. UI 피드백
|
|
57
|
+
$status.textContent = "⚙️ 공정 분석 중...";
|
|
58
|
+
e.target.value = '';
|
|
59
|
+
|
|
60
|
+
// 2. 체크된 타겟 수집
|
|
61
|
+
const targets = Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked'))
|
|
62
|
+
.map(el => el.value);
|
|
63
|
+
|
|
64
|
+
// 3. 분리된 서비스 호출
|
|
65
|
+
try {
|
|
66
|
+
const result = await this.#service.generate(command, targets, this.#routes);
|
|
67
|
+
|
|
68
|
+
// 4. 성공 처리
|
|
69
|
+
$status.textContent = "✅ 완료";
|
|
70
|
+
nine.alert("소스 생성 성공").rgb();
|
|
71
|
+
this.dispatchEvent(new CustomEvent('nine-fab-completed', { detail: result, bubbles: true }));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
// 5. 실패 처리
|
|
74
|
+
$status.textContent = "❌ 실패";
|
|
75
|
+
nine.alert(err.message).rgb().shake();
|
|
42
76
|
}
|
|
43
77
|
}
|
|
44
78
|
});
|
|
45
|
-
|
|
46
|
-
// 기존 경로 정보 로드
|
|
47
|
-
const routeUrl = this.getAttribute('route-url');
|
|
48
|
-
if (routeUrl) {
|
|
49
|
-
try {
|
|
50
|
-
const res = await fetch(routeUrl);
|
|
51
|
-
this.#routes = await res.json();
|
|
52
|
-
trace.log("✅ 현재 프로젝트 경로 분석 완료", this.#routes);
|
|
53
|
-
} catch (err) {
|
|
54
|
-
trace.error("❌ 경로 로드 실패:", err.message);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
79
|
}
|
|
58
80
|
|
|
59
81
|
#render() {
|
|
60
82
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
61
|
-
const
|
|
62
|
-
const customImport = customPath ? `@import "${customPath}";` : "";
|
|
63
|
-
const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'latest';
|
|
83
|
+
const customImport = this.getAttribute("css-path") ? `@import "${this.getAttribute("css-path")}";` : "";
|
|
64
84
|
|
|
65
85
|
this.shadowRoot.innerHTML = `
|
|
66
86
|
<style>
|
|
67
|
-
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${
|
|
87
|
+
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_VERSION__}/dist/css/nine-fab.css";
|
|
68
88
|
${customImport}
|
|
69
89
|
</style>
|
|
70
|
-
|
|
71
90
|
<div class="wrapper">
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<div class="logo"><span></span></div>
|
|
91
|
+
<nine-ai-settings></nine-ai-settings>
|
|
92
|
+
<div class="container">
|
|
93
|
+
<div class="head">
|
|
94
|
+
<div class="logo"><span></span></div>
|
|
77
95
|
<div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">Fab Engine Ready</div>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<div class="menu">
|
|
103
|
-
<div class="collapse-icon"></div>
|
|
104
|
-
<div class="menu-icon menu-filter active"></div>
|
|
105
|
-
<div class="menu-icon menu-general"></div>
|
|
106
|
-
<div class="menu-icon menu-setting"></div>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
<div class="expand-icon"></div>
|
|
96
|
+
</div>
|
|
97
|
+
<nx-ai-chat></nx-ai-chat>
|
|
98
|
+
<div class="foot">
|
|
99
|
+
<div class="apply-src">
|
|
100
|
+
${['MyBatis', 'Service', 'Controller', 'JavaScript'].map(t => `
|
|
101
|
+
<div>
|
|
102
|
+
<input type="checkbox" id="${t.toLowerCase()}" name="gen_target" value="${t}" checked />
|
|
103
|
+
<label for="${t.toLowerCase()}">${t}</label>
|
|
104
|
+
</div>
|
|
105
|
+
`).join('')}
|
|
106
|
+
</div>
|
|
107
|
+
<textarea id="q" rows="4" placeholder="${placeholder}"></textarea>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="menu">
|
|
111
|
+
<div class="collapse-icon"></div>
|
|
112
|
+
<div class="menu-icon menu-filter active"></div>
|
|
113
|
+
<div class="menu-icon menu-general"></div>
|
|
114
|
+
<div class="menu-icon menu-setting"></div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="expand-icon"></div>
|
|
110
118
|
`;
|
|
111
119
|
}
|
|
112
|
-
|
|
113
|
-
async #handleFabCommand(command) {
|
|
114
|
-
const statusEl = this.shadowRoot.getElementById('status-tag');
|
|
115
|
-
|
|
116
|
-
// 체크된 생성 대상 수집
|
|
117
|
-
const targets = Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked'))
|
|
118
|
-
.map(el => el.value);
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
statusEl.textContent = "⚙️ 공정 분석 중...";
|
|
122
|
-
trace.log(`🚀 Fab 명령 실행: "${command}" [대상: ${targets.join(', ')}]`);
|
|
123
|
-
|
|
124
|
-
const response = await fetch(`${this.#connectorUrl}/api/fab/generate`, {
|
|
125
|
-
method: 'POST',
|
|
126
|
-
headers: { 'Content-Type': 'application/json' },
|
|
127
|
-
body: JSON.stringify({
|
|
128
|
-
command,
|
|
129
|
-
targets, // MyBatis, Service 등 선택된 항목 전송
|
|
130
|
-
projectType: 'spring-react',
|
|
131
|
-
currentRoutes: this.#routes
|
|
132
|
-
})
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const result = await response.json();
|
|
136
|
-
|
|
137
|
-
if (!result.success) throw new Error(result.error || "생성 실패");
|
|
138
|
-
|
|
139
|
-
trace.log("✅ 소스 생성 완료", result.files);
|
|
140
|
-
statusEl.textContent = "✅ 파일 주입 완료";
|
|
141
|
-
nine.alert("소스가 성공적으로 생성되었습니다.").rgb();
|
|
142
|
-
|
|
143
|
-
this.dispatchEvent(new CustomEvent('nine-fab-completed', {
|
|
144
|
-
detail: result,
|
|
145
|
-
bubbles: true,
|
|
146
|
-
composed: true
|
|
147
|
-
}));
|
|
148
|
-
|
|
149
|
-
} catch (error) {
|
|
150
|
-
trace.error("Fab 공정 실패:", error);
|
|
151
|
-
statusEl.textContent = "❌ 에러 발생";
|
|
152
|
-
nine.alert(error.message).rgb().shake();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
#toggleCollapseHandler = () => {
|
|
157
|
-
this.classList.toggle("collapse");
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
#menuClickHandler = (e) => {
|
|
161
|
-
this.shadowRoot.querySelectorAll(".menu-icon").forEach(el => el.classList.remove("active"));
|
|
162
|
-
const clickedIcon = e.target.closest(".menu-icon");
|
|
163
|
-
if (clickedIcon) clickedIcon.classList.add("active");
|
|
164
|
-
|
|
165
|
-
// nx-ai-settings 토글 처리
|
|
166
|
-
if (this.$settings) {
|
|
167
|
-
this.$settings.classList.toggle("expand", !!e.target.closest(".menu-setting"));
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (!customElements.get('nine-chat')) {
|
|
173
|
-
customElements.define('nine-chat', NineChat);
|
|
174
120
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { nine, trace } from '@nine-lab/nine-util';
|
|
2
|
+
|
|
3
|
+
export class NineFabService {
|
|
4
|
+
constructor(connectorUrl) {
|
|
5
|
+
this.connectorUrl = connectorUrl;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* AI 소스 생성 실행
|
|
10
|
+
* @param {string} command - 사용자 입력 명령
|
|
11
|
+
* @param {Array} targets - 체크된 생성 대상 (MyBatis, Service 등)
|
|
12
|
+
* @param {Array} currentRoutes - 현재 프로젝트 경로 정보
|
|
13
|
+
*/
|
|
14
|
+
async generate(command, targets, currentRoutes) {
|
|
15
|
+
try {
|
|
16
|
+
trace.log(`🚀 Fab 공정 시작: "${command}" [대상: ${targets.join(', ')}]`);
|
|
17
|
+
|
|
18
|
+
const response = await fetch(`${this.connectorUrl}/api/fab/generate`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: { 'Content-Type': 'application/json' },
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
command,
|
|
23
|
+
targets,
|
|
24
|
+
projectType: 'spring-react',
|
|
25
|
+
currentRoutes
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const result = await response.json();
|
|
30
|
+
|
|
31
|
+
if (!result.success) {
|
|
32
|
+
throw new Error(result.error || "소스 생성 중 서버 오류가 발생했습니다.");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
trace.error("❌ Fab Service Error:", error);
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 초기 경로 정보 로드
|
|
44
|
+
*/
|
|
45
|
+
async fetchRoutes(routeUrl) {
|
|
46
|
+
if (!routeUrl) return [];
|
|
47
|
+
try {
|
|
48
|
+
const res = await fetch(routeUrl);
|
|
49
|
+
return await res.json();
|
|
50
|
+
} catch (err) {
|
|
51
|
+
trace.error("Route load fail", err);
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|