Forge
csharpdeeb25a2
1using System.Text;
2using System.Text.Json;
3
4namespace CascadeIDE.Features.WebAiPortal.Application;
5
6/// <summary>
7/// Строит JavaScript для вставки UTF-8 текста в активный <c>textarea</c>/<c>input</c> или contenteditable после ответа моста ADR 0108 (эвристика сторонней страницы).
8/// </summary>
9public static class WebAiPortalComposerInjectScript
10{
11 /// <summary>Возвращает IIFE со строкой JSON с полями <c>ok</c>, <c>reason</c>, опционально <c>tag</c>.</summary>
12 public static string Build(ReadOnlySpan<char> utf16Text)
13 {
14 var utf8Count = Encoding.UTF8.GetByteCount(utf16Text);
15 Span<byte> utf8 = utf8Count <= 1024 ? stackalloc byte[utf8Count] : new byte[utf8Count];
16 _ = Encoding.UTF8.GetBytes(utf16Text, utf8);
17
18 var b64 = Convert.ToBase64String(utf8);
19 var b64Js = JsonSerializer.Serialize(b64);
20
21 return """
22(() => {
23 const b64 = %%B64%%;
24 let t;
25 try {
26 const bin = atob(b64);
27 const bytes = new Uint8Array(bin.length);
28 for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
29 t = new TextDecoder("utf-8").decode(bytes);
30 } catch (e) {
31 return JSON.stringify({ ok: false, reason: "decode" });
32 }
33 const sep = "\n\n";
34 const ins = sep + t;
35 const el = document.activeElement;
36 if (!el) return JSON.stringify({ ok: false, reason: "no_focus" });
37 const tag = el.tagName || "";
38 if (tag === "TEXTAREA") {
39 const ta = /** @type {HTMLTextAreaElement} */ (el);
40 const start = typeof ta.selectionStart === "number" ? ta.selectionStart : ta.value.length;
41 const end = typeof ta.selectionEnd === "number" ? ta.selectionEnd : ta.value.length;
42 ta.value = ta.value.slice(0, start) + ins + ta.value.slice(end);
43 const pos = start + ins.length;
44 ta.selectionStart = ta.selectionEnd = pos;
45 ta.dispatchEvent(new Event("input", { bubbles: true }));
46 ta.dispatchEvent(new Event("change", { bubbles: true }));
47 return JSON.stringify({ ok: true, tag: "TEXTAREA" });
48 }
49 if (tag === "INPUT") {
50 const inp = /** @type {HTMLInputElement} */ (el);
51 if (inp.type && inp.type !== "text" && inp.type !== "search")
52 return JSON.stringify({ ok: false, reason: "unsupported_input_type", tag: inp.type });
53 const start = typeof inp.selectionStart === "number" ? inp.selectionStart : inp.value.length;
54 const end = typeof inp.selectionEnd === "number" ? inp.selectionEnd : inp.value.length;
55 inp.value = inp.value.slice(0, start) + ins + inp.value.slice(end);
56 const pos = start + ins.length;
57 inp.selectionStart = inp.selectionEnd = pos;
58 inp.dispatchEvent(new Event("input", { bubbles: true }));
59 inp.dispatchEvent(new Event("change", { bubbles: true }));
60 return JSON.stringify({ ok: true, tag: "INPUT" });
61 }
62 if (el.isContentEditable) {
63 document.execCommand("insertText", false, ins.startsWith(sep) ? ins.slice(sep.length) : ins);
64 return JSON.stringify({ ok: true, tag: "contenteditable" });
65 }
66 return JSON.stringify({ ok: false, reason: "unsupported_focus", tag });
67})();
68""".Replace("%%B64%%", b64Js, StringComparison.Ordinal);
69 }
70}
71
View only · write via MCP/CIDE