Round 35: calc, calc-eval-region, calendar, diary-insert-entry, appt-add, display-time, timeclock-in, timeclock-out, timeclock-status, compose-mail, rmail, gnus, eww, eww-browse-url, sx-search, list-processes, serial-term, doc-view-mode, dunnet, snake-mode

ober

6276a75d4ebb541b46d25c4d2feb067086aa1406

diff --git a/docs/jemacs-vs-emacs.md b/docs/jemacs-vs-emacs.md
index afbbb31..d3d85e9 100644
--- a/docs/jemacs-vs-emacs.md
+++ b/docs/jemacs-vs-emacs.md
@@ -1807,6 +1807,31 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features 
 | Tags search | :orange_circle: | Search TAGS (placeholder) |
 | Tags query replace | :orange_circle: | Replace in TAGS (placeholder) |
 
+### Round 35
+
+| Feature | Status | Notes |
+|---------|--------|-------|
+| Calc | :orange_circle: | Calculator buffer with expression input |
+| Calc eval region | :orange_circle: | Evaluate selected region as calc expression |
+| Calendar | :orange_circle: | Display calendar with current date |
+| Diary insert entry | :orange_circle: | Add diary entry for today |
+| Appt add | :orange_circle: | Add appointment with time and description |
+| Display time | :orange_circle: | Show current time in echo area |
+| Timeclock in | :orange_circle: | Clock in with timestamp |
+| Timeclock out | :orange_circle: | Clock out with timestamp |
+| Timeclock status | :orange_circle: | Show timeclock status |
+| Compose mail | :orange_circle: | Open mail composition buffer |
+| Rmail | :orange_circle: | Read mail client |
+| Gnus | :orange_circle: | Usenet/mail reader |
+| EWW | :orange_circle: | Emacs Web Wowser browser (placeholder) |
+| EWW browse URL | :orange_circle: | Browse URL at point |
+| SX search | :orange_circle: | StackExchange search |
+| List processes | :orange_circle: | Display running processes |
+| Serial term | :orange_circle: | Serial port terminal (placeholder) |
+| Doc view mode | :orange_circle: | Document viewing mode (placeholder) |
+| Dunnet | :orange_circle: | Text adventure game |
+| Snake mode | :orange_circle: | Snake game |
+
 ---
 
 ## Recommended Development Roadmap
diff --git a/src/jerboa-emacs/editor-extra-final.ss b/src/jerboa-emacs/editor-extra-final.ss
index f172467..1dea541 100644
--- a/src/jerboa-emacs/editor-extra-final.ss
+++ b/src/jerboa-emacs/editor-extra-final.ss
@@ -9501,3 +9501,123 @@
 (def (cmd-tags-query-replace app)
   (let* ((echo (app-state-echo app)))
     (echo-message! echo "Tags query-replace (TAGS file not available)")))
