Mercurial
comparison mrjunejune/main.c @ 273:e02e2036ef84 default tip
add Layer 2 JRPG component system
Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sat, 08 Aug 2026 02:08:08 -0700 |
| parents | 056790c4fb0d |
| children |
comparison
equal
deleted
inserted
replaced
| 272:41a49c29a28f | 273:e02e2036ef84 |
|---|---|
| 1212 return html_render_error(arena, "Internal Server Error"); | 1212 return html_render_error(arena, "Internal Server Error"); |
| 1213 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); | 1213 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); |
| 1214 return resp; | 1214 return resp; |
| 1215 } | 1215 } |
| 1216 | 1216 |
| 1217 typedef struct { | |
| 1218 const char *name; | |
| 1219 const char *title; | |
| 1220 const char *copy; | |
| 1221 const char *works; | |
| 1222 } Jrpg_Initial_Panel; | |
| 1223 | |
| 1224 static boolean Mjj_Replace_All( | |
| 1225 char *buffer, | |
| 1226 size_t capacity, | |
| 1227 const char *needle, | |
| 1228 const char *replacement) | |
| 1229 { | |
| 1230 if (!buffer || !needle || !replacement || needle[0] == '\0') | |
| 1231 return FALSE; | |
| 1232 | |
| 1233 size_t needle_length = strlen(needle); | |
| 1234 size_t replacement_length = strlen(replacement); | |
| 1235 char *match = strstr(buffer, needle); | |
| 1236 while (match) | |
| 1237 { | |
| 1238 size_t current_length = strlen(buffer); | |
| 1239 size_t offset = (size_t)(match - buffer); | |
| 1240 size_t new_length = | |
| 1241 current_length - needle_length + replacement_length; | |
| 1242 if (new_length >= capacity) | |
| 1243 return FALSE; | |
| 1244 | |
| 1245 memmove( | |
| 1246 match + replacement_length, | |
| 1247 match + needle_length, | |
| 1248 current_length - offset - needle_length + 1); | |
| 1249 memcpy(match, replacement, replacement_length); | |
| 1250 match = strstr(match + replacement_length, needle); | |
| 1251 } | |
| 1252 return TRUE; | |
| 1253 } | |
| 1254 | |
| 1255 static const Jrpg_Initial_Panel *Jrpg_Resolve_Initial_Panel( | |
| 1256 Seobeo_Request_Entry *req) | |
| 1257 { | |
| 1258 static const Jrpg_Initial_Panel panels[] = { | |
| 1259 { | |
| 1260 "resume", | |
| 1261 "Resume", | |
| 1262 "Member of Technical Staff and engineering leader with 10+ years " | |
| 1263 "building AI agent platforms and production systems across Microsoft, " | |
| 1264 "Meta, Google, and growth-stage companies.", | |
| 1265 "<li><a href=\"/resume\" data-resume-modal>" | |
| 1266 "<span>Full resume</span><small>Career dossier</small></a></li>" | |
| 1267 "<li><a href=\"https://www.microsoft.com/en-us/microsoft-copilot/blog/" | |
| 1268 "2026/02/26/copilot-tasks-from-answers-to-actions/\">" | |
| 1269 "<span>Copilot Tasks</span><small>Agentic execution</small></a></li>" | |
| 1270 }, | |
| 1271 { | |
| 1272 "tools", | |
| 1273 "Tools", | |
| 1274 "Useful browser tools backed by first-party C, WASM, media, and " | |
| 1275 "document-processing systems.", | |
| 1276 "<li><a href=\"/tools/markdown_to_html\" data-latest-tool " | |
| 1277 "data-tool-url=\"/tools/markdown_to_html\">" | |
| 1278 "<span>Markdown</span><small>Writing</small></a></li>" | |
| 1279 "<li><a href=\"/tools/file_converter\" data-latest-tool " | |
| 1280 "data-tool-url=\"/tools/file_converter\">" | |
| 1281 "<span>Converter</span><small>Media</small></a></li>" | |
| 1282 "<li><a href=\"/tools/hls_player\" data-latest-tool " | |
| 1283 "data-tool-url=\"/tools/hls_player\">" | |
| 1284 "<span>HLS Player</span><small>Streaming</small></a></li>" | |
| 1285 }, | |
| 1286 { | |
| 1287 "blog", | |
| 1288 "Blogs", | |
| 1289 "Technical writing about networking, rendering, performance, developer " | |
| 1290 "tooling, and experiments.", | |
| 1291 "<li><a href=\"/blog\" data-blog-archive>" | |
| 1292 "<span>All posts</span><small>Archive</small></a></li>" | |
| 1293 }, | |
| 1294 { | |
| 1295 "conversations", | |
| 1296 "Resume", | |
| 1297 "", | |
| 1298 "" | |
| 1299 }, | |
| 1300 }; | |
| 1301 | |
| 1302 const char *requested = NULL; | |
| 1303 void *panel_kv = Dowa_HashMap_Get_Ptr(req, "query_panel"); | |
| 1304 if (panel_kv) | |
| 1305 requested = ((Seobeo_Request_Entry *)panel_kv)->value; | |
| 1306 | |
| 1307 for (size_t i = 0; i < sizeof(panels) / sizeof(panels[0]); ++i) | |
| 1308 { | |
| 1309 if (requested && strcmp(requested, panels[i].name) == 0) | |
| 1310 return &panels[i]; | |
| 1311 } | |
| 1312 return &panels[0]; | |
| 1313 } | |
| 1314 | |
| 1217 Seobeo_Request_Entry *GetJrpg(Seobeo_Request_Entry *req, Dowa_Arena *arena) | 1315 Seobeo_Request_Entry *GetJrpg(Seobeo_Request_Entry *req, Dowa_Arena *arena) |
| 1218 { | 1316 { |
| 1219 Seobeo_Request_Entry *resp = NULL; | 1317 Seobeo_Request_Entry *resp = NULL; |
| 1220 char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); | 1318 char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); |
| 1221 if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/jrpg/index.html", arena)) | 1319 if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/jrpg/index.html", arena)) |
| 1222 return html_render_error(arena, "Internal Server Error"); | 1320 return html_render_error(arena, "Internal Server Error"); |
| 1321 | |
| 1322 const Jrpg_Initial_Panel *panel = Jrpg_Resolve_Initial_Panel(req); | |
| 1323 boolean conversations = | |
| 1324 strcmp(panel->name, "conversations") == 0 ? TRUE : FALSE; | |
| 1325 const char *preview_selection = conversations ? "resume" : panel->name; | |
| 1326 struct { | |
| 1327 const char *token; | |
| 1328 const char *value; | |
| 1329 } replacements[] = { | |
| 1330 {"__MJJ_PANEL__", panel->name}, | |
| 1331 {"__MJJ_PREVIEW_SELECTION__", preview_selection}, | |
| 1332 {"__MJJ_PREVIEW_TITLE__", panel->title}, | |
| 1333 {"__MJJ_PREVIEW_COPY__", panel->copy}, | |
| 1334 {"__MJJ_PREVIEW_WORKS__", panel->works}, | |
| 1335 {"__MJJ_PREVIEW_HIDDEN__", conversations ? "hidden" : ""}, | |
| 1336 {"__MJJ_ARCHIVE_HIDDEN__", conversations ? "" : "hidden"}, | |
| 1337 {"__MJJ_RESUME_PRESSED__", | |
| 1338 strcmp(panel->name, "resume") == 0 ? "true" : "false"}, | |
| 1339 {"__MJJ_TOOLS_PRESSED__", | |
| 1340 strcmp(panel->name, "tools") == 0 ? "true" : "false"}, | |
| 1341 {"__MJJ_BLOG_PRESSED__", | |
| 1342 strcmp(panel->name, "blog") == 0 ? "true" : "false"}, | |
| 1343 {"__MJJ_CONVERSATIONS_PRESSED__", conversations ? "true" : "false"}, | |
| 1344 }; | |
| 1345 for (size_t i = 0; | |
| 1346 i < sizeof(replacements) / sizeof(replacements[0]); | |
| 1347 ++i) | |
| 1348 { | |
| 1349 if (!Mjj_Replace_All( | |
| 1350 final_body, | |
| 1351 HTML_PAGE_CAP, | |
| 1352 replacements[i].token, | |
| 1353 replacements[i].value)) | |
| 1354 return html_render_error(arena, "Internal Server Error"); | |
| 1355 } | |
| 1223 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); | 1356 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); |
| 1224 Dowa_HashMap_Push_Arena(resp, "referrer-policy", "no-referrer", arena); | 1357 Dowa_HashMap_Push_Arena(resp, "referrer-policy", "no-referrer", arena); |
| 1225 return resp; | 1358 return resp; |
| 1226 } | 1359 } |
| 1227 | 1360 |