mirror of
https://github.com/heymoosh/ticktick-quick-add-obsidian.git
synced 2026-07-22 05:43:55 +00:00
- manifest: drop "Obsidian" from description (directory rule), remove unknown `main` field, bump to 1.2.1 - README: title now matches plugin name in manifest - Remove unused base64UrlEncode helper - Remove dead express dependency and server.js (clears qs / path-to-regexp vulnerability warnings) - package.json: real name + author + description, drop sample-plugin template metadata - versions.json: add 1.2.1 → 1.4.0 https://claude.ai/code/session_01V2fsiatACzjUCNwSEhwER9
10 lines
14 KiB
JavaScript
10 lines
14 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var T=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var U=(a,e)=>{for(var n in e)T(a,n,{get:e[n],enumerable:!0})},_=(a,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of I(e))!A.call(a,t)&&t!==n&&T(a,t,{get:()=>e[t],enumerable:!(i=E(e,t))||i.enumerable});return a};var N=a=>_(T({},"__esModule",{value:!0}),a);var O={};U(O,{default:()=>k});module.exports=N(O);var x=require("obsidian"),o=require("obsidian");var c=require("obsidian"),m={accessToken:"",clientId:"",clientSecret:"",redirectUri:"https://ticktick-quick-add-obsidian-6yawfmvnj-mooshs-projects-0635287d.vercel.app",selectionMode:"line",tagPosition:"append"},u=class extends c.PluginSettingTab{constructor(e,n){super(e,n),this.plugin=n}display(){let{containerEl:e}=this;e.empty(),this.plugin.settings.accessToken?this.renderConnectedView(e):this.renderSetupView(e)}renderSetupView(e){e.createEl("h2",{text:"Set up TickTick"}),e.createEl("p",{text:"Connect this plugin to your TickTick account in two steps. You only need to do this once."}),new c.Setting(e).setName("Step 1 \u2014 Enter your TickTick app credentials").setHeading();let n=e.createEl("p");n.appendText("Sign in to the ");let i=n.createEl("a",{text:"TickTick Developer Portal",href:"https://developer.ticktick.com/"});i.setAttr("target","_blank"),i.setAttr("rel","noopener"),n.appendText(", create an app, and copy its Client ID and Client Secret into the fields below. In the portal, set the app's Redirect URI to the value shown in the Redirect URI field."),new c.Setting(e).setName("Client ID").addText(t=>t.setPlaceholder("Your Client ID").setValue(this.plugin.settings.clientId||"").onChange(async s=>{this.plugin.settings.clientId=s.trim(),await this.plugin.saveSettings()})),new c.Setting(e).setName("Client Secret").addText(t=>(t.inputEl.type="password",t.setPlaceholder("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022").setValue(this.plugin.settings.clientSecret||"").onChange(async s=>{this.plugin.settings.clientSecret=s.trim(),await this.plugin.saveSettings()}),t)),new c.Setting(e).setName("Redirect URI").setDesc("Copy this exact value into your TickTick app's Redirect URI field in the developer portal. Only change it here if you self-host the callback page.").addText(t=>t.setPlaceholder(m.redirectUri).setValue(this.plugin.settings.redirectUri||"").onChange(async s=>{this.plugin.settings.redirectUri=s.trim(),await this.plugin.saveSettings()})),new c.Setting(e).setName("Step 2 \u2014 Authorize").setHeading(),e.createEl("p",{text:"Tap Connect, then tap the link in the popup to sign in to TickTick and approve access. The callback page will either return you to Obsidian automatically, or show you an authorization code \u2014 if you see a code, copy it and paste it in the field below."}),new c.Setting(e).setName("Connect to TickTick").addButton(t=>{t.setButtonText("Connect").setCta().onClick(async()=>{await this.plugin.startAuthFlow()})}),new c.Setting(e).setName("Authorization code").setDesc("Only needed if the callback page showed you a code instead of returning you to Obsidian automatically.").addText(t=>t.setPlaceholder("Paste authorization code").onChange(async s=>{s.trim()&&(await this.plugin.exchangeAuthCodeForToken(s.trim()),t.setValue(""),this.display())}))}renderConnectedView(e){e.createEl("h2",{text:"Connected to TickTick"}),e.createEl("p",{text:'Use the "Create TickTick task" command from any note to send the current line or paragraph to TickTick.'}),new c.Setting(e).setName("Plugin behavior").setHeading(),new c.Setting(e).setName("Selection mode").setDesc("Capture only the current line or the entire paragraph.").addDropdown(t=>t.addOption("line","Current line").addOption("paragraph","Entire paragraph").setValue(this.plugin.settings.selectionMode).onChange(async s=>{this.plugin.settings.selectionMode=s,await this.plugin.saveSettings()})),new c.Setting(e).setName("Tag position").setDesc("Where the #ticktick tag is added relative to the text.").addDropdown(t=>t.addOption("append","Append (end)").addOption("prepend","Prepend (beginning)").setValue(this.plugin.settings.tagPosition).onChange(async s=>{this.plugin.settings.tagPosition=s,await this.plugin.saveSettings()}));let n=e.createEl("details",{cls:"ticktick-advanced"});n.style.marginTop="2em";let i=n.createEl("summary",{text:"Advanced \u2014 Connection details"});i.style.cursor="pointer",i.style.fontWeight="600",i.style.padding="0.5em 0",new c.Setting(n).setName("Reconnect").setDesc("Re-run the OAuth flow. Use if your token can no longer be refreshed.").addButton(t=>{t.setButtonText("Reconnect").onClick(async()=>{await this.plugin.startAuthFlow()})}),new c.Setting(n).setName("Authorization code").setDesc("Manual fallback if reconnecting did not return you to Obsidian automatically.").addText(t=>t.setPlaceholder("Paste authorization code").onChange(async s=>{s.trim()&&(await this.plugin.exchangeAuthCodeForToken(s.trim()),t.setValue(""),this.display())})),new c.Setting(n).setName("Disconnect").setDesc("Clear stored tokens. You will need to authorize again to create tasks.").addButton(t=>{t.setButtonText("Disconnect").setWarning().onClick(async()=>{this.plugin.settings.accessToken="",this.plugin.settings.refreshToken=void 0,this.plugin.settings.tokenExpiry=void 0,await this.plugin.saveSettings(),new c.Notice("Disconnected from TickTick."),this.display()})}),new c.Setting(n).setName("Client ID").addText(t=>t.setValue(this.plugin.settings.clientId||"").onChange(async s=>{this.plugin.settings.clientId=s.trim(),await this.plugin.saveSettings()})),new c.Setting(n).setName("Client Secret").addText(t=>(t.inputEl.type="password",t.setPlaceholder("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022").setValue(this.plugin.settings.clientSecret||"").onChange(async s=>{this.plugin.settings.clientSecret=s.trim(),await this.plugin.saveSettings()}),t)),new c.Setting(n).setName("Redirect URI").addText(t=>t.setValue(this.plugin.settings.redirectUri||"").onChange(async s=>{this.plugin.settings.redirectUri=s.trim(),await this.plugin.saveSettings()})),new c.Setting(n).setName("Access token").setDesc("Stored access token (read-only).").addText(t=>{t.inputEl.readOnly=!0,t.setValue(this.plugin.settings.accessToken||"")}),new c.Setting(n).setName("Refresh token").setDesc("Stored refresh token (read-only).").addText(t=>{t.inputEl.readOnly=!0,t.setValue(this.plugin.settings.refreshToken||"")})}};function w(a){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",n="";for(let i=0;i<a;i++)n+=e.charAt(Math.floor(Math.random()*e.length));return n}function D(a,e){return{text:a.getLine(e),line:e}}function R(a,e){let n=e,i=e,t=a.lineCount();for(;n>0&&a.getLine(n-1).trim()!=="";)n--;for(;i<t-1&&a.getLine(i+1).trim()!=="";)i++;let s="";for(let r=n;r<=i;r++)s+=a.getLine(r)+`
|
|
`;return{text:s.trim(),start:n,end:i}}async function V(){let a=w(64),n=new TextEncoder().encode(a),i=await crypto.subtle.digest("SHA-256",n),t=(0,x.arrayBufferToBase64)(i).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");return{codeVerifier:a,codeChallenge:t}}var y=class extends o.Modal{constructor(n,i){super(n);this.authUrl=i}onOpen(){let{contentEl:n}=this;n.empty(),n.createEl("h2",{text:"Authorize TickTick"}),n.createEl("p",{text:"Tap the link below to open the TickTick login page. After you approve access, you will be returned to Obsidian automatically."});let i=n.createEl("a",{text:"Open TickTick login",href:this.authUrl});i.setAttr("target","_blank"),i.setAttr("rel","noopener"),i.style.display="inline-block",i.style.marginTop="0.5em",i.style.fontWeight="bold"}onClose(){this.contentEl.empty()}};function f(a,e,n){console.log(JSON.stringify({timestamp:new Date().toISOString(),plugin:"ticktick-integration",action:e,...n,clientId:a.settings.clientId?"***":void 0,accessToken:a.settings.accessToken?"***":void 0}))}var k=class extends o.Plugin{async onload(){console.log("Loading TickTick Plugin"),await this.loadSettings(),this.addSettingTab(new u(this.app,this)),this.registerObsidianProtocolHandler("ticktick-callback",async e=>{if(!e.code){new o.Notice("TickTick auth failed: no authorization code returned.");return}if(!this.settings.tempState||e.state!==this.settings.tempState){new o.Notice("TickTick auth failed: state mismatch. Please try connecting again.");return}await this.exchangeAuthCodeForToken(e.code)}),this.addCommand({id:"create-ticktick-task",name:"Create TickTick task",icon:"lucide-circle-check-big",editorCallback:(e,n)=>this.createTaskFromEditor(e,n)}),this.registerEvent(this.app.workspace.on("file-menu",(e,n)=>{if(!(n instanceof o.TFile)||n.extension!=="md")return;let i=this.app.workspace.getActiveViewOfType(o.MarkdownView);!i||!i.editor||i.file!==n||e.addItem(t=>{t.setTitle("Create TickTick task").setIcon("lucide-circle-check-big").onClick(async()=>{await this.createTaskFromEditor(i.editor,i)})})}))}async createTaskFromEditor(e,n){let i=e.getCursor();if(!n.file){new o.Notice("No file found for the current view!");return}let t,s,r;if(this.settings.selectionMode==="paragraph"){let{text:h,start:g,end:P}=R(e,i.line);t=h,s=g,r=P}else{let{text:h,line:g}=D(e,i.line);t=h,s=g,r=g}if(!t.trim()){new o.Notice("No text found on the current line!");return}let d=w(8),l=this.settings.tagPosition==="prepend"?`#ticktick ${t} ^${d}`:`${t} #ticktick ^${d}`;e.replaceRange(l,{line:s,ch:0},{line:r,ch:e.getLine(r).length});let p=this.app.vault.getName(),S=n.file.path,C=`obsidian://advanced-uri?vault=${encodeURIComponent(p)}&filepath=${encodeURIComponent(S)}&block=${encodeURIComponent(d)}`,b=`${t}
|
|
|
|
[Open in Obsidian](${C})`,v=t.length>50?t.substring(0,50)+"...":t;await this.ensureFreshToken();try{(await this.createTicktickTask(v,b)).success?new o.Notice("TickTick task created successfully!"):new o.Notice("Failed to create TickTick task.")}catch(h){f(this,"task_creation_failed",{error:h.message}),new o.Notice(`Failed to create task: ${h.message}
|
|
Check console for details.`)}}onunload(){console.log("Unloading TickTick Plugin")}async startAuthFlow(){let e=this.settings.clientId;if(!e){new o.Notice("Please enter your Client ID in the settings.");return}let n=this.settings.redirectUri||"https://ticktick-quick-add-obsidian-6yawfmvnj-mooshs-projects-0635287d.vercel.app",i=encodeURIComponent("tasks:read tasks:write"),{codeVerifier:t,codeChallenge:s}=await V(),r=w(32);this.settings.tempCodeVerifier=t,this.settings.tempState=r,await this.saveSettings();let d=`https://ticktick.com/oauth/authorize?client_id=${e}&redirect_uri=${encodeURIComponent(n)}&response_type=code&scope=${i}&code_challenge=${s}&code_challenge_method=S256&state=${r}`;new y(this.app,d).open()}async exchangeAuthCodeForToken(e){let n="https://ticktick.com/oauth/token",i=this.settings.redirectUri||"https://ticktick-quick-add-obsidian-6yawfmvnj-mooshs-projects-0635287d.vercel.app",t=this.settings.clientId,s=this.settings.clientSecret,r=this.settings.tempCodeVerifier;if(!r||!t||!s){new o.Notice("Missing required credentials. Please update your settings.");return}let d=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:i,client_id:t,client_secret:s,code_verifier:r});try{let l=await(0,o.requestUrl)({url:n,method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},body:d.toString()});if(l.status===200){let p=l.json;p.refresh_token?this.settings.refreshToken=p.refresh_token:(console.warn("Refresh token not received during token exchange."),new o.Notice("Refresh token not received. Re-authentication may be required more often.")),this.settings.accessToken=p.access_token,this.settings.tokenExpiry=Date.now()+p.expires_in*1e3*.85,this.settings.tempCodeVerifier=void 0,this.settings.tempState=void 0,await this.saveSettings(),new o.Notice("TickTick access token obtained successfully!")}else console.error("Token exchange error (status):",l.status),console.log("response.json:",l.json),console.log("response.text:",l.text),new o.Notice("Failed to obtain access token.")}catch(l){console.error("Error during token exchange:",l),new o.Notice("Error during token exchange.")}}async refreshAccessToken(){if(!this.settings.refreshToken)throw new Error("No refresh token available. Please reconnect.");let e="https://ticktick.com/oauth/token",n=this.settings.clientId,i=this.settings.clientSecret,t=new URLSearchParams({grant_type:"refresh_token",refresh_token:this.settings.refreshToken,client_id:n,client_secret:i});try{let s=await(0,o.requestUrl)({url:e,method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},body:t.toString()});if(s.status===200){let r=s.json;r.refresh_token&&(this.settings.refreshToken=r.refresh_token),this.settings.accessToken=r.access_token,this.settings.tokenExpiry=Date.now()+r.expires_in*1e3*.85,await this.saveSettings(),new o.Notice("Access token refreshed successfully!")}else throw console.error("Refresh token error:",s.text),new Error("Failed to refresh token")}catch(s){throw console.error("Error refreshing access token:",s),s}}async ensureFreshToken(){if(this.settings.tokenExpiry&&Date.now()>this.settings.tokenExpiry){f(this,"token_expired",{tokenExpiry:this.settings.tokenExpiry});try{await this.refreshAccessToken()}catch(e){throw new o.Notice("Reauthentication required. Please reconnect to TickTick."),e}}}async createTicktickTask(e,n){let i=this.settings.accessToken;if(!i)return new o.Notice("No access token found. Please connect to TickTick in the settings."),{success:!1};let t="https://api.ticktick.com/open/v1/task",s={title:e,content:n};try{f(this,"task_creation_request",{endpoint:t,title:e});let r=await(0,o.requestUrl)({url:t,method:"POST",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},body:JSON.stringify(s)});return r.status===200?{success:!0}:(console.error("TickTick API error (status):",r.status),console.log("response.json:",r.json),console.log("response.text:",r.text),{success:!1})}catch(r){return console.error("TickTick API error:",r),{success:!1}}}async loadSettings(){this.settings=Object.assign({},m,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}};
|