Skip to content

feat: icon for choice commands on the mobile toolbar (#766)#1342

Merged
chhoumann merged 1 commit into
masterfrom
chhoumann/766-mobile-toolbar-icon
Jun 13, 2026
Merged

feat: icon for choice commands on the mobile toolbar (#766)#1342
chhoumann merged 1 commit into
masterfrom
chhoumann/766-mobile-toolbar-icon

Conversation

@chhoumann

@chhoumann chhoumann commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Closes #766

Problem

On the Obsidian mobile editing toolbar, every QuickAdd command showed the default question-mark glyph instead of a meaningful icon.

Root cause (still live): addCommandForChoice registered each choice command with no icon. Obsidian's mobile toolbar builder falls back to question-mark-glyph for any iconless command:

// Obsidian compileToolbar()
var i = n.icon;  i || (i = "question-mark-glyph");  tv(t, i)

Verified in the dev vault before the fix: all 26 quickadd:choice:* commands had icon: null.

Fix

Resolve a per-type default icon at registration time and pass it to addCommand:

Choice type Icon
Template file-text
Capture pencil
Macro terminal
Multi folder
(unexpected runtime type) file-plus
  • No migration, no data.json bloat — defaults are resolved at registration time only, never persisted. Every existing choice gains an icon for free.
  • default arm is load-bearing, not decorative: data.json is not runtime-validated and the repo compiles with strict: false and no switch-exhaustiveness lint, so a missing case would return undefined and silently re-introduce the ?.
  • An optional per-choice icon? override is honored when set (a non-empty string wins; typeof-guarded so malformed non-string data can't throw during plugin load). There's no picker UI yet — see follow-up below.

The resolver lives in src/utils/choiceUtils.ts (an App-free core util) and is imported by main.ts, keeping command-icon logic out of GUI metadata.

Verification

  • Unit: resolveChoiceIcon / defaultIconForChoiceType — override wins, blank/whitespace falls back, all four type defaults, the default arm, and non-string data doesn't throw. Full suite: 2021 passing.
  • Dev-vault e2e (real Obsidian): after the change, iconless choice commands dropped 26 → 0; Template→file-text, Capture→pencil, Macro→terminal; a synthetic command-enabled Multifolder; an override→its value; a whitespace override→the type default.
  • tsc --noEmit, eslint ., and build-with-lint all clean.

Known limitation (Obsidian-side, documented)

Obsidian's compileToolbar caches by the command-id list and only rebuilds when that list changes. For a command already pinned to a user's mobile toolbar, the new icon appears after the next app restart (it self-heals, no user action) — consistent with QuickAdd's existing "Requires a reload" pattern for the ribbon toggle. Newly added toolbar entries and the command palette pick up the icon immediately.

Scope note / follow-up

This is the focused fix for #766: it removes the ? for everyone with zero config. A per-choice icon picker (a context-menu "Set icon…" over the icon list writing the icon? override) was designed and adversarially reviewed but intentionally deferred — it's a customization feature beyond the report, with extra UI surface and the toolbar-cache caveat. The icon? field added here is the seam for it if there's demand.


🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Choices now support custom icon assignments. Assign a custom icon to individual choices, which overrides the default icon for that choice type and displays in registered commands. Template, Capture, Macro, and Multi choice types have default icons automatically assigned.

Obsidian's mobile editing toolbar renders the default "question-mark-glyph"
("?") for any command registered without an `icon`. QuickAdd registered every
choice command without one (main.ts addCommandForChoice), so all QuickAdd
commands showed "?" on the mobile toolbar.

Resolve a per-type default icon at registration time and pass it to
addCommand: Template -> file-text, Capture -> pencil, Macro -> terminal,
Multi -> folder, with a file-plus fallback for unexpected runtime types. The
default arm is load-bearing: data.json is not runtime-validated and the repo
compiles with strict:false / no switch-exhaustiveness lint, so a missing case
would return undefined and silently re-introduce the "?".

Defaults are resolved only at registration time and never written to
data.json, so every existing choice gains a meaningful icon with no migration
and no settings bloat. An optional per-choice `icon?` override is honored when
set (typeof-guarded against malformed non-string data); no picker UI yet.

Closes #766
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d4756324-de78-4328-bb97-05541754b1ca

📥 Commits

Reviewing files that changed from the base of the PR and between f111d45 and a5de4d4.

📒 Files selected for processing (5)
  • src/main.ts
  • src/types/choices/Choice.ts
  • src/types/choices/IChoice.ts
  • src/utils/choiceUtils.test.ts
  • src/utils/choiceUtils.ts

📝 Walkthrough

Walkthrough

Adds an optional icon?: string field to the IChoice interface and Choice abstract class. Introduces two exported utilities in choiceUtils.ts: defaultIconForChoiceType (maps ChoiceType to lucide icon ids with a fallback) and resolveChoiceIcon (returns a trimmed per-choice override or the type default). main.ts is updated to pass the resolved icon when registering per-choice commands.

Changes

Per-choice icon resolution for registered commands

Layer / File(s) Summary
Choice type contracts: icon field
src/types/choices/IChoice.ts, src/types/choices/Choice.ts
Adds optional icon?: string to the IChoice interface (with inline JSDoc on override semantics and persistence behavior) and to the Choice abstract class.
Icon resolution utilities and tests
src/utils/choiceUtils.ts, src/utils/choiceUtils.test.ts
Exports defaultIconForChoiceType (switch-based mapping from each ChoiceType to a lucide icon id, falling back to "file-plus") and resolveChoiceIcon (uses a trimmed non-empty string override from choice.icon, otherwise delegates to defaultIconForChoiceType). Tests cover all type mappings, unknown/undefined types, override precedence, whitespace trimming, and non-string icon robustness.
Command registration wiring
src/main.ts
Imports resolveChoiceIcon and passes resolveChoiceIcon(choice) as the icon property when creating per-choice Obsidian commands.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 Hop, hop, a little icon appears,
Each choice now wears its symbol with cheer.
Template gets a plus, Capture a note,
Macro taps code, Multi layers its coat.
Unknown? No fear — file-plus is there!
Every command shines with icons to spare. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding icons to choice commands on the mobile toolbar, which directly addresses the PR objective of fixing the default question-mark glyph issue.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chhoumann/766-mobile-toolbar-icon

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: a5de4d4
Status: ✅  Deploy successful!
Preview URL: https://435615af.quickadd.pages.dev
Branch Preview URL: https://chhoumann-766-mobile-toolbar.quickadd.pages.dev

View logs

@chhoumann chhoumann merged commit 6e6f959 into master Jun 13, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE REQUEST] Icon for QuickAdd on mobile toolbar

1 participant