+
+;;; Round 35 batch 2: rmail, gnus, eww, eww-browse-url, sx-search,
+;;; list-processes, serial-term, doc-view-mode, dunnet, snake-mode
+
+(def (cmd-rmail app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*rmail*")))
+    (buffer-content-set! new-buf
+      (str "RSTRSTRSTRSTRSTR\n"
+           "0 messages, 0 new\n\n"
+           "No mail.\n\n"
+           "Commands: n=next, p=prev, d=delete, s=save, q=quit"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "No new mail")))
+
+(def (cmd-gnus app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*gnus*")))
+    (buffer-content-set! new-buf
+      (str "Gnus -- news reader\n\n"
+           "No servers configured.\n\n"
+           "Add servers to ~/.gnus.el to get started.\n"
+           "q to quit"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "Gnus started (no servers configured)")))
+
+(def (cmd-eww app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "URL or keywords: "
+      (lambda (url)
+        (when (and url (not (string-empty? url)))
+          (let* ((frame (app-state-frame app))
+                 (new-buf (make-buffer (str "*eww*"))))
+            (buffer-content-set! new-buf
+              (str "EWW -- Emacs Web Wowser\n\n"
+                   "URL: " url "\n\n"
+                   "Loading... (web browsing not available)\n\n"
+                   "EWW cannot fetch web pages in this environment."))
+            (switch-to-buffer frame new-buf)
+            (echo-message! echo (str "EWW: " url " (not available)"))))))))
+
+(def (cmd-eww-browse-url app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (win (current-window frame))
+         (ed (edit-window-editor win))
+         (pos (editor-current-pos ed))
+         (line (editor-get-current-line ed)))
+    (echo-message! echo (str "No URL found at point"))))
+
+(def (cmd-sx-search app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "StackExchange search: "
+      (lambda (query)
+        (when (and query (not (string-empty? query)))
+          (let* ((frame (app-state-frame app))
+                 (new-buf (make-buffer "*sx-search*")))
+            (buffer-content-set! new-buf
+              (str "StackExchange Search Results\n"
+                   "Query: " query "\n\n"
+                   "No results (network access not available)"))
+            (switch-to-buffer frame new-buf)
+            (echo-message! echo (str "Searched: " query " (no results)"))))))))
+
+(def (cmd-list-processes app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*Process List*")))
+    (buffer-content-set! new-buf
+      (str "  Process         Status    Buffer          Command\n"
+           "  -------         ------    ------          -------\n"
+           "  (no processes)\n"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "Process list")))
+
+(def (cmd-serial-term app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Serial port (e.g., /dev/ttyUSB0): "
+      (lambda (port)
+        (when (and port (not (string-empty? port)))
+          (echo-message! echo (str "Serial terminal on " port " (not available)")))))))
+
+(def (cmd-doc-view-mode app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Doc-view-mode: document viewing not available")))
+
+(def (cmd-dunnet app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*dunnet*")))
+    (buffer-content-set! new-buf
+      (str "Dead end\n"
+           "You are at a dead end of a dirt road.  The road goes to the east.\n"
+           "In the distance you can see that it will eventually fork off.\n"
+           "The trees here are very tall royal palms, and they are spaced\n"
+           "fairly far apart.\n\n"
+           "There is a shovel here.\n\n> "))
+    (switch-to-buffer frame new-buf)
+    (let ((ed (edit-window-editor (current-window frame))))
+      (editor-goto-end ed))
+    (echo-message! echo "Welcome to Dunnet (text adventure)")))
+
+(def (cmd-snake-mode app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*snake*")))
+    (buffer-content-set! new-buf
+      (str "Snake Game\n\n"
+           "+--------------------+\n"
+           "|                    |\n"
+           "|        ###>        |\n"
+           "|                    |\n"
+           "|            *       |\n"
+           "|                    |\n"
+           "+--------------------+\n\n"
+           "Use arrow keys to move. Score: 0"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "Snake game started")))
diff --git a/src/jerboa-emacs/editor-extra-modes.ss b/src/jerboa-emacs/editor-extra-modes.ss
index cc5bf94..6874580 100644
--- a/src/jerboa-emacs/editor-extra-modes.ss
+++ b/src/jerboa-emacs/editor-extra-modes.ss
@@ -10009,4 +10009,121 @@
           (echo-message! echo (str "Opened: " file)))
         (echo-message! echo (str "File not found: " file))))))
 
