isaikr 0.0.1
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/README.md +35 -0
- package/cdn/api.js +19 -0
- package/cdn/character.js +254 -0
- package/cdn/chat.js +33 -0
- package/cdn/code-editor.js +1131 -0
- package/cdn/community-compose.js +270 -0
- package/cdn/games/2048/index.html +12 -0
- package/cdn/games/breakout/index.html +13 -0
- package/cdn/games/clicker/index.html +26 -0
- package/cdn/games/flappy/index.html +11 -0
- package/cdn/games/memory/index.html +34 -0
- package/cdn/games/pong/index.html +13 -0
- package/cdn/games/reaction/index.html +38 -0
- package/cdn/games/runner/index.html +11 -0
- package/cdn/games/snake/index.html +11 -0
- package/cdn/games/tetris/index.html +14 -0
- package/cdn/games/whack/index.html +8 -0
- package/cdn/go.js +126 -0
- package/cdn/go2.js +127 -0
- package/cdn/header3_behavior.js +1167 -0
- package/cdn/header3_layout.js +1004 -0
- package/cdn/header3_layout.js.bak +1004 -0
- package/cdn/header3_style.css +3524 -0
- package/cdn/header3_style.css.bak +3514 -0
- package/cdn/lang.js +198 -0
- package/cdn/loading.js +143 -0
- package/cdn/loading2.js +144 -0
- package/cdn/local-model.js +2941 -0
- package/cdn/main.js +4 -0
- package/cdn/main_asset.js +1849 -0
- package/cdn/main_asset.js.bak +6999 -0
- package/cdn/main_index.css +287 -0
- package/cdn/re_board3.css +733 -0
- package/cdn/re_board3.js +734 -0
- package/cdn/re_chat_tts.js +652 -0
- package/cdn/re_local_runtime.js +2246 -0
- package/cdn/re_local_runtime.js.bak +2246 -0
- package/cdn/re_share.js +577 -0
- package/cdn/re_voice.js +542 -0
- package/cdn/utils.js +36 -0
- package/cdn/view.js +321 -0
- package/header3_behavior.js +804 -0
- package/header3_layout.js +998 -0
- package/header3_style.css +2740 -0
- package/index.js +0 -0
- package/lang.js +179 -0
- package/main_asset.js +2416 -0
- package/main_index.css +274 -0
- package/package.json +14 -0
- package/re_chat_tts.js +1419 -0
- package/re_voice.js +430 -0
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
// extracted from main_asset.js: code editor helpers
|
|
2
|
+
function parseMarkdownLocal(text, isFinished = false) {
|
|
3
|
+
window.extractedCodes =[];
|
|
4
|
+
|
|
5
|
+
let mainContent = text;
|
|
6
|
+
|
|
7
|
+
let html = "";
|
|
8
|
+
|
|
9
|
+
let processedMain = mainContent.replace(/</g, "<").replace(/>/g, ">");
|
|
10
|
+
|
|
11
|
+
const blocks =[];
|
|
12
|
+
let counter = 0;
|
|
13
|
+
|
|
14
|
+
function createBlockUI(lang, code, isStreaming) {
|
|
15
|
+
counter++;
|
|
16
|
+
lang = lang || "text";
|
|
17
|
+
|
|
18
|
+
let cleanCode = code.replace(/</g, "<").replace(/>/g, ">");
|
|
19
|
+
window.extractedCodes.push({
|
|
20
|
+
lang: lang,
|
|
21
|
+
content: cleanCode,
|
|
22
|
+
name: `File ${window.extractedCodes.length + 1} (${lang})`
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const streamingClass = isStreaming ? " is-streaming" : "";
|
|
26
|
+
const languageLabel = lang ? `<div class="chat-code-label">${lang}</div>` : "";
|
|
27
|
+
const uiBlock = `${languageLabel}<pre class="chat-code-block${streamingClass}"><code>${code}</code></pre>`;
|
|
28
|
+
blocks.push(uiBlock);
|
|
29
|
+
return "###CODE_BLOCK_" + (blocks.length - 1) + "###";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
processedMain = processedMain.replace(/```([a-zA-Z0-9_\-\+]*)\n([\s\S]*?)```/g, (match, lang, code) => {
|
|
33
|
+
return createBlockUI(lang, code, false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
processedMain = processedMain.replace(/```([a-zA-Z0-9_\-\+]*)[ \t]*\n?([\s\S]*)$/g, (match, lang, code) => {
|
|
37
|
+
return createBlockUI(lang, code, !isFinished);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
processedMain = processedMain.replace(/`([^`]+)`/g, (match, inlineCode) => {
|
|
41
|
+
return `<code class="bg-black/[0.06] px-1.5 py-0.5 rounded text-[#111827] font-bold font-mono text-sm border border-black/[0.08]">${inlineCode}</code>`;
|
|
42
|
+
})
|
|
43
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong class="font-semibold text-[#111827]">$1</strong>')
|
|
44
|
+
.replace(/^###\s+(.*)$/gm, '<h3 class="text-lg font-bold text-[#111827] mb-2 mt-4">$1</h3>')
|
|
45
|
+
.replace(/^##\s+(.*)$/gm, '<b class="font-bold text-[#111827] text-base mt-3 mb-1 block">$1</b>')
|
|
46
|
+
.replace(/^[\-\*]\s+(.*)$/gm, '<li class="ml-4 list-disc text-sm">$1</li>')
|
|
47
|
+
.replace(/\n/g, "<br>");
|
|
48
|
+
|
|
49
|
+
blocks.forEach((blockHtml, index) => {
|
|
50
|
+
processedMain = processedMain.replace("###CODE_BLOCK_" + index + "###", blockHtml);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return html + processedMain;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function escapeHtmlForBubbleLocal(value) {
|
|
57
|
+
return String(value ?? "")
|
|
58
|
+
.replace(/&/g, "&")
|
|
59
|
+
.replace(/</g, "<")
|
|
60
|
+
.replace(/>/g, ">");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function looksLikeCodeOrHtmlResponseLocal(value) {
|
|
64
|
+
const text = String(value ?? "").trim();
|
|
65
|
+
if (!text) return false;
|
|
66
|
+
return /```/.test(text)
|
|
67
|
+
|| /(?:^|\n)\s*(<!doctype html|<html\b|<head\b|<body\b|<script\b|<style\b|<canvas\b|<\?php|const\b|let\b|var\b|function\b|class\b|import\b|export\b)/i.test(text);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function detectCodeBlockLanguageLocal(value) {
|
|
71
|
+
const text = String(value ?? "").trimStart();
|
|
72
|
+
if (/^<!doctype html|^<html\b|^<head\b|^<body\b|^<script\b|^<style\b|^<canvas\b/i.test(text)) return "html";
|
|
73
|
+
if (/^<\?php/i.test(text)) return "php";
|
|
74
|
+
if (/^(const|let|var|function|class|import|export)\b/.test(text)) return "javascript";
|
|
75
|
+
return "text";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeCodeLikeResponseForBubbleLocal(value) {
|
|
79
|
+
const text = String(value ?? "").trim();
|
|
80
|
+
if (!text) return "";
|
|
81
|
+
if (/```/.test(text)) return text;
|
|
82
|
+
const startMatch = text.match(/(?:^|\n)\s*(<!doctype html|<html\b|<head\b|<body\b|<script\b|<style\b|<canvas\b|<\?php|const\b|let\b|var\b|function\b|class\b|import\b|export\b)/i);
|
|
83
|
+
if (!startMatch) return text;
|
|
84
|
+
|
|
85
|
+
const startIndex = text.search(/(?:^|\n)\s*(<!doctype html|<html\b|<head\b|<body\b|<script\b|<style\b|<canvas\b|<\?php|const\b|let\b|var\b|function\b|class\b|import\b|export\b)/i);
|
|
86
|
+
if (startIndex < 0) return text;
|
|
87
|
+
|
|
88
|
+
const prefix = text.slice(0, startIndex).trimEnd();
|
|
89
|
+
const codeText = text.slice(startIndex).trim();
|
|
90
|
+
const lang = detectCodeBlockLanguageLocal(codeText);
|
|
91
|
+
const fenced = "```" + lang + "\n" + codeText + "\n```";
|
|
92
|
+
return prefix ? (prefix + "\n\n" + fenced) : fenced;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function renderAssistantBubbleContentLocal(text, isFinished = false, options = {}) {
|
|
96
|
+
const textValue = String(text ?? "");
|
|
97
|
+
const normalizedText = (!!(options && options.forcePlainText) || looksLikeCodeOrHtmlResponseLocal(textValue))
|
|
98
|
+
? normalizeCodeLikeResponseForBubbleLocal(textValue)
|
|
99
|
+
: normalizeAssistantSlashLineBreaks(textValue);
|
|
100
|
+
return parseMarkdownLocal(normalizedText, isFinished);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function setAssistantBubbleBodyLocal(bubble, text, options = {}) {
|
|
104
|
+
if (!bubble) return;
|
|
105
|
+
const normalizedText = normalizeAssistantSlashLineBreaks(String(text ?? ""));
|
|
106
|
+
const rawText = trimRepeatedDisplayText(normalizedText);
|
|
107
|
+
const plainText = extractPlainTextLocal(rawText);
|
|
108
|
+
if (!plainText) {
|
|
109
|
+
const locale = String(document.documentElement.lang || navigator.language || "en").toLowerCase();
|
|
110
|
+
const fallback = locale.startsWith("ko")
|
|
111
|
+
? "응답을 불러오지 못했습니다."
|
|
112
|
+
: (locale.startsWith("ja") ? "応答を読み込めませんでした。" : "I could not load a response.");
|
|
113
|
+
bubble.textContent = fallback;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
bubble.style.whiteSpace = "";
|
|
117
|
+
bubble.style.wordBreak = "";
|
|
118
|
+
bubble.style.fontFamily = "";
|
|
119
|
+
bubble.style.lineHeight = "";
|
|
120
|
+
bubble.innerHTML = renderAssistantBubbleContentLocal(rawText, !!options.isFinished, options);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
window.currentImagePrompt = "";
|
|
124
|
+
|
|
125
|
+
window.openPreview = function(url) {
|
|
126
|
+
const container = document.getElementById("img-preview-container");
|
|
127
|
+
const img = document.getElementById("preview-img");
|
|
128
|
+
const appContainer = document.getElementById("app-container");
|
|
129
|
+
const centerApp = document.getElementById("center-app-name");
|
|
130
|
+
|
|
131
|
+
if (container && img) {
|
|
132
|
+
if (url.startsWith("blob:") || url.startsWith("data:") || url.startsWith("http")) {
|
|
133
|
+
img.src = url;
|
|
134
|
+
} else {
|
|
135
|
+
img.src = `data:image/jpeg;base64,${url}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
container.style.setProperty("display", "block", "important");
|
|
139
|
+
container.classList.add("active");
|
|
140
|
+
|
|
141
|
+
if (appContainer) appContainer.style.setProperty("display", "none", "important");
|
|
142
|
+
if (centerApp) centerApp.style.setProperty("display", "none", "important");
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
window.closePreview = function() {
|
|
147
|
+
const container = document.getElementById("img-preview-container");
|
|
148
|
+
const appContainer = document.getElementById("app-container");
|
|
149
|
+
const centerApp = document.getElementById("center-app-name");
|
|
150
|
+
|
|
151
|
+
if (container) {
|
|
152
|
+
container.classList.remove("active");
|
|
153
|
+
container.style.setProperty("display", "none", "important");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (appContainer) {
|
|
157
|
+
appContainer.style.opacity = "1";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (centerApp) {
|
|
161
|
+
centerApp.style.setProperty("display", "block", "important");
|
|
162
|
+
centerApp.style.opacity = "1";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const currentUrl = new URL(window.location.href);
|
|
166
|
+
if (currentUrl.searchParams.get("v")) {
|
|
167
|
+
currentUrl.searchParams.delete("v");
|
|
168
|
+
window.history.pushState({}, "", currentUrl.pathname);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
window.extractedCodes =[];
|
|
173
|
+
|
|
174
|
+
window.openFullEditor = function(blockId) {
|
|
175
|
+
const el = document.getElementById(blockId);
|
|
176
|
+
if (el) {
|
|
177
|
+
const text = el.innerText;
|
|
178
|
+
const codeEditor = document.getElementById("code-editor");
|
|
179
|
+
const rightPanel = document.getElementById("right-panel");
|
|
180
|
+
const codeTabs = document.getElementById("code-tabs");
|
|
181
|
+
const rightPanelOriginAnchor = document.getElementById("right-panel-origin-anchor");
|
|
182
|
+
const desktopCodePanelHost = document.getElementById("desktop-code-panel-host");
|
|
183
|
+
const vw = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
184
|
+
const mobileCodeMode = vw <= 900;
|
|
185
|
+
const desktopCodeMode = vw > 900;
|
|
186
|
+
const codePanelGridColumn = vw <= 1180 ? "1 / span 2" : "1 / span 4";
|
|
187
|
+
const codePanelGridRow = vw <= 900 ? "2" : (vw <= 1180 ? "3" : "2");
|
|
188
|
+
const codePanelMinHeight = vw <= 900 ? "240px" : "320px";
|
|
189
|
+
const codePanelMaxHeight = vw <= 900 ? "360px" : "520px";
|
|
190
|
+
if (codeEditor) {
|
|
191
|
+
codeEditor.value = text;
|
|
192
|
+
scheduleCodeBlockActionsRender();
|
|
193
|
+
}
|
|
194
|
+
if (codeTabs) {
|
|
195
|
+
codeTabs.innerHTML = '<button type="button" class="code-tab-btn active" title="Current Code"><span class="code-tab-icon"><i class="ri-code-block"></i></span><span class="code-tab-label">CODE</span></button>';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (typeof window.syncInlineCodePanel === "function") {
|
|
199
|
+
window.syncInlineCodePanel("code");
|
|
200
|
+
} else {
|
|
201
|
+
if (rightPanel) {
|
|
202
|
+
if (document.body) document.body.setAttribute("data-ui-mode", "code");
|
|
203
|
+
document.body.classList.remove("desktop-code-stage");
|
|
204
|
+
document.body.classList.toggle("mode-code", mobileCodeMode);
|
|
205
|
+
document.body.classList.toggle("mobile-code-stacked", mobileCodeMode);
|
|
206
|
+
document.body.classList.toggle("desktop-code-open", desktopCodeMode);
|
|
207
|
+
document.body.classList.toggle("desktop-code-panel-mounted", desktopCodeMode);
|
|
208
|
+
rightPanel.classList.toggle("mobile-active", mobileCodeMode);
|
|
209
|
+
rightPanel.classList.remove("hidden");
|
|
210
|
+
if (rightPanelOriginAnchor && rightPanelOriginAnchor.parentElement && desktopCodePanelHost) {
|
|
211
|
+
if (desktopCodeMode) {
|
|
212
|
+
desktopCodePanelHost.style.display = "block";
|
|
213
|
+
desktopCodePanelHost.style.width = "100%";
|
|
214
|
+
desktopCodePanelHost.style.minWidth = "0";
|
|
215
|
+
desktopCodePanelHost.style.minHeight = codePanelMinHeight;
|
|
216
|
+
if (rightPanel.parentElement !== desktopCodePanelHost) {
|
|
217
|
+
desktopCodePanelHost.appendChild(rightPanel);
|
|
218
|
+
}
|
|
219
|
+
} else if (rightPanel.parentElement !== rightPanelOriginAnchor.parentElement) {
|
|
220
|
+
rightPanelOriginAnchor.parentElement.insertBefore(rightPanel, rightPanelOriginAnchor.nextSibling);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
rightPanel.style.display = "flex";
|
|
224
|
+
rightPanel.style.visibility = "visible";
|
|
225
|
+
rightPanel.style.opacity = "1";
|
|
226
|
+
rightPanel.style.width = "100%";
|
|
227
|
+
rightPanel.style.minWidth = "0";
|
|
228
|
+
rightPanel.style.maxWidth = "100%";
|
|
229
|
+
if (mobileCodeMode) {
|
|
230
|
+
rightPanel.style.gridRow = codePanelGridRow;
|
|
231
|
+
rightPanel.style.gridColumn = codePanelGridColumn;
|
|
232
|
+
} else {
|
|
233
|
+
rightPanel.style.removeProperty("grid-row");
|
|
234
|
+
rightPanel.style.removeProperty("grid-column");
|
|
235
|
+
}
|
|
236
|
+
rightPanel.style.height = "auto";
|
|
237
|
+
rightPanel.style.minHeight = codePanelMinHeight;
|
|
238
|
+
rightPanel.style.maxHeight = codePanelMaxHeight;
|
|
239
|
+
rightPanel.style.overflow = "hidden";
|
|
240
|
+
rightPanel.style.borderTop = "1px solid rgba(255, 255, 255, 0.06)";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (rightPanel) {
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
try {
|
|
247
|
+
(desktopCodeMode && desktopCodePanelHost ? desktopCodePanelHost : rightPanel).scrollIntoView({ behavior: "smooth", block: "start" });
|
|
248
|
+
} catch (error) {}
|
|
249
|
+
}, vw <= 900 ? 90 : 20);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
window.closeFullEditor = function() {
|
|
255
|
+
const rightPanel = document.getElementById("right-panel");
|
|
256
|
+
if (rightPanel) rightPanel.classList.remove("mobile-active");
|
|
257
|
+
if (document.body) document.body.classList.remove("mobile-code-stacked");
|
|
258
|
+
if (typeof currentMode !== "undefined" && currentMode === "code" && typeof setMode === "function") {
|
|
259
|
+
setMode("chat");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
document.body.classList.remove("mode-code");
|
|
263
|
+
document.body.classList.remove("mobile-code-stacked");
|
|
264
|
+
document.body.classList.remove("desktop-code-stage");
|
|
265
|
+
if (document.body) document.body.setAttribute("data-ui-mode", "chat");
|
|
266
|
+
document.body.classList.remove("desktop-code-open");
|
|
267
|
+
document.body.classList.remove("desktop-code-panel-mounted");
|
|
268
|
+
if (rightPanel) {
|
|
269
|
+
rightPanel.classList.remove("mobile-active");
|
|
270
|
+
rightPanel.style.removeProperty("display");
|
|
271
|
+
rightPanel.style.removeProperty("visibility");
|
|
272
|
+
rightPanel.style.removeProperty("opacity");
|
|
273
|
+
rightPanel.style.removeProperty("width");
|
|
274
|
+
rightPanel.style.removeProperty("min-width");
|
|
275
|
+
rightPanel.style.removeProperty("max-width");
|
|
276
|
+
rightPanel.style.removeProperty("grid-row");
|
|
277
|
+
rightPanel.style.removeProperty("grid-column");
|
|
278
|
+
rightPanel.style.removeProperty("height");
|
|
279
|
+
rightPanel.style.removeProperty("min-height");
|
|
280
|
+
rightPanel.style.removeProperty("max-height");
|
|
281
|
+
rightPanel.style.removeProperty("overflow");
|
|
282
|
+
rightPanel.style.removeProperty("border-top");
|
|
283
|
+
rightPanel.classList.add("hidden");
|
|
284
|
+
}
|
|
285
|
+
const rightPanelOriginAnchor = document.getElementById("right-panel-origin-anchor");
|
|
286
|
+
const desktopCodePanelHost = document.getElementById("desktop-code-panel-host");
|
|
287
|
+
if (rightPanel && desktopCodePanelHost) {
|
|
288
|
+
desktopCodePanelHost.style.display = "none";
|
|
289
|
+
desktopCodePanelHost.style.removeProperty("min-height");
|
|
290
|
+
desktopCodePanelHost.style.removeProperty("visibility");
|
|
291
|
+
desktopCodePanelHost.style.removeProperty("opacity");
|
|
292
|
+
desktopCodePanelHost.style.removeProperty("overflow");
|
|
293
|
+
}
|
|
294
|
+
if (rightPanel && rightPanelOriginAnchor && rightPanelOriginAnchor.parentElement && rightPanel.parentElement !== rightPanelOriginAnchor.parentElement) {
|
|
295
|
+
rightPanelOriginAnchor.parentElement.insertBefore(rightPanel, rightPanelOriginAnchor.nextSibling);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
let codeFiles =[];
|
|
300
|
+
let activeFileIndex = 0;
|
|
301
|
+
let scopedCodeBlocks =[];
|
|
302
|
+
let structuredCodeBlocksState = [];
|
|
303
|
+
let pendingScopedCodeAttachment = null;
|
|
304
|
+
let codeBlockRenderTimer = null;
|
|
305
|
+
let structuredPreviewTimer = null;
|
|
306
|
+
const codeBlockCollapseState = Object.create(null);
|
|
307
|
+
|
|
308
|
+
function getScopedCodeLocale() {
|
|
309
|
+
const locale = String((window.ISAI_SERVER_I18N && window.ISAI_SERVER_I18N.locale) || window.LANG || document.documentElement.lang || navigator.language || "en").toLowerCase();
|
|
310
|
+
if (locale.startsWith("ko")) return "ko";
|
|
311
|
+
if (locale.startsWith("ja")) return "ja";
|
|
312
|
+
if (locale.startsWith("zh")) return "zh";
|
|
313
|
+
if (locale.startsWith("es")) return "es";
|
|
314
|
+
return "en";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function getScopedCodeText(key) {
|
|
318
|
+
const dict = {
|
|
319
|
+
ko: {
|
|
320
|
+
empty: "코드 블록 없음",
|
|
321
|
+
fullFile: "전체 파일",
|
|
322
|
+
setup: "설정 블록",
|
|
323
|
+
editAsk: "이 블록에서 변경할 내용을 입력하세요.",
|
|
324
|
+
processing: "선택 블록 수정 중...",
|
|
325
|
+
applied: "선택 블록이 업데이트되었습니다.",
|
|
326
|
+
failed: "블록 적용 실패. 더 구체적으로 요청해 주세요.",
|
|
327
|
+
attached: "블록이 입력창에 첨부되었습니다."
|
|
328
|
+
},
|
|
329
|
+
ja: {
|
|
330
|
+
empty: "コードブロックなし",
|
|
331
|
+
fullFile: "ファイル全体",
|
|
332
|
+
setup: "セットアップブロック",
|
|
333
|
+
editAsk: "このブロックで変更したい内容を入力してください。",
|
|
334
|
+
processing: "選択ブロックを編集中...",
|
|
335
|
+
applied: "選択ブロックを更新しました。",
|
|
336
|
+
failed: "ブロック適用に失敗しました。より具体的に入力してください。",
|
|
337
|
+
attached: "ブロックを入力欄に添付しました。"
|
|
338
|
+
},
|
|
339
|
+
zh: {
|
|
340
|
+
empty: "没有代码块",
|
|
341
|
+
fullFile: "整个文件",
|
|
342
|
+
setup: "设置块",
|
|
343
|
+
editAsk: "请输入要修改的块内容。",
|
|
344
|
+
processing: "正在编辑所选代码块...",
|
|
345
|
+
applied: "已更新所选代码块。",
|
|
346
|
+
failed: "应用代码块失败,请提供更具体的修改说明。",
|
|
347
|
+
attached: "已将代码块附加到输入框。"
|
|
348
|
+
},
|
|
349
|
+
es: {
|
|
350
|
+
empty: "No hay bloques de c처digo",
|
|
351
|
+
fullFile: "Archivo completo",
|
|
352
|
+
setup: "Bloque base",
|
|
353
|
+
editAsk: "Escribe qu챕 quieres cambiar en este bloque.",
|
|
354
|
+
processing: "Editando bloque seleccionado...",
|
|
355
|
+
applied: "Bloque actualizado.",
|
|
356
|
+
failed: "No se pudo aplicar el bloque. Describe mejor el cambio.",
|
|
357
|
+
attached: "Bloque adjuntado al campo de entrada."
|
|
358
|
+
},
|
|
359
|
+
en: {
|
|
360
|
+
empty: "No code blocks",
|
|
361
|
+
fullFile: "Whole file",
|
|
362
|
+
setup: "Setup block",
|
|
363
|
+
editAsk: "Describe what to change in this block.",
|
|
364
|
+
processing: "Editing selected block...",
|
|
365
|
+
applied: "Selected block updated.",
|
|
366
|
+
failed: "Block apply failed. Please give a more specific edit request.",
|
|
367
|
+
attached: "Block attached to the input field."
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
const locale = getScopedCodeLocale();
|
|
371
|
+
const pack = dict[locale] || dict.en;
|
|
372
|
+
return pack[key] || dict.en[key] || "";
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function normalizeCodeLanguageKey(value) {
|
|
376
|
+
const key = String(value || "").toLowerCase();
|
|
377
|
+
if (["js", "jsx", "ts", "tsx", "javascript", "typescript", "json"].some((item) => key.includes(item))) return "javascript";
|
|
378
|
+
if (["py", "python"].some((item) => key.includes(item))) return "python";
|
|
379
|
+
if (["php"].some((item) => key.includes(item))) return "php";
|
|
380
|
+
if (["html", "xml", "htm"].some((item) => key.includes(item))) return "html";
|
|
381
|
+
if (["css", "scss", "sass", "less"].some((item) => key.includes(item))) return "css";
|
|
382
|
+
return key || "text";
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function getCodeBlockIconClass(lang = "") {
|
|
386
|
+
const key = normalizeCodeLanguageKey(lang);
|
|
387
|
+
if (key === "html") return "ri-html5-line";
|
|
388
|
+
if (key === "css") return "ri-css3-line";
|
|
389
|
+
if (key === "javascript") return "ri-braces-line";
|
|
390
|
+
if (key === "php") return "ri-code-block";
|
|
391
|
+
if (key === "python") return "ri-terminal-box-line";
|
|
392
|
+
return "ri-file-code-line";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function getActiveCodeContentSnapshot() {
|
|
396
|
+
if (codeFiles && codeFiles[activeFileIndex] && typeof codeFiles[activeFileIndex].content === "string") {
|
|
397
|
+
return String(codeFiles[activeFileIndex].content || "");
|
|
398
|
+
}
|
|
399
|
+
const editor = document.getElementById("code-editor");
|
|
400
|
+
return editor ? String(editor.value || "") : "";
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function buildScopedCodeBlockKey(fileIndex, block) {
|
|
404
|
+
const resolvedFileIndex = Number.isFinite(Number(fileIndex)) ? Number(fileIndex) : activeFileIndex;
|
|
405
|
+
const label = String(block && block.label ? block.label : "").trim();
|
|
406
|
+
const startLine = Number.isFinite(Number(block && block.startLine)) ? Number(block.startLine) : 0;
|
|
407
|
+
const endLine = Number.isFinite(Number(block && block.endLine)) ? Number(block.endLine) : startLine;
|
|
408
|
+
return `${resolvedFileIndex}:${label}:${startLine}:${endLine}`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function parseScopedCodeBlocksFromContent(content, langHint = "") {
|
|
412
|
+
const text = String(content || "");
|
|
413
|
+
if (!text.trim()) return [];
|
|
414
|
+
const lines = text.split("\n");
|
|
415
|
+
const lineOffsets =[];
|
|
416
|
+
let offset = 0;
|
|
417
|
+
for (let i = 0; i < lines.length; i++) {
|
|
418
|
+
lineOffsets.push(offset);
|
|
419
|
+
offset += lines[i].length + 1;
|
|
420
|
+
}
|
|
421
|
+
const lang = normalizeCodeLanguageKey(langHint || detectCodeBlockLanguageLocal(text));
|
|
422
|
+
const starts =[];
|
|
423
|
+
const pushStart = (lineIndex, label) => {
|
|
424
|
+
if (!label) return;
|
|
425
|
+
starts.push({ line: lineIndex, label: String(label).trim() });
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
lines.forEach((line, lineIndex) => {
|
|
429
|
+
const trimmed = String(line || "").trim();
|
|
430
|
+
if (!trimmed) return;
|
|
431
|
+
let match = null;
|
|
432
|
+
if (lang === "javascript" || lang === "php") {
|
|
433
|
+
match = trimmed.match(/^function\s+([A-Za-z0-9_$]+)/);
|
|
434
|
+
if (match) return pushStart(lineIndex, `fn ${match[1]}`);
|
|
435
|
+
match = trimmed.match(/^(?:const|let|var)\s+([A-Za-z0-9_$]+)\s*=\s*(?:async\s*)?\(/);
|
|
436
|
+
if (match) return pushStart(lineIndex, `fn ${match[1]}`);
|
|
437
|
+
match = trimmed.match(/^(?:const|let|var)\s+([A-Za-z0-9_$]+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/);
|
|
438
|
+
if (match) return pushStart(lineIndex, `fn ${match[1]}`);
|
|
439
|
+
match = trimmed.match(/^class\s+([A-Za-z0-9_$]+)/);
|
|
440
|
+
if (match) return pushStart(lineIndex, `class ${match[1]}`);
|
|
441
|
+
} else if (lang === "python") {
|
|
442
|
+
match = trimmed.match(/^def\s+([A-Za-z0-9_]+)/);
|
|
443
|
+
if (match) return pushStart(lineIndex, `def ${match[1]}`);
|
|
444
|
+
match = trimmed.match(/^class\s+([A-Za-z0-9_]+)/);
|
|
445
|
+
if (match) return pushStart(lineIndex, `class ${match[1]}`);
|
|
446
|
+
} else if (lang === "html") {
|
|
447
|
+
match = trimmed.match(/^<(script|style|section|main|header|footer|nav|article|aside|div|form)\b/i);
|
|
448
|
+
if (match) return pushStart(lineIndex, `<${match[1].toLowerCase()}>`);
|
|
449
|
+
} else if (lang === "css") {
|
|
450
|
+
match = trimmed.match(/^(@media[^{]+)\{/i);
|
|
451
|
+
if (match) return pushStart(lineIndex, match[1].trim());
|
|
452
|
+
match = trimmed.match(/^([.#]?[A-Za-z0-9_:-][^{]*)\{/);
|
|
453
|
+
if (match) return pushStart(lineIndex, match[1].trim());
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
if (starts.length === 0) {
|
|
458
|
+
return [{
|
|
459
|
+
index: 0,
|
|
460
|
+
label: getScopedCodeText("fullFile"),
|
|
461
|
+
lang,
|
|
462
|
+
startOffset: 0,
|
|
463
|
+
endOffset: text.length,
|
|
464
|
+
startLine: 0,
|
|
465
|
+
endLine: Math.max(0, lines.length - 1),
|
|
466
|
+
sourceText: text,
|
|
467
|
+
promptText: text.trim()
|
|
468
|
+
}];
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const blocks =[];
|
|
472
|
+
const firstStartLine = Number.isFinite(Number(starts[0] && starts[0].line)) ? Number(starts[0].line) : 0;
|
|
473
|
+
const firstStartOffset = lineOffsets[firstStartLine] || 0;
|
|
474
|
+
const leadingText = text.slice(0, firstStartOffset);
|
|
475
|
+
if (leadingText.trim()) {
|
|
476
|
+
blocks.push({
|
|
477
|
+
index: 0,
|
|
478
|
+
label: getScopedCodeText("setup"),
|
|
479
|
+
lang,
|
|
480
|
+
startOffset: 0,
|
|
481
|
+
endOffset: firstStartOffset,
|
|
482
|
+
startLine: 0,
|
|
483
|
+
endLine: Math.max(0, firstStartLine - 1),
|
|
484
|
+
sourceText: leadingText,
|
|
485
|
+
promptText: leadingText.trim()
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
for (let i = 0; i < starts.length; i++) {
|
|
490
|
+
const startLine = starts[i].line;
|
|
491
|
+
const nextLine = i + 1 < starts.length ? starts[i + 1].line : lines.length;
|
|
492
|
+
const startOffset = lineOffsets[startLine] || 0;
|
|
493
|
+
const endOffset = nextLine < lines.length ? (lineOffsets[nextLine] || text.length) : text.length;
|
|
494
|
+
const sourceText = text.slice(startOffset, endOffset);
|
|
495
|
+
const promptText = sourceText.trim();
|
|
496
|
+
if (!promptText) continue;
|
|
497
|
+
blocks.push({
|
|
498
|
+
index: blocks.length,
|
|
499
|
+
label: starts[i].label || `${getScopedCodeText("fullFile")} ${i + 1}`,
|
|
500
|
+
lang,
|
|
501
|
+
startOffset,
|
|
502
|
+
endOffset,
|
|
503
|
+
startLine,
|
|
504
|
+
endLine: Math.max(startLine, nextLine - 1),
|
|
505
|
+
sourceText,
|
|
506
|
+
promptText
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return blocks.length ? blocks.slice(0, 24).map((block, index) => ({ ...block, index })) : [{
|
|
511
|
+
index: 0,
|
|
512
|
+
label: getScopedCodeText("fullFile"),
|
|
513
|
+
lang,
|
|
514
|
+
startOffset: 0,
|
|
515
|
+
endOffset: text.length,
|
|
516
|
+
startLine: 0,
|
|
517
|
+
endLine: Math.max(0, lines.length - 1),
|
|
518
|
+
sourceText: text,
|
|
519
|
+
promptText: text.trim()
|
|
520
|
+
}];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function createScopedTargetFromBlock(block, fileIndex = activeFileIndex) {
|
|
524
|
+
if (!block || typeof block !== "object") return null;
|
|
525
|
+
const next = {
|
|
526
|
+
fileIndex,
|
|
527
|
+
startOffset: Number.isFinite(Number(block.startOffset)) ? Number(block.startOffset) : 0,
|
|
528
|
+
endOffset: Number.isFinite(Number(block.endOffset)) ? Number(block.endOffset) : 0,
|
|
529
|
+
sourceText: String(block.currentText != null ? block.currentText : (block.sourceText || "")),
|
|
530
|
+
originalBlock: String(block.sourceText || ""),
|
|
531
|
+
promptText: String(block.promptText || block.currentText || block.sourceText || "").trim(),
|
|
532
|
+
label: String(block.label || ""),
|
|
533
|
+
lang: String(block.lang || ""),
|
|
534
|
+
startLine: Number.isFinite(Number(block.startLine)) ? Number(block.startLine) : 0,
|
|
535
|
+
endLine: Number.isFinite(Number(block.endLine)) ? Number(block.endLine) : 0
|
|
536
|
+
};
|
|
537
|
+
next.blockKey = buildScopedCodeBlockKey(fileIndex, next);
|
|
538
|
+
return next;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function syncStructuredCodeTextareaHeight(textarea) {
|
|
542
|
+
if (!textarea) return;
|
|
543
|
+
textarea.style.height = "auto";
|
|
544
|
+
textarea.style.height = `${Math.max(120, textarea.scrollHeight)}px`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function scheduleStructuredPreviewRefresh() {
|
|
548
|
+
if (structuredPreviewTimer) clearTimeout(structuredPreviewTimer);
|
|
549
|
+
structuredPreviewTimer = setTimeout(() => {
|
|
550
|
+
structuredPreviewTimer = null;
|
|
551
|
+
if (typeof window.refreshHtmlPreview === "function" && document.body && document.body.classList.contains("html-preview-active")) {
|
|
552
|
+
try { window.refreshHtmlPreview(); } catch (error) {}
|
|
553
|
+
}
|
|
554
|
+
}, 160);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function syncStructuredCodeToMaster(fileIndex = activeFileIndex) {
|
|
558
|
+
const merged = structuredCodeBlocksState.map((block) => String(block && block.currentText != null ? block.currentText : (block.sourceText || ""))).join("");
|
|
559
|
+
if (codeFiles[fileIndex]) {
|
|
560
|
+
codeFiles[fileIndex].content = merged;
|
|
561
|
+
}
|
|
562
|
+
const editor = document.getElementById("code-editor");
|
|
563
|
+
if (editor && fileIndex === activeFileIndex) {
|
|
564
|
+
editor.value = merged;
|
|
565
|
+
}
|
|
566
|
+
scheduleStructuredPreviewRefresh();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function renderPromptAttachmentRow() {
|
|
570
|
+
const row = document.getElementById("prompt-attachment-row");
|
|
571
|
+
if (!row) return;
|
|
572
|
+
row.innerHTML = "";
|
|
573
|
+
if (!pendingScopedCodeAttachment) {
|
|
574
|
+
row.classList.add("hidden");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const chip = document.createElement("div");
|
|
579
|
+
chip.className = "prompt-attachment-chip";
|
|
580
|
+
|
|
581
|
+
const icon = document.createElement("span");
|
|
582
|
+
icon.className = "prompt-attachment-chip-icon";
|
|
583
|
+
icon.innerHTML = '<i class="ri-attachment-2"></i>';
|
|
584
|
+
chip.appendChild(icon);
|
|
585
|
+
|
|
586
|
+
const label = document.createElement("span");
|
|
587
|
+
label.className = "prompt-attachment-chip-label";
|
|
588
|
+
label.textContent = pendingScopedCodeAttachment.label || getScopedCodeText("fullFile");
|
|
589
|
+
chip.appendChild(label);
|
|
590
|
+
|
|
591
|
+
const removeButton = document.createElement("button");
|
|
592
|
+
removeButton.type = "button";
|
|
593
|
+
removeButton.className = "prompt-attachment-chip-remove";
|
|
594
|
+
removeButton.innerHTML = '<i class="ri-close-line"></i>';
|
|
595
|
+
removeButton.addEventListener("click", (event) => {
|
|
596
|
+
event.preventDefault();
|
|
597
|
+
event.stopPropagation();
|
|
598
|
+
clearPendingScopedCodeAttachment();
|
|
599
|
+
});
|
|
600
|
+
chip.appendChild(removeButton);
|
|
601
|
+
|
|
602
|
+
row.appendChild(chip);
|
|
603
|
+
row.classList.remove("hidden");
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function syncStructuredAttachmentState() {
|
|
607
|
+
const container = document.getElementById("code-structured-editor");
|
|
608
|
+
if (!container) return;
|
|
609
|
+
container.querySelectorAll(".code-structured-card").forEach((card) => {
|
|
610
|
+
const isAttached = !!(pendingScopedCodeAttachment && card.dataset.blockKey === pendingScopedCodeAttachment.blockKey);
|
|
611
|
+
card.classList.toggle("is-attached", isAttached);
|
|
612
|
+
const aiButton = card.querySelector(".code-structured-ai");
|
|
613
|
+
if (aiButton) aiButton.classList.toggle("is-attached", isAttached);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function clearPendingScopedCodeAttachment(options = {}) {
|
|
618
|
+
pendingScopedCodeAttachment = null;
|
|
619
|
+
renderPromptAttachmentRow();
|
|
620
|
+
syncStructuredAttachmentState();
|
|
621
|
+
if (!options.silent) {
|
|
622
|
+
const input = document.getElementById("prompt-input");
|
|
623
|
+
if (input) input.focus();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function resolvePendingScopedCodeAttachment() {
|
|
628
|
+
if (!pendingScopedCodeAttachment) return null;
|
|
629
|
+
const fileIndex = Number.isFinite(Number(pendingScopedCodeAttachment.fileIndex))
|
|
630
|
+
? Number(pendingScopedCodeAttachment.fileIndex)
|
|
631
|
+
: activeFileIndex;
|
|
632
|
+
const matched = structuredCodeBlocksState.find((block) => buildScopedCodeBlockKey(fileIndex, block) === pendingScopedCodeAttachment.blockKey);
|
|
633
|
+
if (matched) return createScopedTargetFromBlock(matched, fileIndex);
|
|
634
|
+
return { ...pendingScopedCodeAttachment };
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function attachScopedCodeBlockToPrompt(blockIndex) {
|
|
638
|
+
const index = Number(blockIndex);
|
|
639
|
+
const block = structuredCodeBlocksState[index] || scopedCodeBlocks[index];
|
|
640
|
+
if (!block) return;
|
|
641
|
+
pendingScopedCodeAttachment = createScopedTargetFromBlock(block, activeFileIndex);
|
|
642
|
+
renderPromptAttachmentRow();
|
|
643
|
+
syncStructuredAttachmentState();
|
|
644
|
+
const input = document.getElementById("prompt-input");
|
|
645
|
+
if (input) {
|
|
646
|
+
input.focus();
|
|
647
|
+
if (typeof handleInput === "function") handleInput(input);
|
|
648
|
+
}
|
|
649
|
+
if (typeof showToast === "function") showToast(getScopedCodeText("attached"));
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function renderStructuredCodeEditor() {
|
|
653
|
+
const container = document.getElementById("code-structured-editor");
|
|
654
|
+
const editor = document.getElementById("code-editor");
|
|
655
|
+
const legacyContainer = document.getElementById("code-block-actions");
|
|
656
|
+
if (legacyContainer) {
|
|
657
|
+
legacyContainer.innerHTML = "";
|
|
658
|
+
legacyContainer.classList.add("hidden");
|
|
659
|
+
}
|
|
660
|
+
renderPromptAttachmentRow();
|
|
661
|
+
if (!container || !editor) return;
|
|
662
|
+
|
|
663
|
+
const content = getActiveCodeContentSnapshot();
|
|
664
|
+
const file = codeFiles && codeFiles[activeFileIndex] ? codeFiles[activeFileIndex] : null;
|
|
665
|
+
const lang = normalizeCodeLanguageKey((file && file.lang) || detectCodeBlockLanguageLocal(content));
|
|
666
|
+
|
|
667
|
+
if (!content.trim()) {
|
|
668
|
+
scopedCodeBlocks = [];
|
|
669
|
+
structuredCodeBlocksState = [];
|
|
670
|
+
container.innerHTML = "";
|
|
671
|
+
container.classList.add("hidden");
|
|
672
|
+
container.classList.remove("is-active");
|
|
673
|
+
editor.classList.remove("is-structured-hidden");
|
|
674
|
+
syncStructuredAttachmentState();
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const blocks = parseScopedCodeBlocksFromContent(content, lang).map((block) => ({
|
|
679
|
+
...block,
|
|
680
|
+
fileIndex: activeFileIndex,
|
|
681
|
+
blockKey: buildScopedCodeBlockKey(activeFileIndex, block),
|
|
682
|
+
currentText: String(block.sourceText || "")
|
|
683
|
+
}));
|
|
684
|
+
scopedCodeBlocks = blocks;
|
|
685
|
+
structuredCodeBlocksState = blocks.map((block) => ({ ...block }));
|
|
686
|
+
|
|
687
|
+
container.innerHTML = "";
|
|
688
|
+
container.classList.remove("hidden");
|
|
689
|
+
container.classList.add("is-active");
|
|
690
|
+
editor.classList.add("is-structured-hidden");
|
|
691
|
+
|
|
692
|
+
blocks.forEach((block, idx) => {
|
|
693
|
+
const card = document.createElement("section");
|
|
694
|
+
card.className = "code-structured-card";
|
|
695
|
+
card.dataset.blockKey = block.blockKey;
|
|
696
|
+
|
|
697
|
+
const isCollapsed = !!codeBlockCollapseState[block.blockKey];
|
|
698
|
+
if (isCollapsed) card.classList.add("is-collapsed");
|
|
699
|
+
|
|
700
|
+
const head = document.createElement("div");
|
|
701
|
+
head.className = "code-structured-card-head";
|
|
702
|
+
|
|
703
|
+
const headMain = document.createElement("div");
|
|
704
|
+
headMain.className = "code-structured-card-head-main";
|
|
705
|
+
|
|
706
|
+
const icon = document.createElement("span");
|
|
707
|
+
icon.className = "code-structured-icon";
|
|
708
|
+
icon.innerHTML = `<i class="${getCodeBlockIconClass(block.lang)}"></i>`;
|
|
709
|
+
headMain.appendChild(icon);
|
|
710
|
+
|
|
711
|
+
const titleWrap = document.createElement("div");
|
|
712
|
+
titleWrap.className = "code-structured-card-title-wrap";
|
|
713
|
+
|
|
714
|
+
const title = document.createElement("div");
|
|
715
|
+
title.className = "code-structured-card-title";
|
|
716
|
+
title.textContent = block.label || `${getScopedCodeText("fullFile")} ${idx + 1}`;
|
|
717
|
+
titleWrap.appendChild(title);
|
|
718
|
+
|
|
719
|
+
const meta = document.createElement("div");
|
|
720
|
+
meta.className = "code-structured-card-meta";
|
|
721
|
+
meta.textContent = `L${Number(block.startLine || 0) + 1}-${Number(block.endLine || 0) + 1}`;
|
|
722
|
+
titleWrap.appendChild(meta);
|
|
723
|
+
headMain.appendChild(titleWrap);
|
|
724
|
+
head.appendChild(headMain);
|
|
725
|
+
|
|
726
|
+
const actions = document.createElement("div");
|
|
727
|
+
actions.className = "code-structured-card-actions";
|
|
728
|
+
|
|
729
|
+
const aiButton = document.createElement("button");
|
|
730
|
+
aiButton.type = "button";
|
|
731
|
+
aiButton.className = "code-structured-ai";
|
|
732
|
+
aiButton.innerHTML = '<i class="ri-ai-generate-text"></i>';
|
|
733
|
+
aiButton.addEventListener("click", (event) => {
|
|
734
|
+
event.preventDefault();
|
|
735
|
+
event.stopPropagation();
|
|
736
|
+
attachScopedCodeBlockToPrompt(idx);
|
|
737
|
+
});
|
|
738
|
+
actions.appendChild(aiButton);
|
|
739
|
+
|
|
740
|
+
const toggleButton = document.createElement("button");
|
|
741
|
+
toggleButton.type = "button";
|
|
742
|
+
toggleButton.className = "code-structured-toggle";
|
|
743
|
+
toggleButton.innerHTML = `<i class="${isCollapsed ? "ri-arrow-down-s-line" : "ri-arrow-up-s-line"}"></i>`;
|
|
744
|
+
toggleButton.addEventListener("click", (event) => {
|
|
745
|
+
event.preventDefault();
|
|
746
|
+
event.stopPropagation();
|
|
747
|
+
const nextCollapsed = !card.classList.contains("is-collapsed");
|
|
748
|
+
card.classList.toggle("is-collapsed", nextCollapsed);
|
|
749
|
+
codeBlockCollapseState[block.blockKey] = nextCollapsed;
|
|
750
|
+
toggleButton.innerHTML = `<i class="${nextCollapsed ? "ri-arrow-down-s-line" : "ri-arrow-up-s-line"}"></i>`;
|
|
751
|
+
});
|
|
752
|
+
actions.appendChild(toggleButton);
|
|
753
|
+
|
|
754
|
+
head.appendChild(actions);
|
|
755
|
+
card.appendChild(head);
|
|
756
|
+
|
|
757
|
+
const body = document.createElement("div");
|
|
758
|
+
body.className = "code-structured-card-body";
|
|
759
|
+
|
|
760
|
+
const blockEditor = document.createElement("textarea");
|
|
761
|
+
blockEditor.className = "code-structured-editor-input custom-scrollbar-code";
|
|
762
|
+
blockEditor.spellcheck = false;
|
|
763
|
+
blockEditor.value = block.currentText;
|
|
764
|
+
blockEditor.dataset.blockIndex = String(idx);
|
|
765
|
+
blockEditor.addEventListener("input", (event) => {
|
|
766
|
+
const nextIndex = Number(event.target.dataset.blockIndex);
|
|
767
|
+
const nextBlock = structuredCodeBlocksState[nextIndex];
|
|
768
|
+
if (!nextBlock) return;
|
|
769
|
+
nextBlock.currentText = event.target.value;
|
|
770
|
+
nextBlock.sourceText = event.target.value;
|
|
771
|
+
nextBlock.promptText = String(event.target.value || "").trim();
|
|
772
|
+
syncStructuredCodeTextareaHeight(event.target);
|
|
773
|
+
syncStructuredCodeToMaster(nextBlock.fileIndex);
|
|
774
|
+
if (pendingScopedCodeAttachment && pendingScopedCodeAttachment.blockKey === nextBlock.blockKey) {
|
|
775
|
+
pendingScopedCodeAttachment = createScopedTargetFromBlock(nextBlock, nextBlock.fileIndex);
|
|
776
|
+
renderPromptAttachmentRow();
|
|
777
|
+
syncStructuredAttachmentState();
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
blockEditor.addEventListener("blur", () => {
|
|
781
|
+
scheduleCodeBlockActionsRender();
|
|
782
|
+
});
|
|
783
|
+
body.appendChild(blockEditor);
|
|
784
|
+
card.appendChild(body);
|
|
785
|
+
container.appendChild(card);
|
|
786
|
+
syncStructuredCodeTextareaHeight(blockEditor);
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
syncStructuredAttachmentState();
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function buildScopedCodeEditPrompt(block, instruction, fileInfo = {}) {
|
|
793
|
+
const lang = String(block && block.lang ? block.lang : fileInfo.lang || "text");
|
|
794
|
+
const name = String(fileInfo.name || "");
|
|
795
|
+
const label = String(block && block.label ? block.label : "Block");
|
|
796
|
+
return [
|
|
797
|
+
"You are editing only one code block from a file.",
|
|
798
|
+
"Return ONLY the updated block code.",
|
|
799
|
+
"Do not include markdown fences.",
|
|
800
|
+
"Do not add explanation.",
|
|
801
|
+
`Language: ${lang}`,
|
|
802
|
+
name ? `File: ${name}` : "",
|
|
803
|
+
`Target block: ${label}`,
|
|
804
|
+
"",
|
|
805
|
+
"[Edit request]",
|
|
806
|
+
String(instruction || "").trim(),
|
|
807
|
+
"",
|
|
808
|
+
"[Current block code]",
|
|
809
|
+
String(block && (block.promptText || block.sourceText) ? (block.promptText || block.sourceText) : "").trim()
|
|
810
|
+
].filter(Boolean).join("\n");
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function extractScopedPatchCode(rawText) {
|
|
814
|
+
const source = String(rawText || "").trim();
|
|
815
|
+
if (!source) return "";
|
|
816
|
+
const fencedMatch = source.match(/```[a-zA-Z0-9_+\-]*\s*([\s\S]*?)```/);
|
|
817
|
+
if (fencedMatch && fencedMatch[1] != null) {
|
|
818
|
+
return String(fencedMatch[1]).trim();
|
|
819
|
+
}
|
|
820
|
+
return source.replace(/^`+|`+$/g, "").trim();
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function applyScopedCodePatchToEditor(target, rawText) {
|
|
824
|
+
if (!target || typeof target !== "object") return false;
|
|
825
|
+
const patch = extractScopedPatchCode(rawText);
|
|
826
|
+
if (!patch) return false;
|
|
827
|
+
|
|
828
|
+
const fileIndex = Number.isFinite(Number(target.fileIndex)) ? Number(target.fileIndex) : activeFileIndex;
|
|
829
|
+
const editor = document.getElementById("code-editor");
|
|
830
|
+
const currentText = (codeFiles[fileIndex] && typeof codeFiles[fileIndex].content === "string")
|
|
831
|
+
? String(codeFiles[fileIndex].content || "")
|
|
832
|
+
: (editor ? String(editor.value || "") : "");
|
|
833
|
+
if (!currentText) return false;
|
|
834
|
+
|
|
835
|
+
const originalBlock = String(target.sourceText || target.originalBlock || "");
|
|
836
|
+
if (!originalBlock) return false;
|
|
837
|
+
|
|
838
|
+
let start = Number.isFinite(Number(target.startOffset)) ? Number(target.startOffset) : -1;
|
|
839
|
+
let end = Number.isFinite(Number(target.endOffset)) ? Number(target.endOffset) : -1;
|
|
840
|
+
|
|
841
|
+
if (!(start >= 0 && end >= start && currentText.slice(start, end) === originalBlock)) {
|
|
842
|
+
const foundIndex = currentText.indexOf(originalBlock);
|
|
843
|
+
if (foundIndex < 0) return false;
|
|
844
|
+
start = foundIndex;
|
|
845
|
+
end = foundIndex + originalBlock.length;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const merged = currentText.slice(0, start) + patch + currentText.slice(end);
|
|
849
|
+
if (codeFiles[fileIndex]) {
|
|
850
|
+
codeFiles[fileIndex].content = merged;
|
|
851
|
+
}
|
|
852
|
+
if (fileIndex === activeFileIndex && editor) {
|
|
853
|
+
editor.value = merged;
|
|
854
|
+
}
|
|
855
|
+
renderCodeTabs();
|
|
856
|
+
if (typeof window.refreshHtmlPreview === "function") {
|
|
857
|
+
try { window.refreshHtmlPreview(); } catch (error) {}
|
|
858
|
+
}
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function renderCodeBlockActions() {
|
|
863
|
+
renderPromptAttachmentRow();
|
|
864
|
+
renderStructuredCodeEditor();
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function buildNewCodeBlockTemplate(lang = "") {
|
|
868
|
+
const key = normalizeCodeLanguageKey(lang || "");
|
|
869
|
+
if (key === "html") return "\n\n<section class=\"code-block-new\">\n \n</section>\n";
|
|
870
|
+
if (key === "css") return "\n\n.code-block-new {\n \n}\n";
|
|
871
|
+
if (key === "javascript") return "\n\nfunction codeBlockNew() {\n \n}\n";
|
|
872
|
+
if (key === "php") return "\n\nfunction codeBlockNew() {\n \n}\n";
|
|
873
|
+
if (key === "python") return "\n\ndef code_block_new():\n pass\n";
|
|
874
|
+
return "\n\n/* New code block */\n";
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
window.addCodeBlockAtEnd = function() {
|
|
878
|
+
const editor = document.getElementById("code-editor");
|
|
879
|
+
if (!codeFiles || !codeFiles.length) {
|
|
880
|
+
codeFiles = [{
|
|
881
|
+
lang: "html",
|
|
882
|
+
name: "File 1 (html)",
|
|
883
|
+
content: buildNewCodeBlockTemplate("html").trimStart()
|
|
884
|
+
}];
|
|
885
|
+
activeFileIndex = 0;
|
|
886
|
+
} else {
|
|
887
|
+
const file = codeFiles[activeFileIndex] || codeFiles[0];
|
|
888
|
+
activeFileIndex = codeFiles.indexOf(file);
|
|
889
|
+
const spacer = String(file.content || "").endsWith("\n") ? "" : "\n";
|
|
890
|
+
file.content = String(file.content || "") + spacer + buildNewCodeBlockTemplate(file.lang || file.name || "");
|
|
891
|
+
}
|
|
892
|
+
if (editor && codeFiles[activeFileIndex]) editor.value = codeFiles[activeFileIndex].content;
|
|
893
|
+
renderCodeTabs();
|
|
894
|
+
setTimeout(() => {
|
|
895
|
+
const container = document.getElementById("code-structured-editor");
|
|
896
|
+
if (container) container.scrollTop = container.scrollHeight;
|
|
897
|
+
const editors = container ? container.querySelectorAll(".code-structured-editor-input") : [];
|
|
898
|
+
const last = editors && editors.length ? editors[editors.length - 1] : null;
|
|
899
|
+
if (last) last.focus();
|
|
900
|
+
if (typeof window.refreshHtmlPreview === "function") {
|
|
901
|
+
try { window.refreshHtmlPreview(); } catch (error) {}
|
|
902
|
+
}
|
|
903
|
+
}, 80);
|
|
904
|
+
if (typeof showToast === "function") showToast(getScopedCodeText("attached"));
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
document.addEventListener("click", (event) => {
|
|
908
|
+
const trigger = event.target && event.target.closest ? event.target.closest(".code-panel-brand-icon") : null;
|
|
909
|
+
if (!trigger) return;
|
|
910
|
+
event.preventDefault();
|
|
911
|
+
event.stopPropagation();
|
|
912
|
+
window.addCodeBlockAtEnd();
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
function scheduleCodeBlockActionsRender() {
|
|
916
|
+
if (codeBlockRenderTimer) clearTimeout(codeBlockRenderTimer);
|
|
917
|
+
codeBlockRenderTimer = setTimeout(() => {
|
|
918
|
+
codeBlockRenderTimer = null;
|
|
919
|
+
renderCodeBlockActions();
|
|
920
|
+
}, 120);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
window.requestScopedCodeEdit = function(blockIndex) {
|
|
924
|
+
attachScopedCodeBlockToPrompt(blockIndex);
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
function updateCodePanel(text) {
|
|
928
|
+
const matches =[...text.matchAll(/```(\w+)?\s*([\s\S]*?)(?:```|$)/g)];
|
|
929
|
+
|
|
930
|
+
if (matches.length !== 0) {
|
|
931
|
+
codeFiles = matches.map((m, idx) => ({
|
|
932
|
+
lang: m[1] || "Text",
|
|
933
|
+
content: m[2],
|
|
934
|
+
name: `File ${idx + 1} (${m[1] || "txt"})`
|
|
935
|
+
}));
|
|
936
|
+
renderCodeTabs();
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function getCodeTabIcon(lang = "", name = "") {
|
|
941
|
+
const key = String(lang || name || "").toLowerCase();
|
|
942
|
+
if (key.includes("html")) return "ri-html5-line";
|
|
943
|
+
if (key.includes("css") || key.includes("scss")) return "ri-css3-line";
|
|
944
|
+
if (key.includes("js") || key.includes("ts") || key.includes("json")) return "ri-braces-line";
|
|
945
|
+
if (key.includes("php")) return "ri-ai-generate-text";
|
|
946
|
+
if (key.includes("py")) return "ri-terminal-box-line";
|
|
947
|
+
if (key.includes("sql")) return "ri-database-2-line";
|
|
948
|
+
if (key.includes("md")) return "ri-markdown-line";
|
|
949
|
+
if (key.includes("sh") || key.includes("bash")) return "ri-terminal-line";
|
|
950
|
+
return "ri-ai-generate-text";
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function getCodePanelEmptyMarkup() {
|
|
954
|
+
return '<div class="code-panel-empty"><span class="code-panel-empty-icon"><i class="ri-ai-generate-text text-sm"></i></span><span class="code-panel-empty-dots" aria-hidden="true"><span></span><span></span><span></span></span></div>';
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function renderCodeTabs() {
|
|
958
|
+
const tabsContainer = document.getElementById("code-tabs");
|
|
959
|
+
const editor = document.getElementById("code-editor");
|
|
960
|
+
|
|
961
|
+
if (tabsContainer) {
|
|
962
|
+
tabsContainer.innerHTML = "";
|
|
963
|
+
|
|
964
|
+
if (codeFiles && codeFiles.length !== 0) {
|
|
965
|
+
codeFiles.forEach((file, index) => {
|
|
966
|
+
const btn = document.createElement("button");
|
|
967
|
+
btn.type = "button";
|
|
968
|
+
|
|
969
|
+
btn.className = "code-tab-btn " + (index === activeFileIndex ? "active" : "");
|
|
970
|
+
const iconClass = getCodeTabIcon(file.lang, file.name);
|
|
971
|
+
btn.title = file.name || file.lang || "Code";
|
|
972
|
+
btn.innerHTML = `<span class="code-tab-icon"><i class="${iconClass}"></i></span>`;
|
|
973
|
+
btn.onclick = (e) => {
|
|
974
|
+
e.preventDefault();
|
|
975
|
+
window.switchCodeFile(index);
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
tabsContainer.appendChild(btn);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
if (editor && codeFiles[activeFileIndex]) {
|
|
982
|
+
editor.value = codeFiles[activeFileIndex].content;
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
tabsContainer.innerHTML = getCodePanelEmptyMarkup();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
renderCodeBlockActions();
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function switchTab(index) {
|
|
992
|
+
if (typeof clearPendingScopedCodeAttachment === "function") {
|
|
993
|
+
clearPendingScopedCodeAttachment({ silent: true });
|
|
994
|
+
}
|
|
995
|
+
activeFileIndex = index;
|
|
996
|
+
renderCodeTabs();
|
|
997
|
+
const editor = document.getElementById("code-editor");
|
|
998
|
+
if (editor && codeFiles[index]) {
|
|
999
|
+
editor.value = codeFiles[index].content;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function resetChat(silent = false) {
|
|
1004
|
+
if (isGenerating || window.__ISAI_MAIN_GENERATING__) stopGeneration();
|
|
1005
|
+
if (typeof window.__isaiHardResetConversationState === "function") {
|
|
1006
|
+
window.__isaiHardResetConversationState();
|
|
1007
|
+
} else {
|
|
1008
|
+
chatHistory = [];
|
|
1009
|
+
}
|
|
1010
|
+
if (typeof clearCharacterChatSession === "function") clearCharacterChatSession();
|
|
1011
|
+
|
|
1012
|
+
chatHistory =[];
|
|
1013
|
+
codeFiles =[];
|
|
1014
|
+
activeFileIndex = 0;
|
|
1015
|
+
structuredCodeBlocksState = [];
|
|
1016
|
+
pendingScopedCodeAttachment = null;
|
|
1017
|
+
|
|
1018
|
+
const chatBox = document.getElementById("chat-box");
|
|
1019
|
+
if (chatBox) {
|
|
1020
|
+
chatBox.style.display = "flex";
|
|
1021
|
+
chatBox.style.flexDirection = "column";
|
|
1022
|
+
chatBox.style.justifyContent = "normal";
|
|
1023
|
+
chatBox.innerHTML = "";
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const viewId = new URLSearchParams(window.location.search).get("v");
|
|
1027
|
+
|
|
1028
|
+
const codeTabs = document.getElementById("code-tabs");
|
|
1029
|
+
if (codeTabs) codeTabs.innerHTML = getCodePanelEmptyMarkup();
|
|
1030
|
+
const codeEditor = document.getElementById("code-editor");
|
|
1031
|
+
if (codeEditor) codeEditor.value = "";
|
|
1032
|
+
scopedCodeBlocks = [];
|
|
1033
|
+
renderCodeBlockActions();
|
|
1034
|
+
|
|
1035
|
+
document.body.classList.remove("started");
|
|
1036
|
+
document.body.classList.remove("mode-code");
|
|
1037
|
+
isStarted = false;
|
|
1038
|
+
const scrollbar = document.getElementById("custom-scrollbar");
|
|
1039
|
+
if (scrollbar) scrollbar.style.display = "none";
|
|
1040
|
+
|
|
1041
|
+
if (activeApp) exitAppMode();
|
|
1042
|
+
if (isMenuOpen) toggleStoreMenu();
|
|
1043
|
+
|
|
1044
|
+
const promptInput = document.getElementById("prompt-input");
|
|
1045
|
+
if (promptInput) {
|
|
1046
|
+
promptInput.value = "";
|
|
1047
|
+
if (typeof handleInput === "function") handleInput(promptInput);
|
|
1048
|
+
}
|
|
1049
|
+
setMode("chat");
|
|
1050
|
+
if (!silent && typeof showToast === "function") {
|
|
1051
|
+
showToast("Chat Reset Completed");
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
window.__isaiResetChatCore = resetChat;
|
|
1056
|
+
|
|
1057
|
+
document.addEventListener("click", (event) => {
|
|
1058
|
+
const resetButton = event.target && event.target.closest ? event.target.closest("#btn-reset-chat") : null;
|
|
1059
|
+
if (!resetButton) return;
|
|
1060
|
+
event.preventDefault();
|
|
1061
|
+
event.stopPropagation();
|
|
1062
|
+
event.stopImmediatePropagation();
|
|
1063
|
+
if (typeof window.__isaiResetChatCore === "function") {
|
|
1064
|
+
window.__isaiResetChatCore(false);
|
|
1065
|
+
} else if (typeof window.resetChat === "function") {
|
|
1066
|
+
window.resetChat(false);
|
|
1067
|
+
}
|
|
1068
|
+
}, true);
|
|
1069
|
+
|
|
1070
|
+
async function processBlogImages(element) {
|
|
1071
|
+
if (!element) return;
|
|
1072
|
+
|
|
1073
|
+
let html = element.innerHTML;
|
|
1074
|
+
const matches =[...html.matchAll(/\[\[IMG:\s*(.*?)\]\]/g)];
|
|
1075
|
+
|
|
1076
|
+
if (matches.length === 0) return;
|
|
1077
|
+
|
|
1078
|
+
const loaderId = "img-loader-" + Date.now();
|
|
1079
|
+
const loaderBlock = document.createElement("div");
|
|
1080
|
+
loaderBlock.id = loaderId;
|
|
1081
|
+
loaderBlock.className = "text-xs text-gray-500 mt-2 flex items-center gap-2 animate-pulse";
|
|
1082
|
+
loaderBlock.innerHTML = '<i class="ri-image-line"></i> Searching related images...';
|
|
1083
|
+
element.appendChild(loaderBlock);
|
|
1084
|
+
|
|
1085
|
+
for (const match of matches) {
|
|
1086
|
+
const fullMatch = match[0];
|
|
1087
|
+
const keyword = match[1].trim();
|
|
1088
|
+
let imgUrl = "";
|
|
1089
|
+
let creditHtml = "";
|
|
1090
|
+
|
|
1091
|
+
try {
|
|
1092
|
+
const res = await fetch(`?action=pixabay_search&q=${encodeURIComponent(keyword)}`);
|
|
1093
|
+
const data = await parseJsonResponseSafe(res);
|
|
1094
|
+
|
|
1095
|
+
if (data.hits && data.hits.length > 0) {
|
|
1096
|
+
const hit = data.hits[0];
|
|
1097
|
+
imgUrl = hit.webformatURL;
|
|
1098
|
+
creditHtml = `<div class="text-[10px] text-gray-500 mt-1 text-center">Image by <a href="${hit.pageURL}" target="_blank" class="underline">${hit.user}</a> from Pixabay</div>`;
|
|
1099
|
+
}
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
// Ignored
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
const uiHtml = `
|
|
1105
|
+
<div class="my-6 rounded-xl overflow-hidden shadow-lg border border-white/10 group relative bg-black/20">
|
|
1106
|
+
<img src="${imgUrl}" alt="${keyword}" class="w-full h-auto object-cover transition transform group-hover:scale-105 duration-700 min-h-[200px]" loading="lazy" onload="scrollBottom()">
|
|
1107
|
+
${creditHtml}
|
|
1108
|
+
<button onclick="openPreview('${imgUrl}')" class="absolute top-2 right-2 bg-black/50 text-white p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition backdrop-blur-sm"><i class="ri-fullscreen-line"></i></button>
|
|
1109
|
+
</div>
|
|
1110
|
+
`;
|
|
1111
|
+
|
|
1112
|
+
html = html.replace(fullMatch, uiHtml);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
element.innerHTML = html;
|
|
1116
|
+
const loaderEl = document.getElementById(loaderId);
|
|
1117
|
+
if (loaderEl) loaderEl.remove();
|
|
1118
|
+
scrollBottom();
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
window.switchCodeFile = function(index) {
|
|
1122
|
+
if (typeof clearPendingScopedCodeAttachment === "function") {
|
|
1123
|
+
clearPendingScopedCodeAttachment({ silent: true });
|
|
1124
|
+
}
|
|
1125
|
+
activeFileIndex = index;
|
|
1126
|
+
renderCodeTabs();
|
|
1127
|
+
const editor = document.getElementById("code-editor");
|
|
1128
|
+
if (editor && codeFiles[index]) {
|
|
1129
|
+
editor.value = codeFiles[index].content;
|
|
1130
|
+
}
|
|
1131
|
+
};
|