diff infinite_canvas/main.c @ 282:6bd4a3990696 default tip

Add cross-platform canvas orchestration and typed composer Enable native ARM dependencies, macOS Copilot orchestration, portable service supervision, CPU dictation, and a shared N-key speak-or-type composer. Co-authored-by: Copilot <[email protected]> Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
author MrJuneJune <me@mrjunejune.com>
date Thu, 20 Aug 2026 20:45:25 -0700
parents c57149ad216e
children
line wrap: on
line diff
--- a/infinite_canvas/main.c	Tue Aug 18 22:18:15 2026 -0700
+++ b/infinite_canvas/main.c	Thu Aug 20 20:45:25 2026 -0700
@@ -40,6 +40,8 @@
     boolean dictation_overlay_visible;
     boolean dictation_listening;
     boolean dictation_requested_active;
+    boolean dictation_typing;
+    boolean dictation_refocus_pending;
     boolean dictation_send_pending;
     char dictation_status[128];
     char dictation_partial[CANVAS_ENTITY_TEXT_CAPACITY];
@@ -252,6 +254,21 @@
     return p_entity;
 }
 
+static void Canvas_App_Focus_Dictation_Entity(void)
+{
+    Canvas_Entity *p_entity = Canvas_App_Ensure_Dictation_Entity();
+    if (!p_entity) return;
+    for (size_t index = 0;
+        index < Dowa_Array_Length(g_app->scene.p_entities);
+        index++) {
+        if (g_app->scene.p_entities[index].id == p_entity->id) {
+            g_app->scene.selected_index = (int32)index;
+            break;
+        }
+    }
+    Canvas_Text_Area_Focus_End(p_entity, g_app->font);
+}
+
 static void Canvas_App_Sync_Dictation_Entity(void)
 {
     Canvas_Entity *p_entity =
@@ -283,6 +300,7 @@
     g_app->dictation_overlay_visible = TRUE;
     g_app->dictation_requested_active = active;
     g_app->dictation_listening = FALSE;
+    g_app->dictation_typing = FALSE;
     g_app->dictation_send_pending = FALSE;
     if (active) {
         Canvas_App_Ensure_Dictation_Entity();
@@ -298,6 +316,45 @@
         active);
 }
 
+static void Canvas_App_Start_Dictation_Typing(void)
+{
+    if (g_app->dictation_requested_active ||
+        g_app->dictation_listening) {
+        Canvas_Web_Surface_Set_Dictation_Active(
+            &g_app->web_surface,
+            FALSE);
+    }
+    g_app->dictation_requested_active = FALSE;
+    g_app->dictation_listening = FALSE;
+    g_app->dictation_typing = TRUE;
+    g_app->dictation_refocus_pending = FALSE;
+    g_app->dictation_send_pending = FALSE;
+    g_app->dictation_partial[0] = '\0';
+    g_app->dictation_overlay_visible = TRUE;
+    Canvas_App_Sync_Dictation_Entity();
+    Canvas_App_Focus_Dictation_Entity();
+    snprintf(
+        g_app->dictation_status,
+        sizeof(g_app->dictation_status),
+        "Type a thought");
+
+    while (GetCharPressed() > 0) {
+    }
+}
+
+static void Canvas_App_Sync_Typed_Dictation(void)
+{
+    if (!g_app->dictation_typing) return;
+    Canvas_Entity *p_entity =
+        Canvas_App_Find_Entity(g_app->dictation_entity_id);
+    if (!p_entity) return;
+    snprintf(
+        g_app->dictation_transcript,
+        sizeof(g_app->dictation_transcript),
+        "%s",
+        p_entity->text);
+}
+
 static boolean Canvas_App_Submit_Dictation(void)
 {
     if (!g_app->dictation_transcript[0]) return FALSE;
@@ -311,11 +368,16 @@
     }
     Canvas_Entity *p_entity =
         Canvas_App_Find_Entity(g_app->dictation_entity_id);
-    if (p_entity) p_entity->text_muted = FALSE;
+    if (p_entity) {
+        p_entity->text_muted = FALSE;
+        if (g_app->dictation_typing) p_entity->active = FALSE;
+    }
     g_app->dictation_transcript[0] = '\0';
     g_app->dictation_partial[0] = '\0';
     Canvas_App_Sync_Dictation_Entity();
     g_app->dictation_send_pending = FALSE;
