Forge
csharp3407750f
1using System.Text;
2
3namespace AgentForge.Plugin.View;
4
5public sealed record ForgeInputSpec(
6 string Name,
7 string Type = "text",
8 string? Id = null,
9 string? Value = null,
10 string? Placeholder = null,
11 string? Autocomplete = null,
12 string? InputMode = null,
13 bool Required = false);
14
15public sealed record ForgeTextareaSpec(
16 string Name,
17 string? Id = null,
18 string? CssClass = null,
19 int Rows = 0,
20 string? Placeholder = null,
21 string? Value = null,
22 string? DataForgeSurface = null,
23 string? DataForgeContext = null);
24
25public static partial class ForgeHtml
26{
27 public static string Sep() => Text(" · ");
28
29 public static string Ol(string? cssClass, params ReadOnlySpan<string> items) =>
30 Tag("ol", cssClass, items);
31
32 public static string Pre(string? cssClass, string text) =>
33 Tag("pre", cssClass, Text(text));
34
35 public static string Form(string method, string action, string? cssClass, params ReadOnlySpan<string> inner) =>
36 OpenForm(method, action, cssClass, id: null, role: null, inner);
37
38 public static string FormWithRole(
39 string method,
40 string action,
41 string? cssClass,
42 string role,
43 params ReadOnlySpan<string> inner) =>
44 OpenForm(method, action, cssClass, id: null, role, inner);
45
46 public static string FormWithId(string id, string method, string action, string? cssClass, params ReadOnlySpan<string> inner) =>
47 OpenForm(method, action, cssClass, id, role: null, inner);
48
49 public static string Label(string inputId, params ReadOnlySpan<string> inner)
50 {
51 var sb = new StringBuilder();
52 sb.Append("<label for=\"").Append(H(inputId)).Append("\">");
53 foreach (var part in inner)
54 sb.Append(part);
55 sb.Append("</label>");
56 return sb.ToString();
57 }
58
59 public static string Label(string inputId, string text) =>
60 Label(inputId, (ReadOnlySpan<string>)[Text(text)]);
61
62 public static string LabelWithClass(string inputId, string? labelCssClass, params ReadOnlySpan<string> inner)
63 {
64 var sb = new StringBuilder("<label for=\"").Append(H(inputId)).Append('"');
65 if (!string.IsNullOrEmpty(labelCssClass))
66 sb.Append(" class=\"").Append(H(labelCssClass)).Append('"');
67 sb.Append('>');
68 foreach (var part in inner)
69 sb.Append(part);
70 sb.Append("</label>");
71 return sb.ToString();
72 }
73
74 public static string Input(ForgeInputSpec spec)
75 {
76 var sb = new StringBuilder("<input");
77 AppendAttr(sb, "id", spec.Id ?? spec.Name);
78 AppendAttr(sb, "name", spec.Name);
79 AppendAttr(sb, "type", spec.Type);
80 AppendAttr(sb, "value", spec.Value);
81 AppendAttr(sb, "placeholder", spec.Placeholder);
82 AppendAttr(sb, "autocomplete", spec.Autocomplete);
83 AppendAttr(sb, "inputmode", spec.InputMode);
84 if (spec.Required)
85 sb.Append(" required");
86 sb.Append('>');
87 return sb.ToString();
88 }
89
90 public static string Button(string type, string label, string? cssClass = null, string? id = null, string? title = null)
91 {
92 var sb = new StringBuilder("<button type=\"").Append(H(type)).Append('"');
93 if (!string.IsNullOrEmpty(cssClass))
94 sb.Append(" class=\"").Append(H(cssClass)).Append('"');
95 if (!string.IsNullOrEmpty(id))
96 sb.Append(" id=\"").Append(H(id)).Append('"');
97 if (!string.IsNullOrEmpty(title))
98 sb.Append(" title=\"").Append(H(title)).Append('"');
99 sb.Append('>').Append(H(label)).Append("</button>");
100 return sb.ToString();
101 }
102
103 public static string Select(string name, bool required, params ReadOnlySpan<string> options)
104 {
105 var sb = new StringBuilder("<select name=\"").Append(H(name)).Append('"');
106 if (required)
107 sb.Append(" required");
108 sb.Append('>');
109 foreach (var option in options)
110 sb.Append(option);
111 sb.Append("</select>");
112 return sb.ToString();
113 }
114
115 public static string Select(
116 string id,
117 string name,
118 string? cssClass,
119 bool required,
120 params ReadOnlySpan<string> options)
121 {
122 var sb = new StringBuilder("<select");
123 AppendAttr(sb, "id", id);
124 AppendAttr(sb, "name", name);
125 AppendAttr(sb, "class", cssClass);
126 if (required)
127 sb.Append(" required");
128 sb.Append('>');
129 foreach (var option in options)
130 sb.Append(option);
131 sb.Append("</select>");
132 return sb.ToString();
133 }
134
135 public static string Textarea(ForgeTextareaSpec spec)
136 {
137 var sb = new StringBuilder("<textarea");
138 AppendAttr(sb, "id", spec.Id ?? spec.Name);
139 AppendAttr(sb, "name", spec.Name);
140 AppendAttr(sb, "class", spec.CssClass);
141 AppendAttr(sb, "placeholder", spec.Placeholder);
142 AppendAttr(sb, "data-forge-surface", spec.DataForgeSurface);
143 AppendAttr(sb, "data-forge-context", spec.DataForgeContext);
144 if (spec.Rows > 0)
145 sb.Append(" rows=\"").Append(spec.Rows).Append('"');
146 sb.Append('>').Append(H(spec.Value)).Append("</textarea>");
147 return sb.ToString();
148 }
149
150 public static string SelectNav(
151 string id,
152 string? cssClass,
153 string ariaLabel,
154 params ReadOnlySpan<string> options)
155 {
156 var sb = new StringBuilder("<select");
157 AppendAttr(sb, "id", id);
158 AppendAttr(sb, "class", cssClass);
159 AppendAttr(sb, "aria-label", ariaLabel);
160 sb.Append(" onchange=\"if(this.value)location.href=this.value\">");
161 foreach (var option in options)
162 sb.Append(option);
163 sb.Append("</select>");
164 return sb.ToString();
165 }
166
167 public static string SelectNav(
168 string id,
169 string? cssClass,
170 string ariaLabel,
171 string placeholder,
172 params ReadOnlySpan<string> options)
173 {
174 var sb = new StringBuilder("<select");
175 AppendAttr(sb, "id", id);
176 AppendAttr(sb, "class", cssClass);
177 AppendAttr(sb, "aria-label", ariaLabel);
178 sb.Append(" onchange=\"if(this.value)location.href=this.value\">");
179 sb.Append(Option("", placeholder, selected: true));
180 foreach (var option in options)
181 sb.Append(option);
182 sb.Append("</select>");
183 return sb.ToString();
184 }
185
186 public static string FieldSubmit(string? cssClass, params ReadOnlySpan<string> inner) =>
187 P(cssClass ?? "meta field-submit", inner);
188
189 public static string PWithId(string id, string? cssClass, params ReadOnlySpan<string> inner)
190 {
191 var sb = new StringBuilder("<p");
192 AppendAttr(sb, "id", id);
193 AppendAttr(sb, "class", cssClass);
194 sb.Append('>');
195 foreach (var part in inner)
196 sb.Append(part);
197 sb.Append("</p>");
198 return sb.ToString();
199 }
200
201 public static string Script(string javascript) =>
202 Tag("script", null, Raw(javascript));
203
204 public static string SetGlobal(string name, string jsonEncodedValue) =>
205 Script($"window.{name}={jsonEncodedValue};");
206
207 public static string ARaw(
208 string href,
209 string? cssClass,
210 string? ariaLabel,
211 string? title,
212 params ReadOnlySpan<string> inner)
213 {
214 var sb = new StringBuilder();
215 sb.Append("<a href=\"").Append(H(href)).Append('"');
216 if (!string.IsNullOrEmpty(cssClass))
217 sb.Append(" class=\"").Append(H(cssClass)).Append('"');
218 if (!string.IsNullOrEmpty(ariaLabel))
219 sb.Append(" aria-label=\"").Append(H(ariaLabel)).Append('"');
220 if (!string.IsNullOrEmpty(title))
221 sb.Append(" title=\"").Append(H(title)).Append('"');
222 sb.Append('>');
223 foreach (var part in inner)
224 sb.Append(part);
225 sb.Append("</a>");
226 return sb.ToString();
227 }
228
229 public static string ARawWithForgeUri(
230 string href,
231 string? cssClass,
232 string? ariaLabel,
233 string? title,
234 string? dataForgeUri,
235 params ReadOnlySpan<string> inner)
236 {
237 var sb = new StringBuilder();
238 sb.Append("<a href=\"").Append(H(href)).Append('"');
239 if (!string.IsNullOrEmpty(cssClass))
240 sb.Append(" class=\"").Append(H(cssClass)).Append('"');
241 if (!string.IsNullOrEmpty(ariaLabel))
242 sb.Append(" aria-label=\"").Append(H(ariaLabel)).Append('"');
243 if (!string.IsNullOrEmpty(title))
244 sb.Append(" title=\"").Append(H(title)).Append('"');
245 if (!string.IsNullOrEmpty(dataForgeUri))
246 sb.Append(" data-forge-uri=\"").Append(H(dataForgeUri)).Append('"');
247 sb.Append('>');
248 foreach (var part in inner)
249 sb.Append(part);
250 sb.Append("</a>");
251 return sb.ToString();
252 }
253
254 private static string OpenForm(
255 string method,
256 string action,
257 string? cssClass,
258 string? id,
259 string? role,
260 ReadOnlySpan<string> inner)
261 {
262 var sb = new StringBuilder("<form");
263 AppendAttr(sb, "method", method);
264 AppendAttr(sb, "action", action);
265 AppendAttr(sb, "class", cssClass);
266 AppendAttr(sb, "id", id);
267 AppendAttr(sb, "role", role);
268 sb.Append('>');
269 foreach (var part in inner)
270 sb.Append(part);
271 sb.Append("</form>");
272 return sb.ToString();
273 }
274
275 private static void AppendAttr(StringBuilder sb, string name, string? value)
276 {
277 if (string.IsNullOrEmpty(value))
278 return;
279 sb.Append(' ').Append(name).Append("=\"").Append(H(value)).Append('"');
280 }
281}
282
View only · write via MCP/CIDE