@nine-lab/nine-fab 0.1.5 → 0.1.7
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 +49 -27
- 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 +62 -53
package/dist/nine-fab.js
CHANGED
|
@@ -6,9 +6,8 @@ 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 _connectorUrl, _tipPopup, _routes, _NineChat_instances, init_fn, render_fn, handleFabCommand_fn;
|
|
9
|
+
var _connectorUrl, _tipPopup, _routes, _NineChat_instances, init_fn, render_fn, handleFabCommand_fn, _toggleCollapseHandler, _menuClickHandler;
|
|
10
10
|
import { trace, nine } from "@nine-lab/nine-util";
|
|
11
|
-
import "@nine-lab/nine-ai";
|
|
12
11
|
class NineChat extends HTMLElement {
|
|
13
12
|
constructor() {
|
|
14
13
|
super();
|
|
@@ -16,6 +15,17 @@ class NineChat extends HTMLElement {
|
|
|
16
15
|
__privateAdd(this, _connectorUrl, "");
|
|
17
16
|
__privateAdd(this, _tipPopup, null);
|
|
18
17
|
__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
|
+
});
|
|
19
29
|
this.attachShadow({ mode: "open" });
|
|
20
30
|
}
|
|
21
31
|
async connectedCallback() {
|
|
@@ -29,6 +39,21 @@ _tipPopup = new WeakMap();
|
|
|
29
39
|
_routes = new WeakMap();
|
|
30
40
|
_NineChat_instances = new WeakSet();
|
|
31
41
|
init_fn = async function() {
|
|
42
|
+
this.$textarea = this.shadowRoot.querySelector("#q");
|
|
43
|
+
this.$settings = this.shadowRoot.querySelector("nine-ai-settings");
|
|
44
|
+
this.shadowRoot.querySelector(".expand-icon").addEventListener("click", __privateGet(this, _toggleCollapseHandler));
|
|
45
|
+
this.shadowRoot.querySelector(".collapse-icon").addEventListener("click", __privateGet(this, _toggleCollapseHandler));
|
|
46
|
+
this.shadowRoot.querySelectorAll(".menu-icon").forEach((el) => el.addEventListener("click", __privateGet(this, _menuClickHandler)));
|
|
47
|
+
this.$textarea.addEventListener("keypress", (e) => {
|
|
48
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
const command = e.target.value.trim();
|
|
51
|
+
if (command) {
|
|
52
|
+
__privateMethod(this, _NineChat_instances, handleFabCommand_fn).call(this, command);
|
|
53
|
+
e.target.value = "";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
32
57
|
const routeUrl = this.getAttribute("route-url");
|
|
33
58
|
if (routeUrl) {
|
|
34
59
|
try {
|
|
@@ -44,10 +69,10 @@ render_fn = function() {
|
|
|
44
69
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
45
70
|
const customPath = this.getAttribute("css-path");
|
|
46
71
|
const customImport = customPath ? `@import "${customPath}";` : "";
|
|
72
|
+
const version = "0.1.6";
|
|
47
73
|
this.shadowRoot.innerHTML = `
|
|
48
74
|
<style>
|
|
49
|
-
|
|
50
|
-
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${"0.1.4"}/dist/css/nine-fab.css";
|
|
75
|
+
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${version}/dist/css/nine-fab.css";
|
|
51
76
|
${customImport}
|
|
52
77
|
</style>
|
|
53
78
|
|
|
@@ -57,28 +82,29 @@ render_fn = function() {
|
|
|
57
82
|
<div class="container">
|
|
58
83
|
<div class="head">
|
|
59
84
|
<div class="logo"><span></span></div>
|
|
85
|
+
<div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">Fab Engine Ready</div>
|
|
60
86
|
</div>
|
|
61
87
|
<nx-ai-chat></nx-ai-chat>
|
|
62
88
|
<div class="foot">
|
|
63
89
|
<div class="apply-src">
|
|
64
90
|
<div>
|
|
65
|
-
<input type="checkbox" id="mybatis" name="
|
|
91
|
+
<input type="checkbox" id="mybatis" name="gen_target" value="MyBatis" checked />
|
|
66
92
|
<label for="mybatis">MyBatis</label>
|
|
67
93
|
</div>
|
|
68
94
|
<div>
|
|
69
|
-
<input type="checkbox" id="service" name="
|
|
95
|
+
<input type="checkbox" id="service" name="gen_target" value="Service" checked />
|
|
70
96
|
<label for="service">Service</label>
|
|
71
97
|
</div>
|
|
72
98
|
<div>
|
|
73
|
-
<input type="checkbox" id="controller" name="
|
|
99
|
+
<input type="checkbox" id="controller" name="gen_target" value="Controller" checked />
|
|
74
100
|
<label for="controller">Controller</label>
|
|
75
101
|
</div>
|
|
76
102
|
<div>
|
|
77
|
-
<input type="checkbox" id="javascript" name="
|
|
103
|
+
<input type="checkbox" id="javascript" name="gen_target" value="JavaScript" checked />
|
|
78
104
|
<label for="javascript">JavaScript</label>
|
|
79
105
|
</div>
|
|
80
106
|
</div>
|
|
81
|
-
<textarea
|
|
107
|
+
<textarea id="q" rows="4" placeholder="${placeholder}"></textarea>
|
|
82
108
|
</div>
|
|
83
109
|
</div>
|
|
84
110
|
<div class="menu">
|
|
@@ -88,35 +114,31 @@ render_fn = function() {
|
|
|
88
114
|
<div class="menu-icon menu-setting"></div>
|
|
89
115
|
</div>
|
|
90
116
|
</div>
|
|
91
|
-
|
|
92
117
|
<div class="expand-icon"></div>
|
|
93
118
|
`;
|
|
94
119
|
};
|
|
95
120
|
handleFabCommand_fn = async function(command) {
|
|
96
|
-
|
|
97
|
-
const
|
|
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);
|
|
98
123
|
try {
|
|
99
|
-
statusEl.textContent = "
|
|
100
|
-
|
|
101
|
-
trace.log(`🚀 소스 생성 명령 전송: "${command}"`);
|
|
124
|
+
statusEl.textContent = "⚙️ 공정 분석 중...";
|
|
125
|
+
trace.log(`🚀 Fab 명령 실행: "${command}" [대상: ${targets.join(", ")}]`);
|
|
102
126
|
const response = await fetch(`${__privateGet(this, _connectorUrl)}/api/fab/generate`, {
|
|
103
127
|
method: "POST",
|
|
104
128
|
headers: { "Content-Type": "application/json" },
|
|
105
129
|
body: JSON.stringify({
|
|
106
130
|
command,
|
|
131
|
+
targets,
|
|
132
|
+
// MyBatis, Service 등 선택된 항목 전송
|
|
107
133
|
projectType: "spring-react",
|
|
108
|
-
// 템플릿 정보
|
|
109
134
|
currentRoutes: __privateGet(this, _routes)
|
|
110
|
-
// 기존 경로 정보 (참고용)
|
|
111
135
|
})
|
|
112
136
|
});
|
|
113
137
|
const result = await response.json();
|
|
114
|
-
if (!result.success)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
statusEl.textContent = "✅ 생성 완료! 잠시 후 화면이 갱신됩니다.";
|
|
119
|
-
nine.alert("소스 생성이 완료되었습니다!").rgb();
|
|
138
|
+
if (!result.success) throw new Error(result.error || "생성 실패");
|
|
139
|
+
trace.log("✅ 소스 생성 완료", result.files);
|
|
140
|
+
statusEl.textContent = "✅ 파일 주입 완료";
|
|
141
|
+
nine.alert("소스가 성공적으로 생성되었습니다.").rgb();
|
|
120
142
|
this.dispatchEvent(new CustomEvent("nine-fab-completed", {
|
|
121
143
|
detail: result,
|
|
122
144
|
bubbles: true,
|
|
@@ -124,18 +146,18 @@ handleFabCommand_fn = async function(command) {
|
|
|
124
146
|
}));
|
|
125
147
|
} catch (error) {
|
|
126
148
|
trace.error("Fab 공정 실패:", error);
|
|
127
|
-
statusEl.textContent = "❌
|
|
149
|
+
statusEl.textContent = "❌ 에러 발생";
|
|
128
150
|
nine.alert(error.message).rgb().shake();
|
|
129
|
-
} finally {
|
|
130
|
-
(_b = __privateGet(this, _tipPopup)) == null ? void 0 : _b.close();
|
|
131
151
|
}
|
|
132
152
|
};
|
|
153
|
+
_toggleCollapseHandler = new WeakMap();
|
|
154
|
+
_menuClickHandler = new WeakMap();
|
|
133
155
|
trace.init("nine-fab", "#FF5722");
|
|
134
156
|
if (!customElements.get("nine-chat")) {
|
|
135
157
|
customElements.define("nine-chat", NineChat);
|
|
136
158
|
}
|
|
137
159
|
const NineFab = {
|
|
138
|
-
version: "0.1.
|
|
160
|
+
version: "0.1.6",
|
|
139
161
|
init: (config) => {
|
|
140
162
|
trace.log("🛠️ Nine-Fab Engine initialized", config);
|
|
141
163
|
}
|
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';\nimport { TipPopup } from '@nine-lab/nine-ai';\n\nexport class NineChat extends HTMLElement {\n #connectorUrl = '';\n #tipPopup = null;\n #routes = [];\n\n static {\n // Fab 전용 로깅 컬러로 변경 (색상 차별화)\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 // [중요] Fab 엔진은 route-url을 통해 현재 등록된 전체 메뉴를 파악하여\n // AI가 중복된 경로를 만들지 않게 하거나, 기존 소스를 참고하게 합니다.\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\n const customPath = this.getAttribute(\"css-path\");\n const customImport = customPath ? `@import \"${customPath}\";` : \"\";\n\n this.shadowRoot.innerHTML = `\n <style>\n /* Fab 전용 스타일 (nomenu와 차별화 추천) */\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_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\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=\"mybatis\" value=\"Y\" 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=\"service\" value=\"Y\" 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=\"controller\" value=\"Y\" 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=\"javascript\" value=\"Y\" 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 name=\"ask\" id=\"q\" name=\"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\n\t\t\t<div class=\"expand-icon\"></div>\n `;\n\n /**\n if (customElements.get(\"nine-ai-tip-popup\")) {\n this.#tipPopup = document.createElement('nine-ai-tip-popup');\n this.shadowRoot.appendChild(this.#tipPopup);\n }\n\n const input = this.shadowRoot.querySelector('input');\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n const command = e.target.value.trim();\n if (command) {\n this.#handleFabCommand(command); // 내비게이션 대신 생성 명령 호출\n e.target.value = '';\n }\n }\n }); */\n }\n\n /**\n * [핵심] 소스 코드 생성 및 파일 주입 요청\n */\n async #handleFabCommand(command) {\n const statusEl = this.shadowRoot.getElementById('status');\n try {\n statusEl.textContent = \"AI가 설계를 분석 중입니다...\";\n await this.#tipPopup?.popup();\n\n trace.log(`🚀 소스 생성 명령 전송: \"${command}\"`);\n\n // [변경] nomenu/go -> fab/generate\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 projectType: 'spring-react', // 템플릿 정보\n currentRoutes: this.#routes // 기존 경로 정보 (참고용)\n })\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || \"소스 생성 중 오류가 발생했습니다.\");\n }\n\n trace.log(\"✅ 소스 생성 및 파일 주입 완료!\", result.files);\n statusEl.textContent = \"✅ 생성 완료! 잠시 후 화면이 갱신됩니다.\";\n\n nine.alert(\"소스 생성이 완료되었습니다!\").rgb();\n\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 } finally {\n this.#tipPopup?.close();\n }\n }\n}\n\n// 1. 웹컴포넌트 등록\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":";;;;;;;;;;;AAGO,MAAM,iBAAiB,YAAY;AAAA,EAUtC,cAAc;AACV,UAAA;AAXD;AACH,sCAAgB;AAChB,kCAAY;AACZ,gCAAU,CAAA;AASN,SAAK,aAAa,EAAE,MAAM,OAAA,CAAQ;AAAA,EACtC;AAAA,EAEA,MAAM,oBAAoB;AACtB,uBAAK,eAAgB,KAAK,aAAa,eAAe,KAAK;AAC3D,0BAAK,gCAAL;AACA,UAAM,sBAAK,8BAAL;AAAA,EACV;AAyIJ;AA3JI;AACA;AACA;AAHG;AAqBG,UAAA,iBAAQ;AAGV,QAAM,WAAW,KAAK,aAAa,WAAW;AAC9C,MAAI,UAAU;AACV,QAAI;AACA,YAAM,MAAM,MAAM,MAAM,QAAQ;AAChC,yBAAK,SAAU,MAAM,IAAI,KAAA;AACzB,YAAM,IAAI,sBAAsB,mBAAK,QAAO;AAAA,IAChD,SAAS,KAAK;AACV,YAAM,MAAM,eAAe,IAAI,OAAO;AAAA,IAC1C;AAAA,EACJ;AACJ;AAEA,YAAA,WAAU;AACN,QAAM,cAAc,KAAK,aAAa,aAAa,KAAK;AAExD,QAAM,aAAa,KAAK,aAAa,UAAU;AAC/C,QAAM,eAAe,aAAa,YAAY,UAAU,OAAO;AAE/D,OAAK,WAAW,YAAY;AAAA;AAAA;AAAA,2EAGuC,OAAe;AAAA,kBACxE,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mEA8BqC,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8B1E;AAKM,qCAAkB,SAAS;;AAC7B,QAAM,WAAW,KAAK,WAAW,eAAe,QAAQ;AACxD,MAAI;AACA,aAAS,cAAc;AACvB,YAAM,wBAAK,eAAL,mBAAgB;AAEtB,UAAM,IAAI,oBAAoB,OAAO,GAAG;AAGxC,UAAM,WAAW,MAAM,MAAM,GAAG,mBAAK,cAAa,qBAAqB;AAAA,MACnE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,MAC3B,MAAM,KAAK,UAAU;AAAA,QACjB;AAAA,QACA,aAAa;AAAA;AAAA,QACb,eAAe,mBAAK;AAAA;AAAA,MAAA,CACvB;AAAA,IAAA,CACJ;AAED,UAAM,SAAS,MAAM,SAAS,KAAA;AAE9B,QAAI,CAAC,OAAO,SAAS;AACjB,YAAM,IAAI,MAAM,OAAO,SAAS,qBAAqB;AAAA,IACzD;AAEA,UAAM,IAAI,uBAAuB,OAAO,KAAK;AAC7C,aAAS,cAAc;AAEvB,SAAK,MAAM,iBAAiB,EAAE,IAAA;AAG9B,SAAK,cAAc,IAAI,YAAY,sBAAsB;AAAA,MACrD,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,UAAU;AAAA,IAAA,CACb,CAAC;AAAA,EAEN,SAAS,OAAO;AACZ,UAAM,MAAM,cAAc,KAAK;AAC/B,aAAS,cAAc;AACvB,SAAK,MAAM,MAAM,OAAO,EAAE,IAAA,EAAM,MAAA;AAAA,EACpC,UAAA;AACI,6BAAK,eAAL,mBAAgB;AAAA,EACpB;AACJ;AApJI,MAAM,KAAK,YAAY,SAAS;AAwJxC,IAAI,CAAC,eAAe,IAAI,WAAW,GAAG;AAClC,iBAAe,OAAO,aAAa,QAAQ;AAC/C;AC9JO,MAAM,UAAU;AAAA,EACnB,SAAkD;AAAA,EAClD,MAAM,CAAC,WAAW;AACd,UAAM,IAAI,mCAAmC,MAAM;AAAA,EAEvD;AACJ;"}
|
|
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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"}
|
package/dist/nine-fab.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@nine-lab/nine-util")
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@nine-lab/nine-util")):"function"==typeof define&&define.amd?define(["exports","@nine-lab/nine-util"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).NineFab={},t.NineUtil)}(this,function(t,e){"use strict";var n,i,a,s,o,c,r,l,d,h=t=>{throw TypeError(t)},p=(t,e,n)=>e.has(t)||h("Cannot "+n),v=(t,e,n)=>(p(t,e,"read from private field"),n?n.call(t):e.get(t)),u=(t,e,n)=>e.has(t)?h("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,n),g=(t,e,n,i)=>(p(t,e,"write to private field"),i?i.call(t,n):e.set(t,n),n),f=(t,e,n)=>(p(t,e,"access private method"),n);class m extends HTMLElement{constructor(){super(),u(this,s),u(this,n,""),u(this,i,null),u(this,a,[]),u(this,l,()=>{this.classList.toggle("collapse")}),u(this,d,t=>{this.shadowRoot.querySelectorAll(".menu-icon").forEach(t=>t.classList.remove("active"));const e=t.target.closest(".menu-icon");e&&e.classList.add("active"),this.$settings&&this.$settings.classList.toggle("expand",!!t.target.closest(".menu-setting"))}),this.attachShadow({mode:"open"})}async connectedCallback(){g(this,n,this.getAttribute("connector-url")||"http://localhost:3002"),f(this,s,c).call(this),await f(this,s,o).call(this)}}n=new WeakMap,i=new WeakMap,a=new WeakMap,s=new WeakSet,o=async function(){this.$textarea=this.shadowRoot.querySelector("#q"),this.$settings=this.shadowRoot.querySelector("nine-ai-settings"),this.shadowRoot.querySelector(".expand-icon").addEventListener("click",v(this,l)),this.shadowRoot.querySelector(".collapse-icon").addEventListener("click",v(this,l)),this.shadowRoot.querySelectorAll(".menu-icon").forEach(t=>t.addEventListener("click",v(this,d))),this.$textarea.addEventListener("keypress",t=>{if("Enter"===t.key&&!t.shiftKey){t.preventDefault();const e=t.target.value.trim();e&&(f(this,s,r).call(this,e),t.target.value="")}});const t=this.getAttribute("route-url");if(t)try{const n=await fetch(t);g(this,a,await n.json()),e.trace.log("✅ 현재 프로젝트 경로 분석 완료",v(this,a))}catch(n){e.trace.error("❌ 경로 로드 실패:",n.message)}},c=function(){const t=this.getAttribute("placeholder")||"나에게 무엇이든 물어봐...",e=this.getAttribute("css-path"),n=e?`@import "${e}";`:"";this.shadowRoot.innerHTML=`\n <style>\n @import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@0.1.6/dist/css/nine-fab.css";\n ${n}\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="${t}"></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 `},r=async function(t){const i=this.shadowRoot.getElementById("status-tag"),s=Array.from(this.shadowRoot.querySelectorAll('input[name="gen_target"]:checked')).map(t=>t.value);try{i.textContent="⚙️ 공정 분석 중...",e.trace.log(`🚀 Fab 명령 실행: "${t}" [대상: ${s.join(", ")}]`);const o=await fetch(`${v(this,n)}/api/fab/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({command:t,targets:s,projectType:"spring-react",currentRoutes:v(this,a)})}),c=await o.json();if(!c.success)throw new Error(c.error||"생성 실패");e.trace.log("✅ 소스 생성 완료",c.files),i.textContent="✅ 파일 주입 완료",e.nine.alert("소스가 성공적으로 생성되었습니다.").rgb(),this.dispatchEvent(new CustomEvent("nine-fab-completed",{detail:c,bubbles:!0,composed:!0}))}catch(o){e.trace.error("Fab 공정 실패:",o),i.textContent="❌ 에러 발생",e.nine.alert(o.message).rgb().shake()}},l=new WeakMap,d=new WeakMap,e.trace.init("nine-fab","#FF5722"),customElements.get("nine-chat")||customElements.define("nine-chat",m);const b={version:"0.1.6",init:t=>{e.trace.log("🛠️ Nine-Fab Engine initialized",t)}};t.NineChat=m,t.NineFab=b,t.default=b,Object.defineProperties(t,{__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';\nimport { TipPopup } from '@nine-lab/nine-ai';\n\nexport class NineChat extends HTMLElement {\n #connectorUrl = '';\n #tipPopup = null;\n #routes = [];\n\n static {\n // Fab 전용 로깅 컬러로 변경 (색상 차별화)\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 // [중요] Fab 엔진은 route-url을 통해 현재 등록된 전체 메뉴를 파악하여\n // AI가 중복된 경로를 만들지 않게 하거나, 기존 소스를 참고하게 합니다.\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\n const customPath = this.getAttribute(\"css-path\");\n const customImport = customPath ? `@import \"${customPath}\";` : \"\";\n\n this.shadowRoot.innerHTML = `\n <style>\n /* Fab 전용 스타일 (nomenu와 차별화 추천) */\n @import \"https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_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\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=\"mybatis\" value=\"Y\" 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=\"service\" value=\"Y\" 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=\"controller\" value=\"Y\" 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=\"javascript\" value=\"Y\" 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 name=\"ask\" id=\"q\" name=\"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\n\t\t\t<div class=\"expand-icon\"></div>\n `;\n\n /**\n if (customElements.get(\"nine-ai-tip-popup\")) {\n this.#tipPopup = document.createElement('nine-ai-tip-popup');\n this.shadowRoot.appendChild(this.#tipPopup);\n }\n\n const input = this.shadowRoot.querySelector('input');\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n const command = e.target.value.trim();\n if (command) {\n this.#handleFabCommand(command); // 내비게이션 대신 생성 명령 호출\n e.target.value = '';\n }\n }\n }); */\n }\n\n /**\n * [핵심] 소스 코드 생성 및 파일 주입 요청\n */\n async #handleFabCommand(command) {\n const statusEl = this.shadowRoot.getElementById('status');\n try {\n statusEl.textContent = \"AI가 설계를 분석 중입니다...\";\n await this.#tipPopup?.popup();\n\n trace.log(`🚀 소스 생성 명령 전송: \"${command}\"`);\n\n // [변경] nomenu/go -> fab/generate\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 projectType: 'spring-react', // 템플릿 정보\n currentRoutes: this.#routes // 기존 경로 정보 (참고용)\n })\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error || \"소스 생성 중 오류가 발생했습니다.\");\n }\n\n trace.log(\"✅ 소스 생성 및 파일 주입 완료!\", result.files);\n statusEl.textContent = \"✅ 생성 완료! 잠시 후 화면이 갱신됩니다.\";\n\n nine.alert(\"소스 생성이 완료되었습니다!\").rgb();\n\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 } finally {\n this.#tipPopup?.close();\n }\n }\n}\n\n// 1. 웹컴포넌트 등록\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","attachShadow","mode","connectedCallback","__privateSet","getAttribute","__privateMethod","render_fn","call","init_fn","WeakMap","WeakSet","async","routeUrl","res","fetch","json","trace","log","__privateGet","err","error","message","placeholder","customPath","customImport","shadowRoot","innerHTML","init","customElements","get","define","NineFab","version","config"],"mappings":"8tBAGO,MAAMA,UAAiBC,YAU1B,WAAAC,GACIC,QAXDC,EAAAC,KAAAC,GACHF,EAAAC,KAAAE,EAAgB,IAChBH,EAAAC,KAAAG,EAAY,MACZJ,EAAAC,KAAAI,EAAU,IASNJ,KAAKK,aAAa,CAAEC,KAAM,QAC9B,CAEA,uBAAMC,GACFC,EAAAR,KAAKE,EAAgBF,KAAKS,aAAa,kBAAoB,yBAC3DC,EAAAV,KAAKC,EAAAU,GAALC,KAAAZ,YACMU,OAAKT,EAAAY,GAALD,KAAAZ,KACV,EAlBAE,EAAA,IAAAY,QACAX,EAAA,IAAAW,QACAV,EAAA,IAAAU,QAHGb,EAAA,IAAAc,QAqBGF,EAAAG,iBAGF,MAAMC,EAAWjB,KAAKS,aAAa,aACnC,GAAIQ,EACA,IACI,MAAMC,QAAYC,MAAMF,GACxBT,EAAAR,KAAKI,QAAgBc,EAAIE,QACzBC,EAAAA,MAAMC,IAAI,qBAAsBC,EAAAvB,KAAKI,GACzC,OAASoB,GACLH,EAAAA,MAAMI,MAAM,cAAeD,EAAIE,QACnC,CAER,EAEAf,EAAA,WACI,MAAMgB,EAAc3B,KAAKS,aAAa,gBAAkB,kBAElDmB,EAAa5B,KAAKS,aAAa,YAC/BoB,EAAeD,EAAa,YAAYA,MAAiB,GAE/D5B,KAAK8B,WAAWC,UAAY,qMAIlBF,2sCA8BiDF,oXA8B/D,EAnGIN,QAAMW,KAAK,WAAY,WAwJ1BC,eAAeC,IAAI,cACpBD,eAAeE,OAAO,YAAaxC,GC7JhC,MAAMyC,EAAU,CACnBC,QAAkD,QAClDL,KAAOM,IACHjB,QAAMC,IAAI,kCAAmCgB"}
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { nine, trace } from '@nine-lab/nine-util';
|
|
2
|
-
import { TipPopup } from '@nine-lab/nine-ai';
|
|
2
|
+
// import { TipPopup } from '@nine-lab/nine-ai'; // 필요시 주석 해제
|
|
3
3
|
|
|
4
4
|
export class NineChat extends HTMLElement {
|
|
5
5
|
#connectorUrl = '';
|
|
@@ -7,7 +7,6 @@ export class NineChat extends HTMLElement {
|
|
|
7
7
|
#routes = [];
|
|
8
8
|
|
|
9
9
|
static {
|
|
10
|
-
// Fab 전용 로깅 컬러로 변경 (색상 차별화)
|
|
11
10
|
trace.init("nine-fab", "#FF5722");
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -23,8 +22,28 @@ export class NineChat extends HTMLElement {
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
async #init() {
|
|
26
|
-
//
|
|
27
|
-
|
|
25
|
+
// UI 요소 참조
|
|
26
|
+
this.$textarea = this.shadowRoot.querySelector('#q');
|
|
27
|
+
this.$settings = this.shadowRoot.querySelector('nine-ai-settings');
|
|
28
|
+
|
|
29
|
+
// 아이콘 이벤트 연결
|
|
30
|
+
this.shadowRoot.querySelector(".expand-icon").addEventListener("click", this.#toggleCollapseHandler);
|
|
31
|
+
this.shadowRoot.querySelector(".collapse-icon").addEventListener("click", this.#toggleCollapseHandler);
|
|
32
|
+
this.shadowRoot.querySelectorAll(".menu-icon").forEach(el => el.addEventListener("click", this.#menuClickHandler));
|
|
33
|
+
|
|
34
|
+
// [핵심] 텍스트 영역 엔터 이벤트 (Command 전송)
|
|
35
|
+
this.$textarea.addEventListener('keypress', (e) => {
|
|
36
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
const command = e.target.value.trim();
|
|
39
|
+
if (command) {
|
|
40
|
+
this.#handleFabCommand(command);
|
|
41
|
+
e.target.value = '';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 기존 경로 정보 로드
|
|
28
47
|
const routeUrl = this.getAttribute('route-url');
|
|
29
48
|
if (routeUrl) {
|
|
30
49
|
try {
|
|
@@ -39,14 +58,13 @@ export class NineChat extends HTMLElement {
|
|
|
39
58
|
|
|
40
59
|
#render() {
|
|
41
60
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
42
|
-
|
|
43
61
|
const customPath = this.getAttribute("css-path");
|
|
44
62
|
const customImport = customPath ? `@import "${customPath}";` : "";
|
|
63
|
+
const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'latest';
|
|
45
64
|
|
|
46
65
|
this.shadowRoot.innerHTML = `
|
|
47
66
|
<style>
|
|
48
|
-
|
|
49
|
-
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${__APP_VERSION__}/dist/css/nine-fab.css";
|
|
67
|
+
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-fab@${version}/dist/css/nine-fab.css";
|
|
50
68
|
${customImport}
|
|
51
69
|
</style>
|
|
52
70
|
|
|
@@ -56,28 +74,29 @@ export class NineChat extends HTMLElement {
|
|
|
56
74
|
<div class="container">
|
|
57
75
|
<div class="head">
|
|
58
76
|
<div class="logo"><span></span></div>
|
|
77
|
+
<div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">Fab Engine Ready</div>
|
|
59
78
|
</div>
|
|
60
79
|
<nx-ai-chat></nx-ai-chat>
|
|
61
80
|
<div class="foot">
|
|
62
81
|
<div class="apply-src">
|
|
63
82
|
<div>
|
|
64
|
-
<input type="checkbox" id="mybatis" name="
|
|
83
|
+
<input type="checkbox" id="mybatis" name="gen_target" value="MyBatis" checked />
|
|
65
84
|
<label for="mybatis">MyBatis</label>
|
|
66
85
|
</div>
|
|
67
86
|
<div>
|
|
68
|
-
<input type="checkbox" id="service" name="
|
|
87
|
+
<input type="checkbox" id="service" name="gen_target" value="Service" checked />
|
|
69
88
|
<label for="service">Service</label>
|
|
70
89
|
</div>
|
|
71
90
|
<div>
|
|
72
|
-
<input type="checkbox" id="controller" name="
|
|
91
|
+
<input type="checkbox" id="controller" name="gen_target" value="Controller" checked />
|
|
73
92
|
<label for="controller">Controller</label>
|
|
74
93
|
</div>
|
|
75
94
|
<div>
|
|
76
|
-
<input type="checkbox" id="javascript" name="
|
|
95
|
+
<input type="checkbox" id="javascript" name="gen_target" value="JavaScript" checked />
|
|
77
96
|
<label for="javascript">JavaScript</label>
|
|
78
97
|
</div>
|
|
79
98
|
</div>
|
|
80
|
-
<textarea
|
|
99
|
+
<textarea id="q" rows="4" placeholder="${placeholder}"></textarea>
|
|
81
100
|
</div>
|
|
82
101
|
</div>
|
|
83
102
|
<div class="menu">
|
|
@@ -87,62 +106,40 @@ export class NineChat extends HTMLElement {
|
|
|
87
106
|
<div class="menu-icon menu-setting"></div>
|
|
88
107
|
</div>
|
|
89
108
|
</div>
|
|
90
|
-
|
|
91
109
|
<div class="expand-icon"></div>
|
|
92
110
|
`;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
if (customElements.get("nine-ai-tip-popup")) {
|
|
96
|
-
this.#tipPopup = document.createElement('nine-ai-tip-popup');
|
|
97
|
-
this.shadowRoot.appendChild(this.#tipPopup);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const input = this.shadowRoot.querySelector('input');
|
|
101
|
-
input.addEventListener('keypress', (e) => {
|
|
102
|
-
if (e.key === 'Enter') {
|
|
103
|
-
const command = e.target.value.trim();
|
|
104
|
-
if (command) {
|
|
105
|
-
this.#handleFabCommand(command); // 내비게이션 대신 생성 명령 호출
|
|
106
|
-
e.target.value = '';
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}); */
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
/**
|
|
113
|
-
* [핵심] 소스 코드 생성 및 파일 주입 요청
|
|
114
|
-
*/
|
|
115
113
|
async #handleFabCommand(command) {
|
|
116
|
-
const statusEl = this.shadowRoot.getElementById('status');
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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);
|
|
120
119
|
|
|
121
|
-
|
|
120
|
+
try {
|
|
121
|
+
statusEl.textContent = "⚙️ 공정 분석 중...";
|
|
122
|
+
trace.log(`🚀 Fab 명령 실행: "${command}" [대상: ${targets.join(', ')}]`);
|
|
122
123
|
|
|
123
|
-
// [변경] nomenu/go -> fab/generate
|
|
124
124
|
const response = await fetch(`${this.#connectorUrl}/api/fab/generate`, {
|
|
125
125
|
method: 'POST',
|
|
126
126
|
headers: { 'Content-Type': 'application/json' },
|
|
127
127
|
body: JSON.stringify({
|
|
128
128
|
command,
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
targets, // MyBatis, Service 등 선택된 항목 전송
|
|
130
|
+
projectType: 'spring-react',
|
|
131
|
+
currentRoutes: this.#routes
|
|
131
132
|
})
|
|
132
133
|
});
|
|
133
134
|
|
|
134
135
|
const result = await response.json();
|
|
135
136
|
|
|
136
|
-
if (!result.success)
|
|
137
|
-
throw new Error(result.error || "소스 생성 중 오류가 발생했습니다.");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
trace.log("✅ 소스 생성 및 파일 주입 완료!", result.files);
|
|
141
|
-
statusEl.textContent = "✅ 생성 완료! 잠시 후 화면이 갱신됩니다.";
|
|
137
|
+
if (!result.success) throw new Error(result.error || "생성 실패");
|
|
142
138
|
|
|
143
|
-
|
|
139
|
+
trace.log("✅ 소스 생성 완료", result.files);
|
|
140
|
+
statusEl.textContent = "✅ 파일 주입 완료";
|
|
141
|
+
nine.alert("소스가 성공적으로 생성되었습니다.").rgb();
|
|
144
142
|
|
|
145
|
-
// [알림] 생성 성공 시 프레임워크에 알림을 줘서 메뉴를 새로고침하게 함
|
|
146
143
|
this.dispatchEvent(new CustomEvent('nine-fab-completed', {
|
|
147
144
|
detail: result,
|
|
148
145
|
bubbles: true,
|
|
@@ -151,15 +148,27 @@ export class NineChat extends HTMLElement {
|
|
|
151
148
|
|
|
152
149
|
} catch (error) {
|
|
153
150
|
trace.error("Fab 공정 실패:", error);
|
|
154
|
-
statusEl.textContent = "❌
|
|
151
|
+
statusEl.textContent = "❌ 에러 발생";
|
|
155
152
|
nine.alert(error.message).rgb().shake();
|
|
156
|
-
} finally {
|
|
157
|
-
this.#tipPopup?.close();
|
|
158
153
|
}
|
|
159
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
|
+
};
|
|
160
170
|
}
|
|
161
171
|
|
|
162
|
-
// 1. 웹컴포넌트 등록
|
|
163
172
|
if (!customElements.get('nine-chat')) {
|
|
164
173
|
customElements.define('nine-chat', NineChat);
|
|
165
174
|
}
|