Mercurial
diff infinite_canvas/agent_response.c @ 281:c57149ad216e default tip
Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 22:18:15 -0700 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/infinite_canvas/agent_response.c Tue Aug 18 22:18:15 2026 -0700 @@ -0,0 +1,237 @@ +#include "infinite_canvas/agent_response.h" + +#include <math.h> +#include <stdio.h> +#include <string.h> + +static boolean Canvas_Agent_Entity_Type( + const char *p_kind, + Canvas_Entity_Type *p_type) +{ + struct { + const char *p_name; + Canvas_Entity_Type type; + } types[] = { + {"text", CANVAS_ENTITY_TEXT}, + {"button", CANVAS_ENTITY_BUTTON}, + {"text_area", CANVAS_ENTITY_TEXT_AREA}, + {"accordion", CANVAS_ENTITY_ACCORDION}, + {"card", CANVAS_ENTITY_CARD}, + {"calendar", CANVAS_ENTITY_CALENDAR}, + {"table", CANVAS_ENTITY_TABLE}, + {"notification", CANVAS_ENTITY_NOTIFICATION}, + {"scroll_area", CANVAS_ENTITY_SCROLL_AREA}, + {"image", CANVAS_ENTITY_IMAGE}, + {"web_content", CANVAS_ENTITY_WEB_CONTENT}, + }; + for (size_t index = 0; index < sizeof(types) / sizeof(types[0]); index++) { + if (strcmp(p_kind, types[index].p_name) == 0) { + *p_type = types[index].type; + return TRUE; + } + } + return FALSE; +} + +static boolean Canvas_Agent_JSON_Number( + Dowa_JSON_Entry *p_object, + const char *p_key, + float *p_value, + boolean *p_present) +{ + Dowa_JSON_Value *p_json = Dowa_JSON_Get(p_object, p_key); + *p_present = p_json ? TRUE : FALSE; + if (!p_json) return TRUE; + if (p_json->type != DOWA_JSON_NUMBER || + !isfinite(p_json->num_val) || + fabs(p_json->num_val) > 10000000.0) { + return FALSE; + } + *p_value = (float)p_json->num_val; + return TRUE; +} + +static boolean Canvas_Agent_URL_Allowed(const char *p_value) +{ + return !p_value[0] || + strncmp(p_value, "https://", 8) == 0 || + strncmp(p_value, "http://", 7) == 0; +} + +boolean Canvas_Agent_Response_Parse( + const char *p_json, + Canvas_Agent_Response *p_response) +{ + if (!p_json || !p_response) return FALSE; + memset(p_response, 0, sizeof(*p_response)); + Dowa_Arena *p_arena = Dowa_Arena_Create(128 * 1024); + if (!p_arena) return FALSE; + Dowa_JSON_Value root = Dowa_JSON_Parse( + p_json, (int32)strlen(p_json), p_arena); + if (root.type != DOWA_JSON_OBJECT) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + Dowa_JSON_Entry *p_object = root.object_val; + char *p_action = Dowa_JSON_Get_String(p_object, "action"); + char *p_presentation = + Dowa_JSON_Get_String(p_object, "presentation"); + char *p_title = Dowa_JSON_Get_String(p_object, "title"); + char *p_text = Dowa_JSON_Get_String(p_object, "response"); + Dowa_JSON_Value *p_conversation = + Dowa_JSON_Get(p_object, "conversation_id"); + if (!p_action || + (strcmp(p_action, "create") != 0 && + strcmp(p_action, "append") != 0) || + !p_presentation || + (strcmp(p_presentation, "conversation") != 0 && + strcmp(p_presentation, "action") != 0) || + (strcmp(p_action, "append") == 0 && + strcmp(p_presentation, "conversation") != 0) || + !p_text || + !p_conversation || + p_conversation->type != DOWA_JSON_NUMBER || + floor(p_conversation->num_val) != p_conversation->num_val || + p_conversation->num_val < 0 || + p_conversation->num_val > 4294967295.0) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + snprintf( + p_response->action, + sizeof(p_response->action), + "%s", + p_action); + snprintf( + p_response->presentation, + sizeof(p_response->presentation), + "%s", + p_presentation); + snprintf( + p_response->title, + sizeof(p_response->title), + "%s", + p_title ? p_title : ""); + snprintf( + p_response->response, + sizeof(p_response->response), + "%s", + p_text); + p_response->conversation_id = (uint32)p_conversation->num_val; + + Dowa_JSON_Value *p_entities = Dowa_JSON_Get(p_object, "entities"); + if (p_entities) { + if (p_entities->type != DOWA_JSON_ARRAY || + Dowa_Array_Length(p_entities->array_val) > + CANVAS_AGENT_MAX_CREATED_ENTITIES) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + for (size_t index = 0; + index < Dowa_Array_Length(p_entities->array_val); + index++) { + Dowa_JSON_Value *p_value = &p_entities->array_val[index]; + if (p_value->type != DOWA_JSON_OBJECT) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + Dowa_JSON_Entry *p_entity_object = p_value->object_val; + char *p_kind = + Dowa_JSON_Get_String(p_entity_object, "kind"); + Canvas_Agent_Entity_Spec *p_spec = + &p_response->entities[p_response->entity_count]; + if (!p_kind || + !Canvas_Agent_Entity_Type(p_kind, &p_spec->type)) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + char *p_label = + Dowa_JSON_Get_String(p_entity_object, "label"); + char *p_entity_text = + Dowa_JSON_Get_String(p_entity_object, "text"); + snprintf( + p_spec->label, + sizeof(p_spec->label), + "%s", + p_label ? p_label : ""); + snprintf( + p_spec->text, + sizeof(p_spec->text), + "%s", + p_entity_text ? p_entity_text : ""); + if ((p_spec->type == CANVAS_ENTITY_IMAGE || + p_spec->type == CANVAS_ENTITY_WEB_CONTENT) && + !Canvas_Agent_URL_Allowed(p_spec->text)) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + float x = 0.0f; + float y = 0.0f; + boolean has_x = FALSE; + boolean has_y = FALSE; + if (!Canvas_Agent_JSON_Number( + p_entity_object, "x", &x, &has_x) || + !Canvas_Agent_JSON_Number( + p_entity_object, "y", &y, &has_y)) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + if (has_x != has_y) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + if ((has_x && (fabsf(x) > 1000000.0f || + fabsf(y) > 1000000.0f))) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + p_spec->has_position = has_x && has_y; + p_spec->position = (Vector2){x, y}; + float width = 0.0f; + float height = 0.0f; + boolean has_width = FALSE; + boolean has_height = FALSE; + if (!Canvas_Agent_JSON_Number( + p_entity_object, + "width", + &width, + &has_width) || + !Canvas_Agent_JSON_Number( + p_entity_object, + "height", + &height, + &has_height)) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + if (has_width != has_height || + (has_width && + (width < 32.0f || + height < 24.0f || + width > 5000.0f || + height > 5000.0f))) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + p_spec->has_size = has_width && has_height; + p_spec->size = (Vector2){width, height}; + Dowa_JSON_Value *p_entity_value = + Dowa_JSON_Get(p_entity_object, "value"); + if (p_entity_value) { + if (p_entity_value->type != DOWA_JSON_NUMBER || + floor(p_entity_value->num_val) != + p_entity_value->num_val || + p_entity_value->num_val < -2147483648.0 || + p_entity_value->num_val > 2147483647.0) { + Dowa_Arena_Free(p_arena); + return FALSE; + } + p_spec->value = (int32)p_entity_value->num_val; + p_spec->has_value = TRUE; + } + p_response->entity_count++; + } + } + Dowa_Arena_Free(p_arena); + return TRUE; +}