TickTick's OAuth server requires byte-exact match between the redirect_uri
the plugin sends and the value registered in the developer portal. Users
who register the URI with a trailing slash (common on iOS paste, or by
hand) get rejected with:
error="invalid_grant"
error_description="Invalid redirect: ...vercel.app does not match one
of the registered values: [...vercel.app/]"
(Reported in issue #2.)
Normalize the redirect URI by stripping trailing slashes and whitespace at
all four use/save sites:
- main.ts: startAuthFlow() and exchangeAuthCodeForToken() clean the URI
before sending — protects existing users with a slash already saved
- settings.ts: both Redirect URI inputs strip on save so the persisted
value matches what's displayed
- settings.ts: both descriptions now warn against trailing slashes
- README: new Troubleshooting entry for the invalid_grant error
https://claude.ai/code/session_01V2fsiatACzjUCNwSEhwER9
The task title was built verbatim from the source line, so list/checkbox
markers (- [ ]), bold/italic, links, headings, etc. leaked into TickTick.
Add a stripMarkdown() helper that cleans the title to plain text before
truncation:
- Removes leading list/checkbox/heading/blockquote/ordered markers
- Unwraps links and wiki links to their text
- Strips asterisk emphasis, ~~strike~~, ==highlight==, `code` (matched or
dangling, e.g. a leading ** with no closing pair)
- Leaves single/snake_case underscores intact
The task description still includes the original text and the Open in
Obsidian link unchanged.
https://claude.ai/code/session_01V2fsiatACzjUCNwSEhwER9
Register on workspace 'file-menu' (the event that drives the ⋯ menu
in the upper right of a note view on mobile, and the same menu on
desktop). This is the hook Advanced URI uses for its 'Copy Advanced
URI' item — testing confirmed the mobile screenshot shows file-menu
items, not editor-menu items, which is why the previous editor-menu
registration was invisible on mobile.
Guard the handler so the item only appears when:
- The file is a markdown file
- The active view is a MarkdownView with that exact file open
This keeps it out of file-explorer right-click menus where there'd be
no cursor context.
Update the README to call out the three-dot menu as the first mobile
option (no setup required) above the toolbar-customization paths.
Long-pressing in the editor on iOS surfaces iOS's native selection menu
(Cut/Copy/Paste/Look Up), not Obsidian's editor-menu — so the
'Create TickTick task' item registered on editor-menu was effectively
invisible on mobile, the platform that needed it most. Remove the
handler rather than ship a feature that only works on desktop and
gives mobile users false hope.
Update the README accordingly: split usage into mobile and desktop
sections, drop the editor context menu bullet, and explicitly call
out that the command palette is not the three-dot menu — users have
to add it to the mobile toolbar themselves.
- Replace the question-mark fallback icon on the command and editor
menu item with lucide-circle-check-big — visually distinct from the
square-checkbox toolbar icon that's already taken, while still
reading as a task/check action.
- Rewrite README to reflect the current architecture and surface the
setup flow clearly. The old README still described the long-removed
port-3000 local server and the deprecated 'Create TickTick Task from
Paragraph' command name; replace with mobile and desktop install
paths (BRAT + manual), step-by-step OAuth setup using the new
Vercel-redirected deep link, all four invocation methods (command
palette, hotkey, mobile toolbar, editor context menu), an updated
settings reference matching the new two-view UI, and troubleshooting
for the failure modes users actually hit.
On mobile, hotkeys are not the primary input. Long-pressing text brings
up the editor context menu — the most natural place to act on the
text under the cursor. Register an editor-menu handler that adds the
'Create TickTick task' item alongside Cut/Copy/Paste, calling the same
task-creation logic as the command palette entry.
Refactor the command's body into createTaskFromEditor() so both the
command palette and the context menu share one code path.
iOS WebKit only allows window.open() to escape to the system browser
when called inside the synchronous portion of a user gesture. The
existing startAuthFlow awaits PKCE hashing and saveSettings before
calling window.open, so by the time it fires iOS has lost the gesture
context and silently blocks the popup — Connect appeared to do nothing.
Replace the programmatic window.open with an AuthLinkModal that
displays the auth URL as a real <a> element. The user's tap on the
link is itself a synchronous gesture, so WebKit hands off to Safari
as expected. Works the same on desktop (Electron treats the link the
same way).
- Flip isDesktopOnly to false in manifest.json; the runtime code uses
only platform-safe APIs (requestUrl, crypto.subtle, URLSearchParams).
- Register obsidian://ticktick-callback protocol handler so the OAuth
redirect returns directly into Obsidian on desktop and mobile,
removing the manual copy-paste step.
- Verify state parameter against tempState to mitigate CSRF, and clear
tempCodeVerifier/tempState after a successful exchange.
- Update Vercel callback pages (callback.html, index.html) to redirect
to the obsidian:// deep link instead of displaying the code as text.
Note: the deployed Vercel page must be redeployed for users to see
the new behavior.
- Drop the now-vestigial "Authorization code" paste field from the
settings UI.
Support single-line or paragraph selection via a setting, and allow
the #ticktick tag to be appended or prepended. Defaults to single-line
+ append to fix list formatting issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>