From 55e9c8d3cd4250583aeb59aa568774e9bbd40e52 Mon Sep 17 00:00:00 2001 From: creamhead Date: Sat, 13 Jun 2026 18:23:11 +0900 Subject: [PATCH] Fix remaining plugin review errors and type safety - Replace setCssStyles() with CSS classes (avoids Obsidian 1.6+ API) - Rename heading "Remember The Milk Settings" -> "RTM Sync" (no-settings-in-heading) - Add RTM API response interfaces to eliminate all any-type warnings - Type callRtmApi return as RtmResponse - Fix remaining Promise void warnings Co-Authored-By: Claude Sonnet 4.6 --- main.js | 32 +++++----- src/main.ts | 167 +++++++++++++++++++++++++++++++--------------------- styles.css | 30 ++++++++++ 3 files changed, 145 insertions(+), 84 deletions(-) diff --git a/main.js b/main.js index 70788e5..bdca83c 100644 --- a/main.js +++ b/main.js @@ -3,24 +3,24 @@ THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ -var X=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var at=Object.prototype.hasOwnProperty;var ot=(T,p)=>{for(var e in p)X(T,e,{get:p[e],enumerable:!0})},lt=(T,p,e,m)=>{if(p&&typeof p=="object"||typeof p=="function")for(let o of rt(p))!at.call(T,o)&&o!==e&&X(T,o,{get:()=>p[o],enumerable:!(m=it(p,o))||m.enumerable});return T};var ct=T=>lt(X({},"__esModule",{value:!0}),T);var ut={};ot(ut,{default:()=>J});module.exports=ct(ut);var h=require("obsidian");function I(T){function p(w,k){return w<>>32-k}function e(w,k){let x=w&1073741824,A=k&1073741824,S=w&2147483648,C=k&2147483648,E=(w&1073741823)+(k&1073741823);return x&A?E^2147483648^S^C:x|A?E&1073741824?E^3221225472^S^C:E^1073741824^S^C:E^S^C}function m(w,k,x){return w&k|~w&x}function o(w,k,x){return w&x|k&~x}function l(w,k,x){return w^k^x}function c(w,k,x){return k^(w|~x)}function d(w,k,x,A,S,C,E){return w=e(w,e(e(m(k,x,A),S),E)),e(p(w,C),k)}function f(w,k,x,A,S,C,E){return w=e(w,e(e(o(k,x,A),S),E)),e(p(w,C),k)}function y(w,k,x,A,S,C,E){return w=e(w,e(e(l(k,x,A),S),E)),e(p(w,C),k)}function u(w,k,x,A,S,C,E){return w=e(w,e(e(c(k,x,A),S),E)),e(p(w,C),k)}function g(w){let k,x=w.length,A=x+8,C=((A-A%64)/64+1)*16,E=Array(C-1),z=0,N=0;for(;N>>29,E}function F(w){let k="",x="",A,S;for(S=0;S<=3;S++)A=w>>>S*8&255,x="0"+A.toString(16),k=k+x.substr(x.length-2,2);return k}function b(w){w=w.replace(/\r\n/g,` -`);let k="";for(let x=0;x127&&A<2048?(k+=String.fromCharCode(A>>6|192),k+=String.fromCharCode(A&63|128)):(k+=String.fromCharCode(A>>12|224),k+=String.fromCharCode(A>>6&63|128),k+=String.fromCharCode(A&63|128))}return k}let r=Array(),a,v,D,$,P,n,i,s,t,B=7,R=12,L=17,_=22,M=5,O=9,j=14,W=20,V=4,K=11,U=16,H=23,G=6,q=10,Q=15,Y=21;for(T=b(T),r=g(T),n=1732584193,i=4023233417,s=2562383102,t=271733878,a=0;a{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!1,o=>{this.insertTasksToEditor(e,o)})}}),this.addCommand({id:"select-rtm-tasks",name:"Select and import tasks",editorCallback:async(e,m)=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,o=>{this.insertTasksToEditor(e,o)})}}),this.addCommand({id:"import-rtm-tasks-custom",name:"Download tasks (Custom Filter)",editorCallback:async(e,m)=>{this.checkAuth()&&new tt(this.app,o=>{this.fetchAndProcessTasks(o,!0,l=>{this.insertTasksToEditor(e,l)})}).open()}}),this.addCommand({id:"create-note-from-rtm-task",name:"Create note from RTM task",callback:async()=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,e=>{this.createNotesFromTasks(e)})}}),this.addCommand({id:"add-rtm-task",name:"Add cursor line to RTM",editorCallback:async(e,m)=>{this.checkAuth()&&await this.addTaskFromEditor(e,m)}}),this.addCommand({id:"complete-rtm-task",name:"Complete task at cursor",editorCallback:async(e,m)=>{this.checkAuth()&&await this.completeTaskInEditor(e)}})}checkAuth(){return!this.settings.apiKey||!this.settings.sharedSecret?(new h.Notice("Please set your API Key and Secret in settings."),!1):this.settings.authToken?!0:(new h.Notice("Please authenticate with RTM from settings."),!1)}async fetchListsMap(){let e={};try{let m=await this.callRtmApi("rtm.lists.getList");if(m.rsp.lists&&m.rsp.lists.list){let o=Array.isArray(m.rsp.lists.list)?m.rsp.lists.list:[m.rsp.lists.list];for(let l of o)l&&l.id&&l.name&&(e[l.id]=l.name)}}catch(m){console.error("List fetch error:",m)}return e}async fetchAndProcessTasks(e,m,o){new h.Notice("Fetching tasks...");try{let l=await this.fetchListsMap(),c=await this.callRtmApi("rtm.tasks.getList",{filter:e,notes:"1"});if(!c.rsp.tasks||!c.rsp.tasks.list){new h.Notice("No tasks found.");return}let d=[],f=Array.isArray(c.rsp.tasks.list)?c.rsp.tasks.list:[c.rsp.tasks.list];for(let y of f){if(!y.taskseries)continue;let u=Array.isArray(y.taskseries)?y.taskseries:[y.taskseries];for(let g of u){let F=g.name,b=Array.isArray(g.task)?g.task[0]:g.task,r=y.id;!r&&g.list_id&&(r=g.list_id),r||(r="MISSING");let a="";b.due&&b.due!==""&&(a=`\u{1F4C5} ${b.due.split("T")[0]}`);let v="";b.start&&b.start!==""&&(v=`\u{1F6EB} ${b.start.split("T")[0]}`);let D="";switch(b.priority){case"1":D="\u{1F53A}";break;case"2":D="\u{1F53C}";break;case"3":D="\u{1F53D}";break}let $="",P=l[r];P&&($=P);let n=[];g.tags&&g.tags.tag&&(Array.isArray(g.tags.tag)?g.tags.tag:[g.tags.tag]).forEach(t=>n.push(t));let i=[];g.notes&&g.notes.note&&(Array.isArray(g.notes.note)?g.notes.note:[g.notes.note]).forEach(t=>{let B=t.$t||t.toString();B&&i.push(B)}),d.push({name:F,due:a,start:v,priority:D,listName:$,tags:n,notes:i,rtmId:{list:r,series:g.id,task:b.id},rawPriority:b.priority,rawDue:b.due})}}m?new Z(this.app,d,y=>{o(y)}).open():o(d)}catch(l){console.error(l),new h.Notice("Fetch error.")}}insertTasksToEditor(e,m){if(m.length===0){new h.Notice("No tasks selected.");return}let o="";for(let l of m){let c="";l.listName&&(c=` #${l.listName.replace(/[\s,.]+/g,"_")}`);let d=l.tags.map(g=>` #${g}`).join(""),f=`[\u{1F42E}](rtm:${l.rtmId.list}:${l.rtmId.series}:${l.rtmId.task})`,y=l.priority?` ${l.priority}`:"",u=l.due?` ${l.due}`:"";if(o+=`- [ ] ${f} ${l.name}${y}${u}${c}${d} -`,this.settings.importWithNotesAndLink){let g=`https://www.rememberthemilk.com/app/#all/${l.rtmId.task}`;o+=` - RTM Link: ${g} -`,l.notes&&l.notes.length>0&&l.notes.forEach(F=>{F.split(` -`).forEach(r=>{r.trim()!==""&&(o+=` - Note: ${r} -`)})})}}e.replaceSelection(o),new h.Notice(`${m.length} tasks inserted.`)}async createNotesFromTasks(e){if(e.length===0){new h.Notice("No tasks selected.");return}let m=0;for(let o of e){let l=o.name.replace(/[\\/:*?"<>|]/g,"-"),c=this.settings.noteCreationFolder.trim();c.endsWith("/")&&(c=c.slice(0,-1)),c.startsWith("/")&&(c=c.slice(1)),c&&await this.ensureFolderExists(c);let d=c?`${c}/${l}.md`:`${l}.md`;this.app.vault.getAbstractFileByPath(d)&&(d=c?`${c}/${l}-${Date.now()}.md`:`${l}-${Date.now()}.md`);let f=o.tags.map(g=>`#${g}`).join(" "),y=o.listName?`#${o.listName.replace(/[\s,.]+/g,"_")}`:"",u=`# ${o.name} +var Y=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var at=Object.prototype.hasOwnProperty;var ot=(E,h)=>{for(var n in h)Y(E,n,{get:h[n],enumerable:!0})},ct=(E,h,n,d)=>{if(h&&typeof h=="object"||typeof h=="function")for(let s of rt(h))!at.call(E,s)&&s!==n&&Y(E,s,{get:()=>h[s],enumerable:!(d=it(h,s))||d.enumerable});return E};var lt=E=>ct(Y({},"__esModule",{value:!0}),E);var ut={};ot(ut,{default:()=>X});module.exports=lt(ut);var m=require("obsidian");function M(E){function h(w,k){return w<>>32-k}function n(w,k){let A=w&1073741824,T=k&1073741824,F=w&2147483648,C=k&2147483648,b=(w&1073741823)+(k&1073741823);return A&T?b^2147483648^F^C:A|T?b&1073741824?b^3221225472^F^C:b^1073741824^F^C:b^F^C}function d(w,k,A){return w&k|~w&A}function s(w,k,A){return w&A|k&~A}function l(w,k,A){return w^k^A}function c(w,k,A){return k^(w|~A)}function u(w,k,A,T,F,C,b){return w=n(w,n(n(d(k,A,T),F),b)),n(h(w,C),k)}function p(w,k,A,T,F,C,b){return w=n(w,n(n(s(k,A,T),F),b)),n(h(w,C),k)}function f(w,k,A,T,F,C,b){return w=n(w,n(n(l(k,A,T),F),b)),n(h(w,C),k)}function g(w,k,A,T,F,C,b){return w=n(w,n(n(c(k,A,T),F),b)),n(h(w,C),k)}function y(w){let k,A=w.length,T=A+8,C=((T-T%64)/64+1)*16,b=Array(C-1),J=0,N=0;for(;N>>29,b}function x(w){let k="",A="",T,F;for(F=0;F<=3;F++)T=w>>>F*8&255,A="0"+T.toString(16),k=k+A.substr(A.length-2,2);return k}function S(w){w=w.replace(/\r\n/g,` +`);let k="";for(let A=0;A127&&T<2048?(k+=String.fromCharCode(T>>6|192),k+=String.fromCharCode(T&63|128)):(k+=String.fromCharCode(T>>12|224),k+=String.fromCharCode(T>>6&63|128),k+=String.fromCharCode(T&63|128))}return k}let o=Array(),i,B,D,R,P,e,t,a,r,L=7,_=12,$=17,I=22,v=5,O=9,W=14,j=20,V=4,K=11,U=16,H=23,G=6,q=10,Q=15,z=21;for(E=S(E),o=y(E),e=1732584193,t=4023233417,a=2562383102,r=271733878,i=0;i{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!1,s=>{this.insertTasksToEditor(n,s)})}}),this.addCommand({id:"select-rtm-tasks",name:"Select and import tasks",editorCallback:async(n,d)=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,s=>{this.insertTasksToEditor(n,s)})}}),this.addCommand({id:"import-rtm-tasks-custom",name:"Download tasks (Custom Filter)",editorCallback:async(n,d)=>{this.checkAuth()&&new tt(this.app,s=>{this.fetchAndProcessTasks(s,!0,l=>{this.insertTasksToEditor(n,l)})}).open()}}),this.addCommand({id:"create-note-from-rtm-task",name:"Create note from RTM task",callback:async()=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,n=>{this.createNotesFromTasks(n)})}}),this.addCommand({id:"add-rtm-task",name:"Add cursor line to RTM",editorCallback:async(n,d)=>{this.checkAuth()&&await this.addTaskFromEditor(n,d)}}),this.addCommand({id:"complete-rtm-task",name:"Complete task at cursor",editorCallback:async(n,d)=>{this.checkAuth()&&await this.completeTaskInEditor(n)}})}checkAuth(){return!this.settings.apiKey||!this.settings.sharedSecret?(new m.Notice("Please set your API Key and Secret in settings."),!1):this.settings.authToken?!0:(new m.Notice("Please authenticate with RTM from settings."),!1)}async fetchListsMap(){var d;let n={};try{let s=await this.callRtmApi("rtm.lists.getList");if((d=s.rsp.lists)!=null&&d.list){let l=Array.isArray(s.rsp.lists.list)?s.rsp.lists.list:[s.rsp.lists.list];for(let c of l)c!=null&&c.id&&(c!=null&&c.name)&&(n[c.id]=c.name)}}catch(s){console.error("List fetch error:",s)}return n}async fetchAndProcessTasks(n,d,s){var l,c,u,p;new m.Notice("Fetching tasks...");try{let f=await this.fetchListsMap(),g=await this.callRtmApi("rtm.tasks.getList",{filter:n,notes:"1"});if(!((l=g.rsp.tasks)!=null&&l.list)){new m.Notice("No tasks found.");return}let y=[],x=Array.isArray(g.rsp.tasks.list)?g.rsp.tasks.list:[g.rsp.tasks.list];for(let S of x){if(!S.taskseries)continue;let o=Array.isArray(S.taskseries)?S.taskseries:[S.taskseries];for(let i of o){let B=i.name,D=Array.isArray(i.task)?i.task[0]:i.task,R=S.id;!R&&i.list_id&&(R=i.list_id),R||(R="MISSING");let P="";D.due&&D.due!==""&&(P=`\u{1F4C5} ${D.due.split("T")[0]}`);let e="";D.start&&D.start!==""&&(e=`\u{1F6EB} ${D.start.split("T")[0]}`);let t="";switch(D.priority){case"1":t="\u{1F53A}";break;case"2":t="\u{1F53C}";break;case"3":t="\u{1F53D}";break}let a=(c=f[R])!=null?c:"",r=[];(u=i.tags)!=null&&u.tag&&(Array.isArray(i.tags.tag)?i.tags.tag:[i.tags.tag]).forEach($=>r.push($));let L=[];(p=i.notes)!=null&&p.note&&(Array.isArray(i.notes.note)?i.notes.note:[i.notes.note]).forEach($=>{var v;let I=(v=$.$t)!=null?v:String($);I&&L.push(I)}),y.push({name:B,due:P,start:e,priority:t,listName:a,tags:r,notes:L,rtmId:{list:R,series:i.id,task:D.id},rawPriority:D.priority,rawDue:D.due})}}d?new Z(this.app,y,S=>{s(S)}).open():s(y)}catch(f){console.error(f),new m.Notice("Fetch error.")}}insertTasksToEditor(n,d){if(d.length===0){new m.Notice("No tasks selected.");return}let s="";for(let l of d){let c="";l.listName&&(c=` #${l.listName.replace(/[\s,.]+/g,"_")}`);let u=l.tags.map(y=>` #${y}`).join(""),p=`[\u{1F42E}](rtm:${l.rtmId.list}:${l.rtmId.series}:${l.rtmId.task})`,f=l.priority?` ${l.priority}`:"",g=l.due?` ${l.due}`:"";if(s+=`- [ ] ${p} ${l.name}${f}${g}${c}${u} +`,this.settings.importWithNotesAndLink){let y=`https://www.rememberthemilk.com/app/#all/${l.rtmId.task}`;s+=` - RTM Link: ${y} +`,l.notes&&l.notes.length>0&&l.notes.forEach(x=>{x.split(` +`).forEach(o=>{o.trim()!==""&&(s+=` - Note: ${o} +`)})})}}n.replaceSelection(s),new m.Notice(`${d.length} tasks inserted.`)}async createNotesFromTasks(n){if(n.length===0){new m.Notice("No tasks selected.");return}let d=0;for(let s of n){let l=s.name.replace(/[\\/:*?"<>|]/g,"-"),c=this.settings.noteCreationFolder.trim();c.endsWith("/")&&(c=c.slice(0,-1)),c.startsWith("/")&&(c=c.slice(1)),c&&await this.ensureFolderExists(c);let u=c?`${c}/${l}.md`:`${l}.md`;this.app.vault.getAbstractFileByPath(u)&&(u=c?`${c}/${l}-${Date.now()}.md`:`${l}-${Date.now()}.md`);let p=s.tags.map(y=>`#${y}`).join(" "),f=s.listName?`#${s.listName.replace(/[\s,.]+/g,"_")}`:"",g=`# ${s.name} -`;u+=`- **RTM Link**: [\u{1F42E} Open Task](rtm:${o.rtmId.list}:${o.rtmId.series}:${o.rtmId.task}) -`,o.due&&(u+=`- **Due**: ${o.due} -`),o.start&&(u+=`- **Start**: ${o.start} -`),o.priority&&(u+=`- **Priority**: ${o.priority} -`),(y||f)&&(u+=`- **Tags**: ${y} ${f} -`),u+=` +`;g+=`- **RTM Link**: [\u{1F42E} Open Task](rtm:${s.rtmId.list}:${s.rtmId.series}:${s.rtmId.task}) +`,s.due&&(g+=`- **Due**: ${s.due} +`),s.start&&(g+=`- **Start**: ${s.start} +`),s.priority&&(g+=`- **Priority**: ${s.priority} +`),(f||p)&&(g+=`- **Tags**: ${f} ${p} +`),g+=` --- -`,o.notes.length>0?(u+=`## Notes +`,s.notes.length>0?(g+=`## Notes -`,o.notes.forEach(g=>{u+=`${g} +`,s.notes.forEach(y=>{g+=`${y} -`})):u+=`(No notes in RTM) -`;try{await this.app.vault.create(d,u),m++}catch(g){console.error(`Failed to create file: ${d}`,g),new h.Notice(`Failed to create note for "${o.name}"`)}}new h.Notice(`${m} notes created.`)}async addTaskFromEditor(e,m){let o=e.getCursor(),c=e.getLine(o.line).replace(/^[-*] \[[ x]\] /,"").replace(/^[-*] /,"").trim();if(!c){new h.Notice("Task name is empty.");return}new h.Notice(`Adding: ${c}`);let d=m.file?m.file.basename:"";try{let f=await this.getTimeline(),y=await this.callRtmApi("rtm.tasks.add",{timeline:f,name:c,parse:"1"}),u=Array.isArray(y.rsp.list)?y.rsp.list[0]:y.rsp.list,g=u.id,F=Array.isArray(u.taskseries)?u.taskseries[0]:u.taskseries,b=F.id,a=(Array.isArray(F.task)?F.task[0]:F.task).id;if(this.settings.defaultDueForNewTask==="today")try{await this.callRtmApi("rtm.tasks.setDueDate",{timeline:f,list_id:g,taskseries_id:b,task_id:a,due:"today",parse:"1"})}catch($){console.error("Failed to set due date",$)}if(d)try{await this.callRtmApi("rtm.tasks.notes.add",{timeline:f,list_id:g,taskseries_id:b,task_id:a,note_title:"Obsidian Link",note_text:`From Obsidian Note: [[${d}]]`})}catch($){console.error("Failed to add note to RTM task",$)}let D=`- [ ] ${`[\u{1F42E}](rtm:${g}:${b}:${a})`} ${c}`;e.setLine(o.line,D),new h.Notice("Added to RTM!")}catch(f){console.error("Add Task Error:",f),new h.Notice("Add error.")}}async completeTaskInEditor(e){let m=e.getCursor(),o=e.getLine(m.line),l=/\(rtm:([\w\d]+):([\w\d]+):([\w\d]+)\)/,c=o.match(l);if(!c){new h.Notice("Error: No RTM Link found.");return}let d=c[1],f=c[2],y=c[3];if(!d||d==="MISSING"){new h.Notice("Error: List ID invalid.");return}new h.Notice("Completing task...");try{let u=await this.getTimeline();await this.callRtmApi("rtm.tasks.complete",{timeline:u,list_id:d,taskseries_id:f,task_id:y});let g=o.replace("- [ ]","- [x]");e.setLine(m.line,g),new h.Notice("Task completed!")}catch(u){console.error("Completion Error:",u),new h.Notice("Completion error.")}}async getTimeline(){if(this.timeline)return this.timeline;let e=await this.callRtmApi("rtm.timelines.create");return this.timeline=e.rsp.timeline,this.timeline}async callRtmApi(e,m={}){var g;let o=this.settings.apiKey,l=this.settings.sharedSecret;if(!o||!l)throw new Error("API Key/Secret missing");let c={...m,method:e,api_key:o,format:"json",auth_token:this.settings.authToken},d=Object.keys(c).sort(),f=l;for(let F of d)f+=F+c[F];c.api_sig=I(f);let y=`${nt}?${new URLSearchParams(c).toString()}`,u=await(0,h.requestUrl)({url:y});if(u.json.rsp.stat!=="ok")throw console.error("API Error:",u.json),new Error(`API Error: ${(g=u.json.rsp.err)==null?void 0:g.msg}`);return u.json}async ensureFolderExists(e){if(!e)return;let m=e.split("/"),o="";for(let l of m)if(l&&(o=o===""?l:`${o}/${l}`,!this.app.vault.getAbstractFileByPath(o)))try{await this.app.vault.createFolder(o)}catch(c){console.error(`Failed to create folder: ${o}`,c)}}async loadSettings(){this.settings=Object.assign({},mt,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},Z=class extends h.Modal{constructor(p,e,m){super(p),this.tasks=e,this.selected=new Array(e.length).fill(!1),this.onSubmit=m}onOpen(){let{contentEl:p}=this;new h.Setting(p).setName(`Select Tasks (${this.tasks.length})`).setHeading();let e=p.createDiv();e.setCssStyles({maxHeight:"400px",overflowY:"auto",marginBottom:"10px"}),this.tasks.forEach((c,d)=>{var F;let f=e.createDiv();f.setCssStyles({display:"flex",alignItems:"center",padding:"5px 0",borderBottom:"1px solid var(--background-modifier-border)"});let y=f.createEl("input",{type:"checkbox"});y.checked=(F=this.selected[d])!=null?F:!1,y.onchange=b=>{this.selected[d]=b.target.checked};let u=f.createSpan();u.setCssStyles({marginLeft:"10px"});let g=u.createEl("b",{text:c.name});c.listName&&u.createEl("small",{text:` [${c.listName}]`}).setCssStyles({color:"var(--text-muted)"}),c.due&&u.createEl("small",{text:` ${c.due}`}).setCssStyles({color:"var(--text-accent)"}),c.notes&&c.notes.length>0&&u.createEl("small",{text:" \u{1F4DD}"})});let m=p.createDiv();m.setCssStyles({display:"flex",justifyContent:"flex-end",gap:"10px"});let o=m.createEl("button",{text:"Select All"});o.onclick=()=>{let c=this.selected.every(Boolean);this.selected.fill(!c),e.querySelectorAll('input[type="checkbox"]').forEach(f=>f.checked=!c),o.textContent=c?"Select All":"Deselect All"};let l=m.createEl("button",{text:"Process Selected"});l.className="mod-cta",l.onclick=()=>{let c=this.tasks.filter((d,f)=>this.selected[f]);this.onSubmit(c),this.close()}}onClose(){this.contentEl.empty()}},tt=class extends h.Modal{constructor(p,e){super(p),this.onSubmit=e}onOpen(){let{contentEl:p}=this;p.createEl("h3",{text:"Filter RTM Tasks"}),new h.Setting(p).setName("Search Query").setDesc("e.g. list:Inbox, due:today").addText(e=>e.setValue("status:incomplete").onChange(m=>{}).inputEl.addEventListener("keydown",m=>{m.key==="Enter"&&(this.onSubmit(e.getValue()),this.close())})),new h.Setting(p).addButton(e=>e.setButtonText("Search").setCta().onClick(()=>{let m=p.querySelector("input");m&&(this.onSubmit(m.value),this.close())}))}onClose(){this.contentEl.empty()}},et=class extends h.PluginSettingTab{constructor(p,e){super(p,e),this.plugin=e}display(){let{containerEl:p}=this;p.empty(),new h.Setting(p).setName("Remember The Milk Settings").setHeading(),p.createEl("p",{text:`Version: ${this.plugin.manifest.version} (Build: 6/13/2026, 6:05:29 PM)`,cls:"setting-item-description"}).setCssStyles({marginBottom:"2em"}),new h.Setting(p).setName("API Key").addText(l=>l.setValue(this.plugin.settings.apiKey).onChange(async c=>{this.plugin.settings.apiKey=c,await this.plugin.saveSettings()})),new h.Setting(p).setName("Shared Secret").addText(l=>l.setValue(this.plugin.settings.sharedSecret).onChange(async c=>{this.plugin.settings.sharedSecret=c,await this.plugin.saveSettings()}));let o=new h.Setting(p).setName("Auth");this.plugin.settings.authToken&&o.setDesc("\u2705 Authorized"),o.addButton(l=>l.setButtonText("Start Auth").onClick(async()=>await this.startAuthProcess())),new h.Setting(p).setName("Default Due Date").setDesc("Setting for new tasks created from editor").addDropdown(l=>l.addOption("none","No Due Date").addOption("today","Today").setValue(this.plugin.settings.defaultDueForNewTask).onChange(async c=>{this.plugin.settings.defaultDueForNewTask=c,await this.plugin.saveSettings()})),new h.Setting(p).setName("Note Creation Folder").setDesc("Folder to create notes from RTM tasks.").addDropdown(l=>{l.addOption("","/ (Vault Root)"),this.app.vault.getAllLoadedFiles().filter(d=>d instanceof h.TFolder).forEach(d=>{d.path!=="/"&&l.addOption(d.path,d.path)}),l.setValue(this.plugin.settings.noteCreationFolder),l.onChange(async d=>{this.plugin.settings.noteCreationFolder=d,await this.plugin.saveSettings()})}),new h.Setting(p).setName("Import with Notes and RTM Link").setDesc("When inserting tasks to the editor, also include RTM task notes and links as sub-items.").addToggle(l=>l.setValue(this.plugin.settings.importWithNotesAndLink).onChange(async c=>{this.plugin.settings.importWithNotesAndLink=c,await this.plugin.saveSettings()}))}async startAuthProcess(){let p=this.plugin,e=p.settings.apiKey,m=p.settings.sharedSecret;if(!e||!m){new h.Notice("Enter keys first");return}let o={method:"rtm.auth.getFrob",api_key:e,format:"json"},l=m;Object.keys(o).sort().forEach(g=>l+=g+o[g]),o.api_sig=I(l);let d=(await(0,h.requestUrl)({url:`${nt}?${new URLSearchParams(o).toString()}`})).json.rsp.frob,f={api_key:e,perms:"delete",frob:d},y=m;Object.keys(f).sort().forEach(g=>y+=g+f[g]);let u=I(y);window.open(`${dt}?api_key=${e}&perms=delete&frob=${d}&api_sig=${u}`),new st(this.app,d,p,()=>{this.display()}).open()}},st=class extends h.Modal{constructor(p,e,m,o){super(p),this.frob=e,this.plugin=m,this.onSuccess=o}onOpen(){let{contentEl:p}=this;new h.Setting(p).addButton(e=>e.setButtonText("Finish Auth").setCta().onClick(async()=>{let m=this.plugin.settings.apiKey,o=this.plugin.settings.sharedSecret,l={method:"rtm.auth.getToken",api_key:m,format:"json",frob:this.frob},c=o;Object.keys(l).sort().forEach(d=>c+=d+l[d]),l.api_sig=I(c);try{let d=await(0,h.requestUrl)({url:`${nt}?${new URLSearchParams(l).toString()}`});d.json.rsp.auth?(this.plugin.settings.authToken=d.json.rsp.auth.token,await this.plugin.saveSettings(),new h.Notice("Success!"),this.onSuccess(),this.close()):new h.Notice("Failed.")}catch(d){new h.Notice("Error.")}}))}onClose(){this.contentEl.empty()}}; +`})):g+=`(No notes in RTM) +`;try{await this.app.vault.create(u,g),d++}catch(y){console.error(`Failed to create file: ${u}`,y),new m.Notice(`Failed to create note for "${s.name}"`)}}new m.Notice(`${d} notes created.`)}async addTaskFromEditor(n,d){var p;let s=n.getCursor(),c=n.getLine(s.line).replace(/^[-*] \[[ x]\] /,"").replace(/^[-*] /,"").trim();if(!c){new m.Notice("Task name is empty.");return}new m.Notice(`Adding: ${c}`);let u=d.file?d.file.basename:"";try{let f=await this.getTimeline(),y=(p=(await this.callRtmApi("rtm.tasks.add",{timeline:f,name:c,parse:"1"})).rsp.tasks)==null?void 0:p.list,x=Array.isArray(y)?y[0]:y!=null?y:{id:"",taskseries:void 0},S=x.id,o=x.taskseries,i=Array.isArray(o)?o[0]:o!=null?o:{id:"",name:"",task:{id:"",due:"",start:"",priority:"",completed:""}},B=i.id,R=(Array.isArray(i.task)?i.task[0]:i.task).id;if(this.settings.defaultDueForNewTask==="today")try{await this.callRtmApi("rtm.tasks.setDueDate",{timeline:f,list_id:S,taskseries_id:B,task_id:R,due:"today",parse:"1"})}catch(t){console.error("Failed to set due date",t)}if(u)try{await this.callRtmApi("rtm.tasks.notes.add",{timeline:f,list_id:S,taskseries_id:B,task_id:R,note_title:"Obsidian Link",note_text:`From Obsidian Note: [[${u}]]`})}catch(t){console.error("Failed to add note to RTM task",t)}let e=`- [ ] ${`[\u{1F42E}](rtm:${S}:${B}:${R})`} ${c}`;n.setLine(s.line,e),new m.Notice("Added to RTM!")}catch(f){console.error("Add Task Error:",f),new m.Notice("Add error.")}}async completeTaskInEditor(n){let d=n.getCursor(),s=n.getLine(d.line),l=/\(rtm:([\w\d]+):([\w\d]+):([\w\d]+)\)/,c=s.match(l);if(!c){new m.Notice("Error: No RTM Link found.");return}let u=c[1],p=c[2],f=c[3];if(!u||u==="MISSING"){new m.Notice("Error: List ID invalid.");return}new m.Notice("Completing task...");try{let g=await this.getTimeline();await this.callRtmApi("rtm.tasks.complete",{timeline:g,list_id:u,taskseries_id:p,task_id:f});let y=s.replace("- [ ]","- [x]");n.setLine(d.line,y),new m.Notice("Task completed!")}catch(g){console.error("Completion Error:",g),new m.Notice("Completion error.")}}async getTimeline(){var d;if(this.timeline)return this.timeline;let n=await this.callRtmApi("rtm.timelines.create");return this.timeline=(d=n.rsp.timeline)!=null?d:"",this.timeline}async callRtmApi(n,d={}){var x;let s=this.settings.apiKey,l=this.settings.sharedSecret;if(!s||!l)throw new Error("API Key/Secret missing");let c={...d,method:n,api_key:s,format:"json",auth_token:this.settings.authToken},u=Object.keys(c).sort(),p=l;for(let S of u)p+=S+c[S];c.api_sig=M(p);let f=`${nt}?${new URLSearchParams(c).toString()}`,y=(await(0,m.requestUrl)({url:f})).json;if(y.rsp.stat!=="ok")throw console.error("API Error:",y),new Error(`API Error: ${(x=y.rsp.err)==null?void 0:x.msg}`);return y}async ensureFolderExists(n){if(!n)return;let d=n.split("/"),s="";for(let l of d)if(l&&(s=s===""?l:`${s}/${l}`,!this.app.vault.getAbstractFileByPath(s)))try{await this.app.vault.createFolder(s)}catch(c){console.error(`Failed to create folder: ${s}`,c)}}async loadSettings(){this.settings=Object.assign({},mt,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},Z=class extends m.Modal{constructor(h,n,d){super(h),this.tasks=n,this.selected=new Array(n.length).fill(!1),this.onSubmit=d}onOpen(){let{contentEl:h}=this;new m.Setting(h).setName(`Select Tasks (${this.tasks.length})`).setHeading();let n=h.createDiv({cls:"rtm-task-list"});this.tasks.forEach((c,u)=>{var y;let p=n.createDiv({cls:"rtm-task-item"}),f=p.createEl("input",{type:"checkbox"});f.checked=(y=this.selected[u])!=null?y:!1,f.onchange=x=>{this.selected[u]=x.target.checked};let g=p.createSpan({cls:"rtm-task-label"});g.createEl("b",{text:c.name}),c.listName&&g.createEl("small",{text:` [${c.listName}]`,cls:"rtm-task-list-name"}),c.due&&g.createEl("small",{text:` ${c.due}`,cls:"rtm-task-due"}),c.notes&&c.notes.length>0&&g.createEl("small",{text:" \u{1F4DD}"})});let d=h.createDiv({cls:"rtm-btn-row"}),s=d.createEl("button",{text:"Select All"});s.onclick=()=>{let c=this.selected.every(Boolean);this.selected.fill(!c),n.querySelectorAll('input[type="checkbox"]').forEach(p=>p.checked=!c),s.textContent=c?"Select All":"Deselect All"};let l=d.createEl("button",{text:"Process Selected"});l.className="mod-cta",l.onclick=()=>{let c=this.tasks.filter((u,p)=>this.selected[p]);this.onSubmit(c),this.close()}}onClose(){this.contentEl.empty()}},tt=class extends m.Modal{constructor(h,n){super(h),this.onSubmit=n}onOpen(){let{contentEl:h}=this;h.createEl("h3",{text:"Filter RTM Tasks"}),new m.Setting(h).setName("Search Query").setDesc("e.g. list:Inbox, due:today").addText(n=>n.setValue("status:incomplete").onChange(d=>{}).inputEl.addEventListener("keydown",d=>{d.key==="Enter"&&(this.onSubmit(n.getValue()),this.close())})),new m.Setting(h).addButton(n=>n.setButtonText("Search").setCta().onClick(()=>{let d=h.querySelector("input");d&&(this.onSubmit(d.value),this.close())}))}onClose(){this.contentEl.empty()}},et=class extends m.PluginSettingTab{constructor(h,n){super(h,n),this.plugin=n}display(){let{containerEl:h}=this;h.empty(),new m.Setting(h).setName("RTM Sync").setHeading(),h.createEl("p",{text:`Version: ${this.plugin.manifest.version} (Build: 6/13/2026, 6:23:04 PM)`,cls:"setting-item-description rtm-version-info"}),new m.Setting(h).setName("API Key").addText(s=>s.setValue(this.plugin.settings.apiKey).onChange(async l=>{this.plugin.settings.apiKey=l,await this.plugin.saveSettings()})),new m.Setting(h).setName("Shared Secret").addText(s=>s.setValue(this.plugin.settings.sharedSecret).onChange(async l=>{this.plugin.settings.sharedSecret=l,await this.plugin.saveSettings()}));let d=new m.Setting(h).setName("Auth");this.plugin.settings.authToken&&d.setDesc("\u2705 Authorized"),d.addButton(s=>s.setButtonText("Start Auth").onClick(async()=>await this.startAuthProcess())),new m.Setting(h).setName("Default Due Date").setDesc("Setting for new tasks created from editor").addDropdown(s=>s.addOption("none","No Due Date").addOption("today","Today").setValue(this.plugin.settings.defaultDueForNewTask).onChange(async l=>{this.plugin.settings.defaultDueForNewTask=l,await this.plugin.saveSettings()})),new m.Setting(h).setName("Note Creation Folder").setDesc("Folder to create notes from RTM tasks.").addDropdown(s=>{s.addOption("","/ (Vault Root)"),this.app.vault.getAllLoadedFiles().filter(c=>c instanceof m.TFolder).forEach(c=>{c.path!=="/"&&s.addOption(c.path,c.path)}),s.setValue(this.plugin.settings.noteCreationFolder),s.onChange(async c=>{this.plugin.settings.noteCreationFolder=c,await this.plugin.saveSettings()})}),new m.Setting(h).setName("Import with Notes and RTM Link").setDesc("When inserting tasks to the editor, also include RTM task notes and links as sub-items.").addToggle(s=>s.setValue(this.plugin.settings.importWithNotesAndLink).onChange(async l=>{this.plugin.settings.importWithNotesAndLink=l,await this.plugin.saveSettings()}))}async startAuthProcess(){var y;let h=this.plugin,n=h.settings.apiKey,d=h.settings.sharedSecret;if(!n||!d){new m.Notice("Enter keys first");return}let s={method:"rtm.auth.getFrob",api_key:n,format:"json"},l=d;Object.keys(s).sort().forEach(x=>l+=x+s[x]),s.api_sig=M(l);let u=(y=(await(0,m.requestUrl)({url:`${nt}?${new URLSearchParams(s).toString()}`})).json.rsp.frob)!=null?y:"",p={api_key:n,perms:"delete",frob:u},f=d;Object.keys(p).sort().forEach(x=>f+=x+p[x]);let g=M(f);window.open(`${dt}?api_key=${n}&perms=delete&frob=${u}&api_sig=${g}`),new st(this.app,u,h,()=>{this.display()}).open()}},st=class extends m.Modal{constructor(h,n,d,s){super(h),this.frob=n,this.plugin=d,this.onSuccess=s}onOpen(){let{contentEl:h}=this;new m.Setting(h).addButton(n=>n.setButtonText("Finish Auth").setCta().onClick(async()=>{let d=this.plugin.settings.apiKey,s=this.plugin.settings.sharedSecret,l={method:"rtm.auth.getToken",api_key:d,format:"json",frob:this.frob},c=s;Object.keys(l).sort().forEach(u=>c+=u+l[u]),l.api_sig=M(c);try{let p=(await(0,m.requestUrl)({url:`${nt}?${new URLSearchParams(l).toString()}`})).json;p.rsp.auth?(this.plugin.settings.authToken=p.rsp.auth.token,await this.plugin.saveSettings(),new m.Notice("Success!"),this.onSuccess(),this.close()):new m.Notice("Failed.")}catch(u){new m.Notice("Error.")}}))}onClose(){this.contentEl.empty()}}; diff --git a/src/main.ts b/src/main.ts index b18c413..bbab07a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,6 +25,50 @@ const DEFAULT_SETTINGS: RtmPluginSettings = { importWithNotesAndLink: false } +// RTM API レスポンス型 +interface RtmResponse { + rsp: { + stat: string; + err?: { msg: string }; + lists?: { list: RtmList | RtmList[] }; + tasks?: { list: RtmTaskList | RtmTaskList[] }; + timeline?: string; + frob?: string; + auth?: { token: string }; + }; +} + +interface RtmList { + id: string; + name: string; +} + +interface RtmTaskList { + id: string; + taskseries?: RtmTaskSeries | RtmTaskSeries[]; +} + +interface RtmTaskSeries { + id: string; + name: string; + list_id?: string; + task: RtmTask | RtmTask[]; + tags?: { tag: string | string[] }; + notes?: { note: RtmNote | RtmNote[] }; +} + +interface RtmTask { + id: string; + due: string; + start: string; + priority: string; + completed: string; +} + +interface RtmNote { + $t?: string; +} + // 内部で扱うタスク情報の構造 interface FormattedTask { name: string; @@ -92,7 +136,7 @@ export default class RtmPlugin extends Plugin { callback: async () => { if (!this.checkAuth()) return; await this.fetchAndProcessTasks('status:incomplete', true, (tasks) => { - this.createNotesFromTasks(tasks); + void this.createNotesFromTasks(tasks); }); } }); @@ -135,10 +179,10 @@ export default class RtmPlugin extends Plugin { const map: Record = {}; try { const res = await this.callRtmApi('rtm.lists.getList'); - if (res.rsp.lists && res.rsp.lists.list) { - const lists = Array.isArray(res.rsp.lists.list) ? res.rsp.lists.list : [res.rsp.lists.list]; + if (res.rsp.lists?.list) { + const lists: RtmList[] = Array.isArray(res.rsp.lists.list) ? res.rsp.lists.list : [res.rsp.lists.list]; for (const l of lists) { - if (l && l.id && l.name) map[l.id] = l.name; + if (l?.id && l?.name) map[l.id] = l.name; } } } catch (e) { console.error("List fetch error:", e); } @@ -152,36 +196,34 @@ export default class RtmPlugin extends Plugin { const listMap = await this.fetchListsMap(); const response = await this.callRtmApi('rtm.tasks.getList', { filter: filterStr, notes: '1' }); - if (!response.rsp.tasks || !response.rsp.tasks.list) { + if (!response.rsp.tasks?.list) { new Notice('No tasks found.'); return; } const parsedTasks: FormattedTask[] = []; - const lists = Array.isArray(response.rsp.tasks.list) ? response.rsp.tasks.list : [response.rsp.tasks.list]; + const lists: RtmTaskList[] = Array.isArray(response.rsp.tasks.list) ? response.rsp.tasks.list : [response.rsp.tasks.list]; for (const list of lists) { if (!list.taskseries) continue; - const seriesArray = Array.isArray(list.taskseries) ? list.taskseries : [list.taskseries]; - + const seriesArray: RtmTaskSeries[] = Array.isArray(list.taskseries) ? list.taskseries : [list.taskseries]; + for (const s of seriesArray) { const name = s.name; - const task = Array.isArray(s.task) ? s.task[0] : s.task; - + const task: RtmTask = Array.isArray(s.task) ? s.task[0] : s.task; + let realListId = list.id; if (!realListId && s.list_id) realListId = s.list_id; - if (!realListId) realListId = "MISSING"; + if (!realListId) realListId = "MISSING"; let dueDisplay = ""; if (task.due && task.due !== "") { - const datePart = task.due.split('T')[0]; - dueDisplay = `📅 ${datePart}`; + dueDisplay = `📅 ${task.due.split('T')[0]}`; } let startDisplay = ""; if (task.start && task.start !== "") { - const startPart = task.start.split('T')[0]; - startDisplay = `🛫 ${startPart}`; + startDisplay = `🛫 ${task.start.split('T')[0]}`; } let priDisplay = ""; @@ -191,27 +233,25 @@ export default class RtmPlugin extends Plugin { case '3': priDisplay = "🔽"; break; } - let listNameDisplay = ""; - const listName = listMap[realListId]; - if (listName) listNameDisplay = listName; + const listNameDisplay = listMap[realListId] ?? ""; const tagArray: string[] = []; - if (s.tags && s.tags.tag) { - const rawTags = Array.isArray(s.tags.tag) ? s.tags.tag : [s.tags.tag]; - rawTags.forEach((t: string) => tagArray.push(t)); + if (s.tags?.tag) { + const rawTags: string[] = Array.isArray(s.tags.tag) ? s.tags.tag : [s.tags.tag]; + rawTags.forEach(t => tagArray.push(t)); } const noteArray: string[] = []; - if (s.notes && s.notes.note) { - const rawNotes = Array.isArray(s.notes.note) ? s.notes.note : [s.notes.note]; - rawNotes.forEach((n: any) => { - const text = n.$t || n.toString(); + if (s.notes?.note) { + const rawNotes: RtmNote[] = Array.isArray(s.notes.note) ? s.notes.note : [s.notes.note]; + rawNotes.forEach((n: RtmNote) => { + const text = n.$t ?? String(n); if (text) noteArray.push(text); }); } parsedTasks.push({ - name: name, + name, due: dueDisplay, start: startDisplay, priority: priDisplay, @@ -331,11 +371,13 @@ export default class RtmPlugin extends Plugin { const timeline = await this.getTimeline(); const res = await this.callRtmApi('rtm.tasks.add', { timeline: timeline, name: taskName, parse: '1' }); - const list = Array.isArray(res.rsp.list) ? res.rsp.list[0] : res.rsp.list; + const rawList = res.rsp.tasks?.list; + const list: RtmTaskList = Array.isArray(rawList) ? rawList[0] : (rawList ?? { id: '', taskseries: undefined }); const listId = list.id; - const series = Array.isArray(list.taskseries) ? list.taskseries[0] : list.taskseries; + const seriesRaw = list.taskseries; + const series: RtmTaskSeries = Array.isArray(seriesRaw) ? seriesRaw[0] : (seriesRaw ?? { id: '', name: '', task: { id: '', due: '', start: '', priority: '', completed: '' } }); const taskSeriesId = series.id; - const taskObj = Array.isArray(series.task) ? series.task[0] : series.task; + const taskObj: RtmTask = Array.isArray(series.task) ? series.task[0] : series.task; const taskId = taskObj.id; // 現在の設定に従ってデフォルト期日を設定 @@ -410,16 +452,16 @@ export default class RtmPlugin extends Plugin { async getTimeline(): Promise { if (this.timeline) return this.timeline; const res = await this.callRtmApi('rtm.timelines.create'); - this.timeline = res.rsp.timeline; - return this.timeline!; + this.timeline = res.rsp.timeline ?? ''; + return this.timeline; } - async callRtmApi(method: string, params: any = {}) { + async callRtmApi(method: string, params: Record = {}): Promise { const apiKey = this.settings.apiKey; const sharedSecret = this.settings.sharedSecret; if(!apiKey || !sharedSecret) throw new Error("API Key/Secret missing"); - const apiParams: any = { ...params, method: method, api_key: apiKey, format: 'json', auth_token: this.settings.authToken }; + const apiParams: Record = { ...params, method, api_key: apiKey, format: 'json', auth_token: this.settings.authToken }; const keys = Object.keys(apiParams).sort(); let sigString = sharedSecret; for (const key of keys) sigString += key + apiParams[key]; @@ -427,11 +469,12 @@ export default class RtmPlugin extends Plugin { const url = `${RTM_REST_URL}?${new URLSearchParams(apiParams).toString()}`; const res = await requestUrl({ url: url }); - if(res.json.rsp.stat !== 'ok') { - console.error('API Error:', res.json); - throw new Error(`API Error: ${res.json.rsp.err?.msg}`); + const json = res.json as RtmResponse; + if(json.rsp.stat !== 'ok') { + console.error('API Error:', json); + throw new Error(`API Error: ${json.rsp.err?.msg}`); } - return res.json; + return json; } async ensureFolderExists(folderPath: string) { @@ -473,12 +516,10 @@ class TaskImportModal extends Modal { const { contentEl } = this; new Setting(contentEl).setName(`Select Tasks (${this.tasks.length})`).setHeading(); - const listContainer = contentEl.createDiv(); - listContainer.setCssStyles({ maxHeight: "400px", overflowY: "auto", marginBottom: "10px" }); + const listContainer = contentEl.createDiv({ cls: "rtm-task-list" }); this.tasks.forEach((task, index) => { - const itemDiv = listContainer.createDiv(); - itemDiv.setCssStyles({ display: "flex", alignItems: "center", padding: "5px 0", borderBottom: "1px solid var(--background-modifier-border)" }); + const itemDiv = listContainer.createDiv({ cls: "rtm-task-item" }); const checkbox = itemDiv.createEl("input", { type: "checkbox" }); checkbox.checked = this.selected[index] ?? false; @@ -486,31 +527,21 @@ class TaskImportModal extends Modal { this.selected[index] = (e.target as HTMLInputElement).checked; }; - const label = itemDiv.createSpan(); - label.setCssStyles({ marginLeft: "10px" }); - - const nameEl = label.createEl("b", { text: task.name }); - if (task.listName) { - const listEl = label.createEl("small", { text: ` [${task.listName}]` }); - listEl.setCssStyles({ color: "var(--text-muted)" }); - } - if (task.due) { - const dueEl = label.createEl("small", { text: ` ${task.due}` }); - dueEl.setCssStyles({ color: "var(--text-accent)" }); - } + const label = itemDiv.createSpan({ cls: "rtm-task-label" }); + label.createEl("b", { text: task.name }); + if (task.listName) label.createEl("small", { text: ` [${task.listName}]`, cls: "rtm-task-list-name" }); + if (task.due) label.createEl("small", { text: ` ${task.due}`, cls: "rtm-task-due" }); if (task.notes && task.notes.length > 0) label.createEl("small", { text: " 📝" }); - void nameEl; }); - const btnDiv = contentEl.createDiv(); - btnDiv.setCssStyles({ display: "flex", justifyContent: "flex-end", gap: "10px" }); + const btnDiv = contentEl.createDiv({ cls: "rtm-btn-row" }); const toggleBtn = btnDiv.createEl("button", { text: "Select All" }); toggleBtn.onclick = () => { const allSelected = this.selected.every(Boolean); this.selected.fill(!allSelected); - const checkboxes = listContainer.querySelectorAll('input[type="checkbox"]'); - checkboxes.forEach((cb: any) => cb.checked = !allSelected); + const checkboxes = listContainer.querySelectorAll('input[type="checkbox"]'); + checkboxes.forEach(cb => cb.checked = !allSelected); // ★修正: .text ではなく .textContent を使用 toggleBtn.textContent = allSelected ? "Select All" : "Deselect All"; }; @@ -559,14 +590,13 @@ class RtmSettingTab extends PluginSettingTab { constructor(app: App, plugin: RtmPlugin) { super(app, plugin); this.plugin = plugin; } display(): void { const {containerEl} = this; containerEl.empty(); - new Setting(containerEl).setName('Remember The Milk Settings').setHeading(); + new Setting(containerEl).setName('RTM Sync').setHeading(); const buildTime = typeof BUILD_TIME !== 'undefined' ? BUILD_TIME : "Unknown"; - const versionEl = containerEl.createEl('p', { + containerEl.createEl('p', { text: `Version: ${this.plugin.manifest.version} (Build: ${buildTime})`, - cls: 'setting-item-description' + cls: 'setting-item-description rtm-version-info' }); - versionEl.setCssStyles({ marginBottom: '2em' }); new Setting(containerEl).setName('API Key').addText(text => text.setValue(this.plugin.settings.apiKey).onChange(async (v) => { this.plugin.settings.apiKey = v; await this.plugin.saveSettings(); })); new Setting(containerEl).setName('Shared Secret').addText(text => text.setValue(this.plugin.settings.sharedSecret).onChange(async (v) => { this.plugin.settings.sharedSecret = v; await this.plugin.saveSettings(); })); @@ -623,13 +653,13 @@ class RtmSettingTab extends PluginSettingTab { const apiKey = rtm.settings.apiKey; const sharedSecret = rtm.settings.sharedSecret; if (!apiKey || !sharedSecret) { new Notice('Enter keys first'); return; } - const frobParams:any = { method: 'rtm.auth.getFrob', api_key: apiKey, format: 'json' }; + const frobParams: Record = { method: 'rtm.auth.getFrob', api_key: apiKey, format: 'json' }; let sigString = sharedSecret; Object.keys(frobParams).sort().forEach(k => sigString += k + frobParams[k]); frobParams['api_sig'] = md5(sigString); const frobRes = await requestUrl({ url: `${RTM_REST_URL}?${new URLSearchParams(frobParams).toString()}` }); - const frob = frobRes.json.rsp.frob; - const authParams:any = { api_key: apiKey, perms: 'delete', frob: frob }; + const frob = (frobRes.json as RtmResponse).rsp.frob ?? ''; + const authParams: Record = { api_key: apiKey, perms: 'delete', frob }; let authSigString = sharedSecret; Object.keys(authParams).sort().forEach(k => authSigString += k + authParams[k]); const apiSig = md5(authSigString); @@ -646,14 +676,15 @@ class TokenModal extends Modal { new Setting(contentEl).addButton(btn => btn.setButtonText('Finish Auth').setCta().onClick(async () => { const apiKey = this.plugin.settings.apiKey; const sharedSecret = this.plugin.settings.sharedSecret; - const tokenParams:any = { method: 'rtm.auth.getToken', api_key: apiKey, format: 'json', frob: this.frob }; + const tokenParams: Record = { method: 'rtm.auth.getToken', api_key: apiKey, format: 'json', frob: this.frob }; let sig = sharedSecret; Object.keys(tokenParams).sort().forEach(k => sig += k + tokenParams[k]); tokenParams['api_sig'] = md5(sig); try { const res = await requestUrl({ url: `${RTM_REST_URL}?${new URLSearchParams(tokenParams).toString()}` }); - if(res.json.rsp.auth) { - this.plugin.settings.authToken = res.json.rsp.auth.token; + const json = res.json as RtmResponse; + if(json.rsp.auth) { + this.plugin.settings.authToken = json.rsp.auth.token; await this.plugin.saveSettings(); new Notice('Success!'); this.onSuccess(); this.close(); } else { new Notice('Failed.'); } diff --git a/styles.css b/styles.css index 58bd873..a02ee80 100644 --- a/styles.css +++ b/styles.css @@ -9,4 +9,34 @@ If your plugin does not need CSS, delete this file. /* IDタグを非表示にする */ body .rtm-id { display: none; +} + +/* Task import modal */ +.rtm-task-list { + max-height: 400px; + overflow-y: auto; + margin-bottom: 10px; +} +.rtm-task-item { + display: flex; + align-items: center; + padding: 5px 0; + border-bottom: 1px solid var(--background-modifier-border); +} +.rtm-task-label { + margin-left: 10px; +} +.rtm-task-list-name { + color: var(--text-muted); +} +.rtm-task-due { + color: var(--text-accent); +} +.rtm-btn-row { + display: flex; + justify-content: flex-end; + gap: 10px; +} +.rtm-version-info { + margin-bottom: 2em; } \ No newline at end of file