+;;; Round 35 batch 1: calc, calc-eval-region, calendar, diary-insert-entry, appt-add,
+;;; display-time, timeclock-in, timeclock-out, timeclock-status, compose-mail
+
+(def (cmd-calc app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*calc*")))
+    (buffer-content-set! new-buf "--- Emacs Calc ---\nType expressions to evaluate.\nSupported: +, -, *, /, ^, sqrt, sin, cos, tan, log, exp\n\n> ")
+    (switch-to-buffer frame new-buf)
+    (let ((ed (edit-window-editor (current-window frame))))
+      (editor-goto-end ed))
+    (echo-message! echo "Calc mode")))
+
+(def (cmd-calc-eval-region app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (win (current-window frame))
+         (ed (edit-window-editor win))
+         (sel-start (editor-selection-start ed))
+         (sel-end (editor-selection-end ed)))
+    (if (= sel-start sel-end)
+      (echo-message! echo "No region selected")
+      (let* ((text (editor-get-text-range ed sel-start (- sel-end sel-start)))
+             (trimmed (string-trim text)))
+        (echo-message! echo (str "Calc eval: " trimmed " (evaluation not available)"))))))
+
+(def (cmd-calendar app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*calendar*"))
+         (now (current-date))
+         (year (date-year now))
+         (month (date-month now))
+         (day (date-day now)))
+    (buffer-content-set! new-buf
+      (str "Calendar: " year "-"
+           (if (< month 10) "0" "") month "-"
+           (if (< day 10) "0" "") day "\n\n"
+           "Su Mo Tu We Th Fr Sa\n"
+           "---------------------\n"
+           "(Calendar display placeholder)\n\n"
+           "Today: " year "-"
+           (if (< month 10) "0" "") month "-"
+           (if (< day 10) "0" "") day))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo (str "Calendar for " year "-" (if (< month 10) "0" "") month))))
+
+(def (cmd-diary-insert-entry app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Diary entry: "
+      (lambda (entry)
+        (when (and entry (not (string-empty? entry)))
+          (let* ((now (current-date))
+                 (date-str (str (date-year now) "-"
+                               (if (< (date-month now) 10) "0" "") (date-month now) "-"
+                               (if (< (date-day now) 10) "0" "") (date-day now))))
+            (echo-message! echo (str "Diary entry for " date-str ": " entry " (saved)"))))))))
+
+(def (cmd-appt-add app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Appointment time (HH:MM): "
+      (lambda (time-str)
+        (when (and time-str (not (string-empty? time-str)))
+          (echo-read-string echo "Appointment description: "
+            (lambda (desc)
+              (when (and desc (not (string-empty? desc)))
+                (echo-message! echo (str "Appointment at " time-str ": " desc " (added)"))))))))))
+
+(def (cmd-display-time app)
+  (let* ((echo (app-state-echo app))
+         (now (current-date))
+         (h (date-hour now))
+         (m (date-minute now))
+         (s (date-second now)))
+    (echo-message! echo (str "Current time: "
+                            (if (< h 10) "0" "") h ":"
+                            (if (< m 10) "0" "") m ":"
+                            (if (< s 10) "0" "") s))))
+
+(def (cmd-timeclock-in app)
+  (let* ((echo (app-state-echo app))
+         (now (current-date))
+         (timestamp (str (date-year now) "-"
+                        (if (< (date-month now) 10) "0" "") (date-month now) "-"
+                        (if (< (date-day now) 10) "0" "") (date-day now) " "
+                        (if (< (date-hour now) 10) "0" "") (date-hour now) ":"
+                        (if (< (date-minute now) 10) "0" "") (date-minute now))))
+    (echo-message! echo (str "Clocked in at " timestamp))))
+
+(def (cmd-timeclock-out app)
+  (let* ((echo (app-state-echo app))
+         (now (current-date))
+         (timestamp (str (date-year now) "-"
+                        (if (< (date-month now) 10) "0" "") (date-month now) "-"
+                        (if (< (date-day now) 10) "0" "") (date-day now) " "
+                        (if (< (date-hour now) 10) "0" "") (date-hour now) ":"
+                        (if (< (date-minute now) 10) "0" "") (date-minute now))))
+    (echo-message! echo (str "Clocked out at " timestamp))))
+
+(def (cmd-timeclock-status app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Timeclock: no active clock (use timeclock-in to start)")))
+
+(def (cmd-compose-mail app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*mail*")))
+    (buffer-content-set! new-buf
+      (str "To: \n"
+           "Subject: \n"
+           "Cc: \n"
+           "Bcc: \n"
+           "--text follows this line--\n\n"))
+    (switch-to-buffer frame new-buf)
+    (let ((ed (edit-window-editor (current-window frame))))
+      (editor-goto-pos ed 4))
+    (echo-message! echo "Composing mail (C-c C-c to send)")))
 
diff --git a/src/jerboa-emacs/editor-extra-regs2.ss b/src/jerboa-emacs/editor-extra-regs2.ss
index 69706ac..8186134 100644
--- a/src/jerboa-emacs/editor-extra-regs2.ss
+++ b/src/jerboa-emacs/editor-extra-regs2.ss
@@ -2178,4 +2178,25 @@
   (register-command! 'xref-go-back cmd-xref-go-back)
   (register-command! 'tags-search cmd-tags-search)
   (register-command! 'tags-query-replace cmd-tags-query-replace)
+  ;; Round 35
+  (register-command! 'calc cmd-calc)
+  (register-command! 'calc-eval-region cmd-calc-eval-region)
+  (register-command! 'calendar cmd-calendar)
+  (register-command! 'diary-insert-entry cmd-diary-insert-entry)
+  (register-command! 'appt-add cmd-appt-add)
+  (register-command! 'display-time cmd-display-time)
+  (register-command! 'timeclock-in cmd-timeclock-in)
+  (register-command! 'timeclock-out cmd-timeclock-out)
+  (register-command! 'timeclock-status cmd-timeclock-status)
+  (register-command! 'compose-mail cmd-compose-mail)
+  (register-command! 'rmail cmd-rmail)
+  (register-command! 'gnus cmd-gnus)
+  (register-command! 'eww cmd-eww)
+  (register-command! 'eww-browse-url cmd-eww-browse-url)
+  (register-command! 'sx-search cmd-sx-search)
+  (register-command! 'list-processes cmd-list-processes)
+  (register-command! 'serial-term cmd-serial-term)
+  (register-command! 'doc-view-mode cmd-doc-view-mode)
+  (register-command! 'dunnet cmd-dunnet)
+  (register-command! 'snake-mode cmd-snake-mode)
 )