Move SSD manual cell id selection to typed Kotlin

ober

8e73e8b11fb5183a5b51e52a086b8e4987300c43

diff --git a/.build.yml b/.build.yml
index d7e2a8b..dd5cffe 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#5c182bfb123e9d76e0799f63180067818eb085bc"
+  - "https://git.sr.ht/~lisp/jerboa#bb4035286612e288dd9851184464e6450e2dd32c"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 5c182bfb123e9d76e0799f63180067818eb085bc
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 6d011dae14ea399008582a9c0886bf1873278a0b
+      test "$(git -C ../jerboa rev-parse HEAD)" = bb4035286612e288dd9851184464e6450e2dd32c
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 9e10bc1d5b2dbbdbd87aebccdc4a9bacde7a0238
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 7a75e11..06aa54f 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "5c182bfb123e9d76e0799f63180067818eb085bc",
-    "tree": "6d011dae14ea399008582a9c0886bf1873278a0b"
+    "commit": "bb4035286612e288dd9851184464e6450e2dd32c",
+    "tree": "9e10bc1d5b2dbbdbd87aebccdc4a9bacde7a0238"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index b97ea52..036d783 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=5c182bfb123e9d76e0799f63180067818eb085bc # gitsafe:ignore
-jerboa_tree=6d011dae14ea399008582a9c0886bf1873278a0b # gitsafe:ignore
+jerboa_commit=bb4035286612e288dd9851184464e6450e2dd32c # gitsafe:ignore
+jerboa_tree=9e10bc1d5b2dbbdbd87aebccdc4a9bacde7a0238 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 98d310f..b55e93d 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -552,7 +552,7 @@
                 ssdSessionBoundedSsdArea ssdAreaSelectionTooSmall
                 ssdSessionStoreSsdArea ssdSessionSsdAreaOutsideCount
                 ssdSessionSetSsdArea ssdSessionPruneGroupsOutsideSsdArea
-                createManualCells
+                createManualCells selectedOrManualCellIds
                 ssdSessionNextCellId ssdSessionNextGroupId
                 bboxForMutableCells)
         (type Int32)
@@ -688,6 +688,12 @@
         (def (manualCellRows (count : Int32) (columns : Int32)) : Int32
           (let ((rows (/ (+ count (- columns (int32 1))) columns)))
             (if (< rows (int32 1)) (int32 1) rows)))
+        (def (sessionManualCellCount (count : Int32)) : Int32
+          (if (< count (int32 1))
+            (int32 1)
+            (if (> count (int32 400))
+              (int32 400)
+              count)))
         (def (createManualCells (session : SsdSession)
                                 (rect : FloatArray)
                                 (count : Int32)) : (MutableList String)
@@ -730,6 +736,19 @@
                         (mutable-list-add! (SsdSession-cells session) cell)
                         (mutable-list-add! ids (SsdCell-id cell))
                         ids))))))))
+        (def (copyStringIds (ids : (List String))) : (MutableList String)
+          (for/fold ((out (mutable-list-empty String)))
+                    ((i (in-range (int32 0) (list-size ids))))
+            (begin
+              (mutable-list-add! out (list-ref ids i))
+              out)))
+        (def (selectedOrManualCellIds (session : SsdSession)
+                                      (rect : FloatArray)
+                                      (detectedIds : (List String))
+                                      (count : Int32)) : (MutableList String)
+          (if (> (list-size detectedIds) (int32 0))
+            (copyStringIds detectedIds)
+            (createManualCells session rect (sessionManualCellCount count))))
         (def (ssdSessionFindCellById (session : SsdSession)
                                      (id : String)) : (Nullable SsdCell)
           (for/fold ((found (nullable-none SsdCell)))
@@ -6087,11 +6106,7 @@
        "        val session = currentSession ?: return"
        "        val resolvedType = BoxTypes.resolve(this, boxType)"
        "        val cleanLabel = manualGroupLabel(resolvedLabelForBoxTypeAndroid(this, label, boxType, \"\"), resolvedType?.name, \"\")"
-       "        val ids = if (detectedIds.isNotEmpty()) {"
-       "            detectedIds.toMutableList()"
-       "        } else {"
-       "            createManualCells(session, rect, manualCellCount(count))"
-       "        }"
+       "        val ids = selectedOrManualCellIds(session, rect, detectedIds, count)"
        "        val group = SsdGroup("
        "            id = ssdSessionNextGroupId(session),"
        "            label = cleanLabel,"
@@ -6120,11 +6135,7 @@
        "    private fun appendAreaToGroup(rect: FloatArray, detectedIds: List<String>, group: SsdGroup, count: Int) {"
        "        val session = currentSession ?: return"
        "        if (!session.groups.contains(group)) return"
-       "        val ids = if (detectedIds.isNotEmpty()) {"
-       "            detectedIds.toMutableList()"
-       "        } else {"
-       "            createManualCells(session, rect, manualCellCount(count))"
-       "        }"
+       "        val ids = selectedOrManualCellIds(session, rect, detectedIds, count)"
        "        val added = appendUniqueCellIdsToGroup(group, ids)"
        "        ssdSessionRecomputeGroup(session, group)"
        "        group.status = \"reviewed\""