diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-04 14:11:48 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-04 14:11:48 -0500 |
| commit | ffc9707790a3dff195bd827c17039faf0dcf44b0 (patch) | |
| tree | aece358117fc5a60c4da3d7c6f2404b758e369f9 | |
| parent | 559231d91ca24a7a7b632261643b9f4eff329fbb (diff) | |
| download | chime-ffc9707790a3dff195bd827c17039faf0dcf44b0.tar.gz chime-ffc9707790a3dff195bd827c17039faf0dcf44b0.zip | |
Show modeline icon immediately on chime-mode enable
Previously chime-modeline-string was nil until the first async check
completed (~10-15 seconds after startup). Now the icon appears
instantly with a "waiting for first event check" tooltip.
On validation or async failure, the icon stays visible and the
tooltip updates to show the error state instead of going blank.
| -rw-r--r-- | chime.el | 58 | ||||
| -rw-r--r-- | tests/test-integration-chime-mode.el | 45 |
2 files changed, 92 insertions, 11 deletions
@@ -1763,7 +1763,8 @@ Does nothing if a check is already in progress." (chime--debug-log-async-error (cdr events))) (chime--log-silently "Chime: Async error: %s" (error-message-string (cdr events))) - (chime--maybe-warn-persistent-failures)) + (chime--maybe-warn-persistent-failures) + (chime--set-modeline-error-state "Event check failed — check *Messages* buffer")) ;; Success - process events normally (setq chime--consecutive-async-failures 0) (when (featurep 'chime-debug) @@ -1776,7 +1777,8 @@ Does nothing if a check is already in progress." (chime--debug-log-async-error err)) (chime--log-silently "Chime: Error processing events: %s" (error-message-string err)) - (chime--maybe-warn-persistent-failures))))))))) + (chime--maybe-warn-persistent-failures) + (chime--set-modeline-error-state "Event check failed — check *Messages* buffer"))))))))) (defun chime--log-silently (format-string &rest args) "Append formatted message to *Messages* buffer without echoing. @@ -1799,17 +1801,25 @@ Returns nil if validation failed and check should be skipped." (progn (setq chime--validation-retry-count (1+ chime--validation-retry-count)) (if (> chime--validation-retry-count chime-validation-max-retries) - (let ((errors (cl-remove-if-not (lambda (i) (eq (car i) :error)) issues))) - (chime--log-silently "Chime: Configuration validation failed with %d error(s) after %d retries:" - (length errors) - chime--validation-retry-count) - (dolist (err errors) - (chime--log-silently "") - (chime--log-silently "ERROR: %s" (cadr err))) - (message "Chime: Configuration errors detected (see *Messages* buffer for details)")) + (progn + (let ((errors (cl-remove-if-not (lambda (i) (eq (car i) :error)) issues))) + (chime--log-silently "Chime: Configuration validation failed with %d error(s) after %d retries:" + (length errors) + chime--validation-retry-count) + (dolist (err errors) + (chime--log-silently "") + (chime--log-silently "ERROR: %s" (cadr err)))) + (message "Chime: Configuration errors detected (see *Messages* buffer for details)") + ;; Update modeline tooltip to show error state + (chime--set-modeline-error-state "Configuration error — check *Messages* buffer")) (message "Chime: Waiting for org-agenda-files to load... (attempt %d/%d)" chime--validation-retry-count - chime-validation-max-retries)) + chime-validation-max-retries) + ;; Update modeline tooltip to show waiting state + (chime--set-modeline-error-state + (format "Waiting for org-agenda-files... (attempt %d/%d)" + chime--validation-retry-count + chime-validation-max-retries))) nil) (setq chime--validation-done t) (setq chime--validation-retry-count 0) @@ -1848,6 +1858,30 @@ Does nothing if a check is already in progress in the background." (lambda (events) (chime--update-modeline events)))) +(defun chime--set-modeline-error-state (error-message) + "Update modeline icon tooltip to show ERROR-MESSAGE. +Keeps the icon visible so the user knows chime is running but has a problem." + (when chime-modeline-no-events-text + (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] #'chime--open-calendar-url) + (setq chime-modeline-string + (propertize chime-modeline-no-events-text + 'help-echo (format "Chime: %s" error-message) + 'mouse-face 'mode-line-highlight + 'local-map map)) + (force-mode-line-update t)))) + +(defun chime--make-initial-modeline-string () + "Create the initial modeline string shown before the first check completes. +Uses `chime-modeline-no-events-text' with a loading tooltip." + (when chime-modeline-no-events-text + (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] #'chime--open-calendar-url) + (propertize chime-modeline-no-events-text + 'help-echo "Chime: waiting for first event check..." + 'mouse-face 'mode-line-highlight + 'local-map map)))) + ;;;###autoload (define-minor-mode chime-mode "Toggle org notifications globally. @@ -1861,6 +1895,8 @@ if needed." ;; Add modeline string to global-mode-string (when (and chime-enable-modeline (> chime-modeline-lookahead-minutes 0)) + ;; Set icon immediately so the user sees chime is active + (setq chime-modeline-string (chime--make-initial-modeline-string)) (if global-mode-string (add-to-list 'global-mode-string 'chime-modeline-string 'append) (setq global-mode-string '("" chime-modeline-string))))) diff --git a/tests/test-integration-chime-mode.el b/tests/test-integration-chime-mode.el index 0f19330..69ad176 100644 --- a/tests/test-integration-chime-mode.el +++ b/tests/test-integration-chime-mode.el @@ -65,5 +65,50 @@ and set it to nil." ;; Timer should be nil after disable (should (null chime--timer)))) +(ert-deftest test-integration-chime-mode-enable-sets-modeline-string-immediately () + "Enabling chime-mode should set chime-modeline-string to a non-nil value +immediately, before the first async check completes." + (let ((chime-enable-modeline t) + (chime-modeline-lookahead-minutes 120) + (chime-modeline-no-events-text " ⏰")) + (unwind-protect + (progn + (chime-mode 1) + ;; Should be non-nil right away, not after startup delay + (should chime-modeline-string) + (should (stringp chime-modeline-string))) + (chime-mode -1)))) + +(ert-deftest test-integration-chime-mode-enable-immediate-string-has-tooltip () + "The immediate modeline string should have a help-echo tooltip." + (let ((chime-enable-modeline t) + (chime-modeline-lookahead-minutes 120) + (chime-modeline-no-events-text " ⏰")) + (unwind-protect + (progn + (chime-mode 1) + (should (get-text-property 0 'help-echo chime-modeline-string))) + (chime-mode -1)))) + +(ert-deftest test-integration-chime-mode-validation-failure-keeps-icon-visible () + "When validation fails, modeline should still show the icon with error info +in the tooltip, not go blank." + (let ((chime-enable-modeline t) + (chime-modeline-lookahead-minutes 120) + (chime-modeline-no-events-text " ⏰") + (org-agenda-files nil) + (chime--validation-done nil) + (chime--validation-retry-count 0) + (chime-validation-max-retries 0)) + (unwind-protect + (progn + (chime-mode 1) + ;; Force a check that will fail validation + (chime-check) + ;; Icon should still be visible, not nil + (should chime-modeline-string) + (should (stringp chime-modeline-string))) + (chime-mode -1)))) + (provide 'test-integration-chime-mode) ;;; test-integration-chime-mode.el ends here |
