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