+    g_app->dictation_typing = FALSE;
+    g_app->dictation_refocus_pending = FALSE;
     g_app->dictation_overlay_visible =
         g_app->dictation_listening;
     return TRUE;
@@ -360,15 +422,32 @@
             g_app->dictation_listening = FALSE;
         }
         g_app->dictation_send_pending = FALSE;
+        g_app->dictation_typing = FALSE;
         g_app->dictation_partial[0] = '\0';
         g_app->dictation_transcript[0] = '\0';
+        Canvas_Entity *p_entity =
+            Canvas_App_Find_Entity(g_app->dictation_entity_id);
+        if (p_entity) p_entity->active = FALSE;
         Canvas_App_Sync_Dictation_Entity();
         g_app->dictation_overlay_visible = FALSE;
         return;
     }
     if (!IsKeyPressed(KEY_ENTER)) return;
+    if (g_app->dictation_typing &&
+        (IsKeyDown(KEY_LEFT_SHIFT) ||
+            IsKeyDown(KEY_RIGHT_SHIFT))) {
+        return;
+    }
 
-    if (g_app->dictation_partial[0]) {
+    if (g_app->dictation_typing) {
+        Canvas_Entity *p_entity =
+            Canvas_App_Find_Entity(g_app->dictation_entity_id);
+        if (p_entity) p_entity->active = FALSE;
+        Canvas_App_Sync_Typed_Dictation();
+        if (!Canvas_App_Submit_Dictation()) {
+            g_app->dictation_refocus_pending = TRUE;
+        }
+    } else if (g_app->dictation_partial[0]) {
         g_app->dictation_send_pending = TRUE;
         snprintf(
             g_app->dictation_status,
@@ -620,9 +699,16 @@
         !Canvas_Scene_Is_Text_Editing(&g_app->scene) &&
         !Canvas_Web_Chrome_Is_Editing(&g_app->scene) &&
         IsKeyPressed(KEY_M);
+    boolean typing_hotkey =
+        !dev_ui_editing &&
+        !Canvas_Scene_Is_Text_Editing(&g_app->scene) &&
+        !Canvas_Web_Chrome_Is_Editing(&g_app->scene) &&
+        IsKeyPressed(KEY_N);
     if (dictation_hotkey) {
         Canvas_App_Set_Dictation_Active(
             g_app->dictation_requested_active ? FALSE : TRUE);
+    } else if (typing_hotkey) {
+        Canvas_App_Start_Dictation_Typing();
     }
     Canvas_App_Update_Dictation_Overlay_Input();
     Canvas_Web_Chrome_Action chrome_action = CANVAS_WEB_CHROME_NONE;
@@ -660,6 +746,7 @@
     boolean web_input = FALSE;
     if (!block_pointer_input &&
         !dictation_hotkey &&
+        !typing_hotkey &&
         chrome_action == CANVAS_WEB_CHROME_NONE) {
         web_input = Canvas_Web_Surface_Update_Input(
             &g_app->web_surface,
@@ -675,6 +762,11 @@
         &g_app->camera,
         g_app->font,
         block_pointer_input);
+    if (g_app->dictation_refocus_pending) {
+        Canvas_App_Focus_Dictation_Entity();
+        g_app->dictation_refocus_pending = FALSE;
+    }
+    Canvas_App_Sync_Typed_Dictation();
     boolean keyboard_focus =
         Canvas_Scene_Is_Text_Editing(&g_app->scene) ||
         Canvas_Web_Chrome_Is_Editing(&g_app->scene) ||
@@ -717,7 +809,8 @@
                 }
                 Canvas_App_Submit_Dictation();
             } else if (!g_app->dictation_transcript[0] &&
-                !g_app->dictation_partial[0]) {
+                !g_app->dictation_partial[0] &&
+                !g_app->dictation_typing) {
                 g_app->dictation_overlay_visible = FALSE;
             }
         } else if (strcmp(dictation_text, "Listening") == 0 ||