From 8efba22ed34b6eeb28b5f410177e5a1545b5d798 Mon Sep 17 00:00:00 2001 From: shu Date: Tue, 28 Jul 2026 19:16:16 +0800 Subject: 用 Org GTD 工作流替换事情看板 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 注册 text/x-org 内容类型和 CodeMirror 编辑器,支持 TODO 状态流转、层级调整、子树移动与折叠。 新增 Org 文档解析、Agenda 日期分组、源任务定位、侧栏入口和响应式样式,并补充中文使用说明。 快捷键改为仅在 Org 编辑器中按配置加载,默认不绑定;无效 JSON 或键名只禁用自定义键位,不影响编辑器启动。 移除旧事情看板的字段模板、组件、样式和默认入口,统一使用 Org GTD 工作流。 --- tiddlers/plugin-org-gtd-library.tid | 141 ++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 tiddlers/plugin-org-gtd-library.tid (limited to 'tiddlers/plugin-org-gtd-library.tid') diff --git a/tiddlers/plugin-org-gtd-library.tid b/tiddlers/plugin-org-gtd-library.tid new file mode 100644 index 0000000..af41028 --- /dev/null +++ b/tiddlers/plugin-org-gtd-library.tid @@ -0,0 +1,141 @@ +title: $:/plugins/local/org-gtd/library.js +type: application/javascript +module-type: library + +/*\ +title: $:/plugins/local/org-gtd/library.js +type: application/javascript +module-type: library +\*/ +(function() { +"use strict"; + +var TODO_STATES = ["TODO","NEXT","WAIT","DONE"], + TODO_LOOKUP = Object.create(null), + HEADING_PATTERN = /^(\*+)\s+(?:(TODO|NEXT|WAIT|DONE)\s+)?(.*)$/, + TAG_PATTERN = /\s+:([A-Za-z0-9_@#%+.-]+(?::[A-Za-z0-9_@#%+.-]+)*):\s*$/, + DATE_PATTERN = /\b(SCHEDULED|DEADLINE):\s*<([0-9]{4}-[0-9]{2}-[0-9]{2})(?:\s+[^>]*)?>/g; + +TODO_STATES.forEach(function(state) { + TODO_LOOKUP[state] = true; +}); + +function parseHeading(line,lineIndex) { + var match = HEADING_PATTERN.exec(line), + tagMatch, + title, + tags = []; + if(!match) { + return null; + } + title = match[3]; + tagMatch = TAG_PATTERN.exec(title); + if(tagMatch) { + tags = tagMatch[1].split(":"); + title = title.substring(0,tagMatch.index).trim(); + } + return { + level: match[1].length, + state: match[2] || "", + title: title, + tags: tags, + line: lineIndex, + sourceLine: lineIndex + 1, + endLine: lineIndex, + scheduled: "", + deadline: "" + }; +} + +function parse(text) { + var lines = (text || "").split("\n"), + headings = [], + current = null; + lines.forEach(function(line,index) { + var heading = parseHeading(line,index), + match; + if(heading) { + if(current) { + current.endLine = index - 1; + } + headings.push(heading); + current = heading; + return; + } + if(current) { + DATE_PATTERN.lastIndex = 0; + while((match = DATE_PATTERN.exec(line)) !== null) { + if(match[1] === "SCHEDULED") { + current.scheduled = match[2]; + } else { + current.deadline = match[2]; + } + } + } + }); + if(current) { + current.endLine = lines.length - 1; + } + for(var index = 0; index < headings.length; index++) { + var heading = headings[index], + nextIndex = index + 1; + while(nextIndex < headings.length && headings[nextIndex].level > heading.level) { + nextIndex++; + } + heading.subtreeEndLine = nextIndex < headings.length ? headings[nextIndex].line - 1 : lines.length - 1; + } + return {lines: lines,headings: headings}; +} + +function headingAtLine(parsed,line) { + var result = null; + parsed.headings.forEach(function(heading) { + if(heading.line <= line && heading.subtreeEndLine >= line) { + if(!result || heading.level >= result.level) { + result = heading; + } + } + }); + return result; +} + +function cycleState(line) { + var heading = parseHeading(line,0), + stars, + nextState, + rest; + if(!heading) { + return null; + } + stars = new Array(heading.level + 1).join("*"); + if(heading.state) { + nextState = TODO_STATES[(TODO_STATES.indexOf(heading.state) + 1) % TODO_STATES.length]; + rest = line.replace(/^(\*+)\s+(?:TODO|NEXT|WAIT|DONE)\s+/,"$1 " + nextState + " "); + } else { + rest = line.replace(/^(\*+)\s+/,"$1 TODO "); + } + return {line: rest,state: nextState || "TODO",level: stars.length}; +} + +function dateToTime(value) { + var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value || ""), + date; + if(!match) { + return NaN; + } + date = new Date(Number(match[1]),Number(match[2]) - 1,Number(match[3])); + if(date.getFullYear() !== Number(match[1]) || date.getMonth() !== Number(match[2]) - 1 || date.getDate() !== Number(match[3])) { + return NaN; + } + return date.getTime(); +} + +exports.TODO_STATES = TODO_STATES; +exports.TODO_LOOKUP = TODO_LOOKUP; +exports.parseHeading = parseHeading; +exports.parse = parse; +exports.headingAtLine = headingAtLine; +exports.cycleState = cycleState; +exports.dateToTime = dateToTime; + +})(); -- cgit v1.2.3