imdone-core 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dependency-cruiser.cjs +412 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/workflows/ci.yml +24 -0
- package/.imdoneignore +13 -0
- package/.mocharc.json +10 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +160 -0
- package/_index.md +7 -0
- package/_navbar.md +1 -0
- package/build.js +57 -0
- package/esm-migration.md +446 -0
- package/index.html +184 -0
- package/lib/FileProjectContext.js +13 -0
- package/lib/ProjectContext.js +43 -0
- package/lib/adapters/date-time.js +15 -0
- package/lib/adapters/environment.js +3 -0
- package/lib/adapters/file-gateway.js +154 -0
- package/lib/adapters/git-download.js +55 -0
- package/lib/adapters/index.js +9 -0
- package/lib/adapters/logger.js +136 -0
- package/lib/adapters/markdown.js +180 -0
- package/lib/adapters/parsers/DateLanguageParser.js +12 -0
- package/lib/adapters/parsers/content-transformer.js +116 -0
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +36 -0
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +126 -0
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +17 -0
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +106 -0
- package/lib/adapters/parsers/file/index.js +4 -0
- package/lib/adapters/parsers/function-parser.js +25 -0
- package/lib/adapters/parsers/index.js +5 -0
- package/lib/adapters/parsers/task/CardContentParser.js +367 -0
- package/lib/adapters/parsers/task/CheckStyleTaskParser.js +28 -0
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +27 -0
- package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/TaskParser.js +38 -0
- package/lib/adapters/parsers/task/index.js +6 -0
- package/lib/adapters/storage/config.js +44 -0
- package/lib/adapters/storage/index.js +2 -0
- package/lib/adapters/storage/tags.js +29 -0
- package/lib/adapters/yaml.js +9 -0
- package/lib/card.js +299 -0
- package/lib/checksum.js +22 -0
- package/lib/config.js +249 -0
- package/lib/constants.js +68 -0
- package/lib/context/ApplicationContext.js +35 -0
- package/lib/context/index.js +1 -0
- package/lib/default-settings.js +37 -0
- package/lib/domain/entities/View.js +23 -0
- package/lib/domain/entities/index.js +1 -0
- package/lib/domain/index.js +2 -0
- package/lib/domain/usecases/GetViewsUsecase.js +44 -0
- package/lib/domain/usecases/SaveViewUsecase.js +51 -0
- package/lib/domain/usecases/index.js +2 -0
- package/lib/file.js +1391 -0
- package/lib/get-tasks-to-modify.js +117 -0
- package/lib/languages.js +113 -0
- package/lib/list.js +56 -0
- package/lib/migrate-config.js +165 -0
- package/lib/mixins/index.js +2 -0
- package/lib/mixins/repo-fs-store.js +359 -0
- package/lib/mixins/repo-watched-fs-store.js +206 -0
- package/lib/plugins/archive-plugin.js +72 -0
- package/lib/plugins/default-board-actions-plugin.js +56 -0
- package/lib/plugins/default-board-properties-plugin.js +101 -0
- package/lib/plugins/default-plugins.js +7 -0
- package/lib/plugins/epic-plugin.js +177 -0
- package/lib/plugins/extension-plugin.js +94 -0
- package/lib/plugins/index.js +9 -0
- package/lib/plugins/persist-tags-plugin.js +14 -0
- package/lib/plugins/plugin-manager.js +458 -0
- package/lib/plugins/plugin-registry.js +57 -0
- package/lib/project-factory.js +42 -0
- package/lib/project.js +864 -0
- package/lib/repository.js +1514 -0
- package/lib/task.js +780 -0
- package/lib/tools.js +176 -0
- package/lib/usecases/actions.js +32 -0
- package/lib/usecases/get-imdone-url.js +6 -0
- package/lib/usecases/get-project-tags.js +7 -0
- package/lib/usecases/get-tasks-in-file.js +26 -0
- package/lib/usecases/index.js +4 -0
- package/package.json +2 -6
- package/scripts/delete-markdown-indexes.js +87 -0
- package/scripts/generate-index.js +35 -0
- package/scripts/index-markdown.js +55 -0
- package/tsconfig.json +15 -0
- package/types/FileProjectContext.d.ts +4 -0
- package/types/ProjectContext.d.ts +9 -0
- package/types/adapters/date-time.d.ts +2 -0
- package/types/adapters/environment.d.ts +1 -0
- package/types/adapters/file-gateway.d.ts +51 -0
- package/types/adapters/git-download.d.ts +1 -0
- package/types/adapters/index.d.ts +9 -0
- package/types/adapters/logger.d.ts +24 -0
- package/types/adapters/markdown.d.ts +13 -0
- package/types/adapters/parsers/DateLanguageParser.d.ts +1 -0
- package/types/adapters/parsers/content-transformer.d.ts +10 -0
- package/types/adapters/parsers/file/LocalFileParserFactory.d.ts +5 -0
- package/types/adapters/parsers/file/ReadStreamCodeParser.d.ts +13 -0
- package/types/adapters/parsers/file/ReadStreamMarkdownParser.d.ts +7 -0
- package/types/adapters/parsers/file/ReadStreamTaskParser.d.ts +19 -0
- package/types/adapters/parsers/file/index.d.ts +4 -0
- package/types/adapters/parsers/function-parser.d.ts +1 -0
- package/types/adapters/parsers/index.d.ts +5 -0
- package/types/adapters/parsers/task/CardContentParser.d.ts +45 -0
- package/types/adapters/parsers/task/CheckStyleTaskParser.d.ts +12 -0
- package/types/adapters/parsers/task/CodeStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/HashStyleTaskParser.d.ts +13 -0
- package/types/adapters/parsers/task/MarkdownStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/TaskParser.d.ts +7 -0
- package/types/adapters/parsers/task/index.d.ts +6 -0
- package/types/adapters/storage/config.d.ts +6 -0
- package/types/adapters/storage/index.d.ts +2 -0
- package/types/adapters/storage/tags.d.ts +3 -0
- package/types/adapters/yaml.d.ts +2 -0
- package/types/card.d.ts +166 -0
- package/types/checksum.d.ts +8 -0
- package/types/config.d.ts +61 -0
- package/types/constants.d.ts +46 -0
- package/types/context/ApplicationContext.d.ts +24 -0
- package/types/context/index.d.ts +1 -0
- package/types/default-settings.d.ts +2 -0
- package/types/domain/entities/FileProjectContext.d.ts +5 -0
- package/types/domain/entities/View.d.ts +17 -0
- package/types/domain/entities/index.d.ts +1 -0
- package/types/domain/index.d.ts +2 -0
- package/types/domain/usecases/GetViewsUsecase.d.ts +4 -0
- package/types/domain/usecases/SaveViewUsecase.d.ts +8 -0
- package/types/domain/usecases/index.d.ts +2 -0
- package/types/file.d.ts +344 -0
- package/types/get-tasks-to-modify.d.ts +3 -0
- package/types/index.d.ts +23 -0
- package/types/languages.d.ts +649 -0
- package/types/list.d.ts +30 -0
- package/types/migrate-config.d.ts +1 -0
- package/types/mixins/index.d.ts +2 -0
- package/types/mixins/repo-fs-store.d.ts +2 -0
- package/types/mixins/repo-watched-fs-store.d.ts +2 -0
- package/types/plugins/archive-plugin.d.ts +8 -0
- package/types/plugins/default-board-actions-plugin.d.ts +9 -0
- package/types/plugins/default-board-properties-plugin.d.ts +16 -0
- package/types/plugins/default-plugins.d.ts +6 -0
- package/types/plugins/epic-plugin.d.ts +19 -0
- package/types/plugins/extension-plugin.d.ts +15 -0
- package/types/plugins/index.d.ts +9 -0
- package/types/plugins/persist-tags-plugin.d.ts +4 -0
- package/types/plugins/plugin-manager.d.ts +68 -0
- package/types/plugins/plugin-registry.d.ts +1 -0
- package/types/project-factory.d.ts +14 -0
- package/types/project.d.ts +158 -0
- package/types/repository.d.ts +996 -0
- package/types/task.d.ts +202 -0
- package/types/tools.d.ts +29 -0
- package/types/usecases/actions.d.ts +7 -0
- package/types/usecases/get-imdone-url.d.ts +1 -0
- package/types/usecases/get-project-tags.d.ts +1 -0
- package/types/usecases/get-tasks-in-file.d.ts +4 -0
- package/types/usecases/index.d.ts +4 -0
- package/vitest.config.js +13 -0
package/lib/project.js
ADDED
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
import { Project } from 'imdone-api/lib/project.js';
|
|
2
|
+
import { PluginManager } from './plugins/plugin-manager.js';
|
|
3
|
+
import { Repository } from './repository.js';
|
|
4
|
+
import _cloneDeep from 'lodash.clonedeep';
|
|
5
|
+
import { newCard } from './card.js';
|
|
6
|
+
import { constants } from './constants.js';
|
|
7
|
+
const { JOURNAL_TYPE, CONFIG_DIR } = constants;
|
|
8
|
+
import _path from 'node:path';
|
|
9
|
+
import moment from 'moment';
|
|
10
|
+
import { getIsoDateWithOffset } from './adapters/date-time.js';
|
|
11
|
+
import * as fileGateway from './adapters/file-gateway.js';
|
|
12
|
+
import matter from 'gray-matter';
|
|
13
|
+
import _isObject from 'lodash.isobject';
|
|
14
|
+
import _isString from 'lodash.isstring';
|
|
15
|
+
import _isFunction from 'lodash.isfunction';
|
|
16
|
+
import { exec } from 'node:child_process';
|
|
17
|
+
import { sort } from 'fast-sort';
|
|
18
|
+
import { Task } from './task.js';
|
|
19
|
+
import eol from 'eol';
|
|
20
|
+
import { renderMarkdown, extractWikilinkTopics } from './adapters/markdown.js';
|
|
21
|
+
import { getFunctionSignature } from './adapters/parsers/function-parser.js';
|
|
22
|
+
import { format } from './adapters/parsers/content-transformer.js';
|
|
23
|
+
import { logger } from './adapters/logger.js';
|
|
24
|
+
|
|
25
|
+
function calculateTotals(lists = []) {
|
|
26
|
+
const totals = {}
|
|
27
|
+
lists.forEach((list) => {
|
|
28
|
+
try {
|
|
29
|
+
totals[list.name] = list.tasks.length
|
|
30
|
+
} catch (e) {
|
|
31
|
+
logger.error('Error calculating list totals:', e)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
return totals
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function onChange(project, event, data) {
|
|
38
|
+
project.emit(event, data)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const EVENTS = [
|
|
42
|
+
'file.processed',
|
|
43
|
+
'files.found',
|
|
44
|
+
'file.update',
|
|
45
|
+
'file.saved',
|
|
46
|
+
'list.modified',
|
|
47
|
+
'config.update',
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
export class WorkerProject extends Project {
|
|
51
|
+
constructor(repo) {
|
|
52
|
+
super()
|
|
53
|
+
this.repo = repo
|
|
54
|
+
this.innerFilter = ''
|
|
55
|
+
this.fileGateway = fileGateway
|
|
56
|
+
this._updatedAt = undefined
|
|
57
|
+
this.data = {}
|
|
58
|
+
this.dataKeys = []
|
|
59
|
+
this.repo.project = this
|
|
60
|
+
this.pluginManager = new PluginManager(this)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get configDir() {
|
|
64
|
+
return _path.join(this.path, CONFIG_DIR)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get allTopics() {
|
|
68
|
+
return [...this.repo.allTopics]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get allTags() {
|
|
72
|
+
return [...this.repo.allTags]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get allContexts() {
|
|
76
|
+
return [...this.repo.allContexts]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get allMeta() {
|
|
80
|
+
return this.repo.allMeta
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get lists() {
|
|
84
|
+
return this.getLists({
|
|
85
|
+
tasks: this.filteredCards,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get allLists() {
|
|
90
|
+
return this.getLists()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get filteredCards() {
|
|
94
|
+
return this.getCards(this.filter)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get isoDateWithOffset() {
|
|
98
|
+
return getIsoDateWithOffset()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get updatedAt() {
|
|
102
|
+
return this._updatedAt
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get files() {
|
|
106
|
+
return this.repo.getFilePaths()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get config() {
|
|
110
|
+
return this.repo.config
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get defaultFilter() {
|
|
114
|
+
return this.config && this.config.defaultFilter
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
set defaultFilter(filter) {
|
|
118
|
+
this.config.defaultFilter = filter
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get totals() {
|
|
122
|
+
return calculateTotals(this.lists)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get path() {
|
|
126
|
+
return this.repo.path
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get name() {
|
|
130
|
+
return this.repo.getDisplayName()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get doneList() {
|
|
134
|
+
return this.config.getDoneList()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get boardActions() {
|
|
138
|
+
return [
|
|
139
|
+
...this.pluginManager.getBoardActions(),
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get filter() {
|
|
144
|
+
return this.innerFilter
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
set filter(filter) {
|
|
148
|
+
this.innerFilter = filter
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async init() {
|
|
152
|
+
this.pluginManager.on('plugin-installed', () => this.emitUpdate())
|
|
153
|
+
this.pluginManager.on('plugin-uninstalled', () => this.emitUpdate())
|
|
154
|
+
this.pluginManager.on('plugins-reloaded', () => this.emitUpdate())
|
|
155
|
+
await this.pluginManager.loadPlugins()
|
|
156
|
+
this.data = await this.pluginManager.getBoardProperties()
|
|
157
|
+
this.dataKeys = this.getDataKeys(this.data)
|
|
158
|
+
|
|
159
|
+
logger.log('data', this.data)
|
|
160
|
+
logger.log('dataKeys', this.dataKeys)
|
|
161
|
+
EVENTS.forEach((event) => {
|
|
162
|
+
this.repo.on(event, (data) => onChange(this, event, data))
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
this.repo.on('task.found', (task) => this.pluginManager.onTaskFound(task))
|
|
166
|
+
|
|
167
|
+
const files = await this.repo.init()
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
await this.pluginManager.startDevMode()
|
|
171
|
+
} catch (err) {
|
|
172
|
+
logger.log('Error on starting dev mode', err)
|
|
173
|
+
throw new Error('Error on starting dev mode', { cause: err })
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return files
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
initIndexes(allLists) {
|
|
180
|
+
allLists.forEach((list) => {
|
|
181
|
+
list.tasks.filter(list => !list.filter).forEach((task, index) => {
|
|
182
|
+
task.index = index
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// HACK To handle circular dependency issue with file
|
|
188
|
+
// <!--
|
|
189
|
+
// order:0
|
|
190
|
+
// -->
|
|
191
|
+
toJSON() {
|
|
192
|
+
return { path: this.path }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
getListsForImdoneJSON() {
|
|
196
|
+
this.initIndexes(this.repo.getTasksByList())
|
|
197
|
+
let allLists = this.getLists({
|
|
198
|
+
populateFiltered: true,
|
|
199
|
+
})
|
|
200
|
+
const totals = calculateTotals(allLists)
|
|
201
|
+
allLists = allLists.map((list) => {
|
|
202
|
+
list = { ...list }
|
|
203
|
+
list.tasks = list.tasks.map((card, index) => {
|
|
204
|
+
card.interpretedContent = ''
|
|
205
|
+
card.totals = totals
|
|
206
|
+
return card
|
|
207
|
+
})
|
|
208
|
+
return list
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
return { allLists, totals }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getInitializedCards(allLists, totals) {
|
|
215
|
+
return allLists
|
|
216
|
+
.map((list) => list.tasks)
|
|
217
|
+
.flat()
|
|
218
|
+
.map(
|
|
219
|
+
(card) => card.init(totals)
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async toImdoneJSON() {
|
|
224
|
+
this.pluginManager.initDevMode()
|
|
225
|
+
logger.time('toJSON time')
|
|
226
|
+
this._updatedAt = new Date()
|
|
227
|
+
const { allLists , totals } = this.getListsForImdoneJSON()
|
|
228
|
+
|
|
229
|
+
logger.time('plugin onBeforeBoardUpdate time')
|
|
230
|
+
await this.pluginManager.onBeforeBoardUpdate()
|
|
231
|
+
logger.timeEnd('plugin onBeforeBoardUpdate time')
|
|
232
|
+
|
|
233
|
+
logger.time('getBoardProperties time')
|
|
234
|
+
const data = this.data = await this.pluginManager.getBoardProperties()
|
|
235
|
+
logger.timeEnd('getBoardProperties time')
|
|
236
|
+
|
|
237
|
+
logger.time('getDataKeys time')
|
|
238
|
+
const dataKeys = this.dataKays = this.getDataKeys(data)
|
|
239
|
+
logger.timeEnd('getDataKeys time')
|
|
240
|
+
|
|
241
|
+
logger.time('card init time')
|
|
242
|
+
const cards = this.getInitializedCards(allLists, totals)
|
|
243
|
+
logger.timeEnd('card init time')
|
|
244
|
+
|
|
245
|
+
logger.time('plugin onBoardUpdate time')
|
|
246
|
+
await this.pluginManager.onBoardUpdate(allLists)
|
|
247
|
+
logger.timeEnd('plugin onBoardUpdate time')
|
|
248
|
+
|
|
249
|
+
logger.time('getTags time')
|
|
250
|
+
const tags = this.getTags(cards)
|
|
251
|
+
logger.timeEnd('getTags time')
|
|
252
|
+
|
|
253
|
+
logger.time('getLists time')
|
|
254
|
+
const lists = this.filter
|
|
255
|
+
? this.getRequestedLists(cards)
|
|
256
|
+
: allLists
|
|
257
|
+
logger.timeEnd('getLists time')
|
|
258
|
+
|
|
259
|
+
logger.timeEnd('toJSON time')
|
|
260
|
+
return {
|
|
261
|
+
path: this.path,
|
|
262
|
+
config: this.config,
|
|
263
|
+
lists: lists,
|
|
264
|
+
files: this.files,
|
|
265
|
+
totals,
|
|
266
|
+
totalCards: this.repo.getTasks().length,
|
|
267
|
+
tags,
|
|
268
|
+
allMeta: this.allMeta,
|
|
269
|
+
allContexts: this.allContexts,
|
|
270
|
+
allTags: this.allTags,
|
|
271
|
+
filter: this.filter,
|
|
272
|
+
defaultFilter: this.defaultFilter,
|
|
273
|
+
actions: this.boardActions,
|
|
274
|
+
plugins: this.pluginManager.getPlugins(),
|
|
275
|
+
data,
|
|
276
|
+
dataKeys,
|
|
277
|
+
queryProps: this.getQueryProps(cards),
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
getRequestedLists(cards) {
|
|
282
|
+
return this.getLists({
|
|
283
|
+
tasks: this.getCards(this.filter, cards),
|
|
284
|
+
populateFiltered: true,
|
|
285
|
+
}).map(list => {
|
|
286
|
+
const configList = this.config.lists.find(l => l.name === list.name)
|
|
287
|
+
list.hidden = configList && configList.hidden
|
|
288
|
+
if (this.hideLists.includes(list.name)) {
|
|
289
|
+
list.hidden = true
|
|
290
|
+
}
|
|
291
|
+
return list
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
emit() {} // This is implemented in imdone UI worker
|
|
296
|
+
|
|
297
|
+
emitUpdate() {
|
|
298
|
+
this.emit('file.update')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async destroy() {
|
|
302
|
+
await this.repo.destroy()
|
|
303
|
+
if (this.pluginManager) this.pluginManager.destroyPlugins()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
getDataKeys(data) {
|
|
307
|
+
const keys = Object.keys(data).map((key) => {
|
|
308
|
+
const value = data[key]
|
|
309
|
+
if (_isFunction(value)) {
|
|
310
|
+
key = getFunctionSignature(value)
|
|
311
|
+
}
|
|
312
|
+
return key
|
|
313
|
+
})
|
|
314
|
+
keys.forEach((key) => {
|
|
315
|
+
const value = data[key]
|
|
316
|
+
if (_isObject(value) && !Array.isArray(value)) {
|
|
317
|
+
keys.push(...Object.keys(value).map((k) => `${key}.${k}`))
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
return keys
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
removeList(list) {
|
|
324
|
+
this.repo.removeList(list)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
getLists(opts) {
|
|
328
|
+
const {tasks = this.getDefaultFilteredCards(), populateFiltered = false} = opts || {}
|
|
329
|
+
return Repository.getTasksByList(this.repo, tasks, true, populateFiltered)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
getTaskQueryProps(props) {
|
|
333
|
+
const queryProps = []
|
|
334
|
+
if (props) {
|
|
335
|
+
// add all properties of the task to the queryProps array
|
|
336
|
+
Object.keys(props).forEach((key) => {
|
|
337
|
+
if (key === 'data') return
|
|
338
|
+
if (queryProps.includes(key)) return
|
|
339
|
+
|
|
340
|
+
queryProps.push(key)
|
|
341
|
+
|
|
342
|
+
if (_isObject(props[key])) {
|
|
343
|
+
Object.keys(props[key]).forEach((subKey) => {
|
|
344
|
+
if (!queryProps.includes(`${key}.${subKey}`))
|
|
345
|
+
queryProps.push(`${key}.${subKey}`)
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
return queryProps
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
getQueryProps(cards) {
|
|
354
|
+
if (!cards) return []
|
|
355
|
+
const queryProps = this.getTaskQueryProps(cards[0])
|
|
356
|
+
return [
|
|
357
|
+
...new Set(queryProps),
|
|
358
|
+
...[...this.repo.metaKeys].map(key => `allMeta.${key}`),
|
|
359
|
+
...[...this.repo.metaKeys].map(key => `meta.${key}`),
|
|
360
|
+
...this.allTopics,
|
|
361
|
+
...this.allTags,
|
|
362
|
+
...this.allContexts,
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
getTags(cards = this.getDefaultFilteredCards()) {
|
|
368
|
+
const tags = []
|
|
369
|
+
cards.forEach((card) => {
|
|
370
|
+
card.allTags.forEach((tag) => {
|
|
371
|
+
let foundTag = tags.find(({name}) => tag === name)
|
|
372
|
+
if (!foundTag) {
|
|
373
|
+
foundTag = {name: tag, count: 1}
|
|
374
|
+
tags.push(foundTag)
|
|
375
|
+
}
|
|
376
|
+
foundTag.count++
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
return sort(tags).by([
|
|
381
|
+
{desc: (tag) => tag.count},
|
|
382
|
+
{asc: (tag) => tag.name},
|
|
383
|
+
])
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
getDefaultFilteredCards() {
|
|
387
|
+
return this.filterCards(this.repo.getTasks(), this.defaultFilter)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
getAllCards(filter) {
|
|
391
|
+
const allTasks = this.repo.getTasks()
|
|
392
|
+
return this.getCards(filter, allTasks)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
getCards(filter, cards = this.getDefaultFilteredCards()) {
|
|
396
|
+
cards = filter ? this.filterCards(cards, filter) : cards
|
|
397
|
+
return cards
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
filterCards(cards, filter) {
|
|
401
|
+
this.hideLists = []
|
|
402
|
+
const {
|
|
403
|
+
result,
|
|
404
|
+
hideLists
|
|
405
|
+
} = Repository.filterCards(cards, filter)
|
|
406
|
+
this.hideLists = hideLists
|
|
407
|
+
return result
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async addMetadata(task, key, value) {
|
|
411
|
+
if (task.hasMetaData(key, value)) return
|
|
412
|
+
if (!task.allMeta[key]) task.allMeta[key] = []
|
|
413
|
+
task.allMeta[key].push(value)
|
|
414
|
+
if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
|
|
415
|
+
const metaData = `${key}${this.config.getMetaSep()}${value}`
|
|
416
|
+
const content = task.addToLastCommentInContent(
|
|
417
|
+
task.content,
|
|
418
|
+
metaData,
|
|
419
|
+
this.config.isMetaNewLine()
|
|
420
|
+
)
|
|
421
|
+
return await this.updateCardContent(task, content)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async removeMetadata(task, key, value) {
|
|
425
|
+
if (!task.meta[key]) return
|
|
426
|
+
const file = this.getFileForTask(task)
|
|
427
|
+
const content = file.removeMetaData(task.content, key, value)
|
|
428
|
+
return await this.updateCardContent(task,content)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async addTag(task, tag) {
|
|
432
|
+
if (task.tags.includes(tag)) return
|
|
433
|
+
task.allTags.push(tag)
|
|
434
|
+
const tagContent = `${this.config.getTagPrefix()}${tag}`
|
|
435
|
+
const content = task.addToLastCommentInContent(
|
|
436
|
+
task.content,
|
|
437
|
+
tagContent,
|
|
438
|
+
this.config.isMetaNewLine()
|
|
439
|
+
)
|
|
440
|
+
return await this.updateCardContent(task, content)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async removeTag(task, tag) {
|
|
444
|
+
if (!task.tags.includes(tag)) return
|
|
445
|
+
const tagContent = new RegExp(
|
|
446
|
+
`\\${this.config.getTagPrefix()}${tag}\\s`,
|
|
447
|
+
'g'
|
|
448
|
+
)
|
|
449
|
+
logger.log('removeTag regex:', tagContent)
|
|
450
|
+
const content = task.content.replace(tagContent, '')
|
|
451
|
+
return await this.updateCardContent(task, content)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async moveTask(task, newList, newPos) {
|
|
455
|
+
return this.repo.moveTask({task, newList, newPos})
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
getFile(filePath) {
|
|
459
|
+
return this.repo.getFile(filePath)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
getFileForTask(task) {
|
|
463
|
+
return this.repo.getFileForTask(task)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
rollBackFileForTask(task) {
|
|
467
|
+
return this.getFileForTask(task).rollback().extractTasks(this.config)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async updateCardContent(task, content) {
|
|
471
|
+
const file = await this.repo.modifyTaskFromContent(task, content)
|
|
472
|
+
this.emitUpdate()
|
|
473
|
+
return file
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async snackBar({ message, type, duration }) {
|
|
477
|
+
this.emit('project.snackBar', { message, type, duration })
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async toast({ message, type, duration }) {
|
|
481
|
+
this.emit('project.toast', { message, type, duration })
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
filterLists(filter, lists = this.lists) {
|
|
485
|
+
return lists.map((list) => {
|
|
486
|
+
let newList = { ...list, tasks: [] }
|
|
487
|
+
newList = _cloneDeep(newList)
|
|
488
|
+
newList.tasks = Repository.query(list.tasks, filter)
|
|
489
|
+
return newList
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async copyToClipboard(text, message) {this.files
|
|
494
|
+
this.emit('project.copyToClipboard', { text, message })
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
async openUrl(url) {
|
|
498
|
+
this.emit('project.openUrl', url)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// TODO For methods that emit, they should still do the thing without imdone UI
|
|
502
|
+
// <!--
|
|
503
|
+
// #urgent
|
|
504
|
+
// #important
|
|
505
|
+
// order:-195
|
|
506
|
+
// -->
|
|
507
|
+
async openPath(path) {
|
|
508
|
+
this.emit('project.openPath', path)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
saveFile(content, file) {
|
|
512
|
+
const filePath = this.getFullPath(file)
|
|
513
|
+
this.emit('project.saveFile', { file: filePath, content })
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/*
|
|
517
|
+
* @param {Object} opts - The options object
|
|
518
|
+
* @param {String} opts.list - The list name to add the card to
|
|
519
|
+
* @param {String} opts.path - The path to the file to add the card to
|
|
520
|
+
* @param {String} opts.template - The template to use for the new card
|
|
521
|
+
* @param {String} opts.title - The title of the new card
|
|
522
|
+
* @param {String} opts.comments - The comments to add to the new card
|
|
523
|
+
* @param {Boolean} opts.emit - Whether to emit the new card event
|
|
524
|
+
*/
|
|
525
|
+
async newCard({ list, path, template, title, comments, emit = true }) {
|
|
526
|
+
if (!path || !_path.parse(path).ext) path = this.getNewCardsFile({title})
|
|
527
|
+
path = this.getFullPath(path)
|
|
528
|
+
|
|
529
|
+
const { isFile, isDirectory } = await fileGateway.preparePathForWriting(path)
|
|
530
|
+
|
|
531
|
+
if (!template) template = this.getNewCardTemplate(path, isFile)
|
|
532
|
+
|
|
533
|
+
const boardData = await this.pluginManager.getBoardProperties();
|
|
534
|
+
template = format(template, boardData)
|
|
535
|
+
|
|
536
|
+
if (comments) template = Task.addToLastCommentInContent(template, comments, this.config.isMetaNewLine())
|
|
537
|
+
|
|
538
|
+
let relativePath = _path.relative(this.path, path)
|
|
539
|
+
if (isDirectory) relativePath += _path.sep
|
|
540
|
+
|
|
541
|
+
const data = {
|
|
542
|
+
list,
|
|
543
|
+
path,
|
|
544
|
+
relativePath,
|
|
545
|
+
template,
|
|
546
|
+
isDirectory,
|
|
547
|
+
}
|
|
548
|
+
if (emit) this.emit('project.newCard', data)
|
|
549
|
+
return data
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async addCardToFile(opts) {
|
|
553
|
+
return await this.addTaskToFile(opts)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async addTaskToFile({path, list = this.config?.getDefaultList(), content, tags = [], contexts = [], meta = [], useCardTemplate = false}) {
|
|
557
|
+
const pluginMods = await this.pluginManager.onBeforeAddTask({path, list, meta, tags, contexts, content, useCardTemplate})
|
|
558
|
+
path = pluginMods.path
|
|
559
|
+
content = pluginMods.content
|
|
560
|
+
meta = pluginMods.meta
|
|
561
|
+
tags = pluginMods.tags
|
|
562
|
+
contexts = pluginMods.contexts
|
|
563
|
+
const cardData = await this.newCard({list, path, title: eol.split(content)[0], emit: false})
|
|
564
|
+
const filePath = cardData.path
|
|
565
|
+
if (useCardTemplate) content += cardData.template
|
|
566
|
+
|
|
567
|
+
const boardData = await this.pluginManager.getBoardProperties();
|
|
568
|
+
const data = { ...boardData, ...cardData}
|
|
569
|
+
content = format(content, data)
|
|
570
|
+
|
|
571
|
+
content = this.addTagsToContent(tags, content)
|
|
572
|
+
content = this.addContextsToContent(contexts, content)
|
|
573
|
+
content = this.addMetaToContent(meta, content)
|
|
574
|
+
|
|
575
|
+
return await this.repo.addTaskToFile(filePath, list, content)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
addMetaToContent(meta, content) {
|
|
579
|
+
if (meta && meta.length > 0) {
|
|
580
|
+
let metaContent = ''
|
|
581
|
+
meta.forEach(({ key, value }) => {
|
|
582
|
+
if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
|
|
583
|
+
const metaData = `${key}${this.repo.config.getMetaSep()}${value}`
|
|
584
|
+
if (content.includes(metaData)) return
|
|
585
|
+
const existingMetadata = Task.parseMetaData(this.config, content)[key]
|
|
586
|
+
if (existingMetadata) {
|
|
587
|
+
content = Task.removeMetaData({
|
|
588
|
+
config: this.config,
|
|
589
|
+
content,
|
|
590
|
+
key,
|
|
591
|
+
value: existingMetadata[0],
|
|
592
|
+
})
|
|
593
|
+
}
|
|
594
|
+
const spaceOrNewLine = this.config.isMetaNewLine() ? '\n' : ' '
|
|
595
|
+
metaContent = `${metaContent}${spaceOrNewLine}${metaData}`
|
|
596
|
+
})
|
|
597
|
+
content = Task.addToLastCommentInContent(content, metaContent.trim(), this.config.isMetaNewLine())
|
|
598
|
+
}
|
|
599
|
+
return content
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
addContextsToContent(contexts, content) {
|
|
603
|
+
if (contexts && contexts.length > 0) {
|
|
604
|
+
let contextContent = ''
|
|
605
|
+
contexts.forEach(context => {
|
|
606
|
+
const contextWithPrefix = `@${context}`
|
|
607
|
+
if (content.includes(contextWithPrefix)) return
|
|
608
|
+
contextContent = `${contextContent} @${context}`
|
|
609
|
+
})
|
|
610
|
+
content = Task.addToLastCommentInContent(content, contextContent.trim(), this.config.isMetaNewLine())
|
|
611
|
+
}
|
|
612
|
+
return content
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
addTagsToContent(tags, content) {
|
|
616
|
+
if (tags && tags.length > 0) {
|
|
617
|
+
let tagContent = ''
|
|
618
|
+
tags.forEach(tag => {
|
|
619
|
+
const tagWithPrefix = `${this.config.getTagPrefix()}${tag}`
|
|
620
|
+
if (content.includes(tagWithPrefix)) return
|
|
621
|
+
tagContent = `${tagContent} ${tagWithPrefix}`
|
|
622
|
+
})
|
|
623
|
+
content = Task.addToLastCommentInContent(content, tagContent.trim(), this.config.isMetaNewLine())
|
|
624
|
+
}
|
|
625
|
+
return content
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async deleteTask(task) {
|
|
629
|
+
await this.repo.deleteTask(task)
|
|
630
|
+
await this.pluginManager.onAfterDeleteTask(task)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async deleteTasks(tasks) {
|
|
634
|
+
await this.repo.deleteTasks(tasks)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
setFilter(filter) {
|
|
638
|
+
this.emit('project.filter', { filter })
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
getNewCardTemplate(file, isFile) {
|
|
642
|
+
const frontMatter = this.getNewCardFileFrontMatter(file, isFile)
|
|
643
|
+
const card = newCard(
|
|
644
|
+
{
|
|
645
|
+
frontMatter,
|
|
646
|
+
repoId: this.path,
|
|
647
|
+
text: '',
|
|
648
|
+
source: { path: this.getNewCardsFile() },
|
|
649
|
+
},
|
|
650
|
+
this,
|
|
651
|
+
true
|
|
652
|
+
)
|
|
653
|
+
card.init(this.totals)
|
|
654
|
+
return card.formatContent(frontMatter.template).content
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
getNewCardFileFrontMatter(file, isFile) {
|
|
658
|
+
let fileContent = ''
|
|
659
|
+
if (file && isFile) {
|
|
660
|
+
fileContent = fileGateway.readFileSync(file, 'utf8')
|
|
661
|
+
} else if (JOURNAL_TYPE.NEW_FILE === this.config.journlType) {
|
|
662
|
+
fileContent = this.config.journalTemplate
|
|
663
|
+
}
|
|
664
|
+
let { props, computed, template } = this.config.settings.cards
|
|
665
|
+
const frontMatter = matter(fileContent).data || {}
|
|
666
|
+
if (!_isObject(props)) props = {}
|
|
667
|
+
if (!_isObject(computed)) computed = {}
|
|
668
|
+
if (!_isString(template)) template = ''
|
|
669
|
+
if (!_isObject(frontMatter.props)) frontMatter.props = {}
|
|
670
|
+
if (!_isObject(frontMatter.computed)) frontMatter.computed = {}
|
|
671
|
+
if (!_isString(frontMatter.template)) frontMatter.template = template
|
|
672
|
+
props = {
|
|
673
|
+
...props,
|
|
674
|
+
...frontMatter.props,
|
|
675
|
+
now: new Date().toDateString(),
|
|
676
|
+
totals: this.totals,
|
|
677
|
+
}
|
|
678
|
+
computed = { ...computed, ...frontMatter.computed }
|
|
679
|
+
return {
|
|
680
|
+
...frontMatter,
|
|
681
|
+
props,
|
|
682
|
+
computed,
|
|
683
|
+
template: frontMatter.template,
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
getNewCardsFile(opts = { relPath: false }) {
|
|
688
|
+
const { relPath, title } = opts
|
|
689
|
+
if (!this.config) return ''
|
|
690
|
+
const filePath = this.appendNewCardsTo(title)
|
|
691
|
+
if (!filePath) return ''
|
|
692
|
+
return filePath
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
appendNewCardsTo(title) {
|
|
696
|
+
const journalType = this.config.journalType
|
|
697
|
+
if (journalType === JOURNAL_TYPE.SINGLE_FILE)
|
|
698
|
+
return this.getFullPath(this.config.appendNewCardsTo)
|
|
699
|
+
if (journalType === JOURNAL_TYPE.FOLDER)
|
|
700
|
+
return this.getJournalFile().fullFilePath
|
|
701
|
+
if (journalType === JOURNAL_TYPE.NEW_FILE) {
|
|
702
|
+
if (!title) return this.getFullPath(this.config.journalPath)
|
|
703
|
+
const fileName = `${this.sanitizeFileName(title)}.md`
|
|
704
|
+
const fileFolder = this.getFullPath(this.config.journalPath)
|
|
705
|
+
const filePath = _path.join(fileFolder, fileName)
|
|
706
|
+
|
|
707
|
+
return filePath
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
sanitizeFileName(fileName) {
|
|
712
|
+
return fileGateway.sanitizeFileName(fileName, this.config.replaceSpacesWith)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
getJournalFile() {
|
|
716
|
+
const month = moment().format('YYYY-MM')
|
|
717
|
+
const today = moment().format('YYYY-MM-DD')
|
|
718
|
+
const journalPath = this.config.journalPath
|
|
719
|
+
const folderPath = _path.join(journalPath, month)
|
|
720
|
+
const journalFilePrefix = this.config.journalFilePrefix
|
|
721
|
+
const journalFileSuffix = this.config.journalFileSuffix
|
|
722
|
+
const filePath = _path.join(
|
|
723
|
+
folderPath,
|
|
724
|
+
`${journalFilePrefix}${today}${journalFileSuffix}.md`
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
const fullFilePath = this.getFullPath(filePath)
|
|
728
|
+
return { filePath, fullFilePath }
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
getFullPath(...path) {
|
|
732
|
+
if (_path.join(...path).startsWith(this.path)) {
|
|
733
|
+
return _path.join(...path)
|
|
734
|
+
}
|
|
735
|
+
return _path.join.apply({}, [this.path, ...path])
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
performCardAction(action, task) {
|
|
739
|
+
task = this.repo.getTask(task.id)
|
|
740
|
+
try {
|
|
741
|
+
action = JSON.parse(action)
|
|
742
|
+
} catch (e) {
|
|
743
|
+
//
|
|
744
|
+
}
|
|
745
|
+
if (action.plugin) return this.pluginManager.performCardAction(action, task)
|
|
746
|
+
|
|
747
|
+
const actionFunction = task.getCardActions()[action.index].action
|
|
748
|
+
// TODO These card actions should be removed
|
|
749
|
+
// #imdone-1.54.0
|
|
750
|
+
// <!--
|
|
751
|
+
// order:-175
|
|
752
|
+
// -->
|
|
753
|
+
const actions = {
|
|
754
|
+
filter: (filter) => {
|
|
755
|
+
this.setFilter(filter)
|
|
756
|
+
},
|
|
757
|
+
newCard: async (list, path) => {
|
|
758
|
+
if (task.source.lang !== 'text')
|
|
759
|
+
return this.alert('Unable to append cards in code files.')
|
|
760
|
+
if (path) path = this.getFullPath(path)
|
|
761
|
+
await this.newCard({ list, path })
|
|
762
|
+
},
|
|
763
|
+
alert: (msg) => {
|
|
764
|
+
this.toast({ message: msg })
|
|
765
|
+
},
|
|
766
|
+
openUrl: (url) => {
|
|
767
|
+
this.openUrl(url)
|
|
768
|
+
},
|
|
769
|
+
execCommand: (cmd) => {
|
|
770
|
+
return this.exec(cmd)
|
|
771
|
+
},
|
|
772
|
+
copy: (content, msg) => {
|
|
773
|
+
this.copyToClipboard(content, msg)
|
|
774
|
+
},
|
|
775
|
+
}
|
|
776
|
+
const actionThis = {
|
|
777
|
+
...task.data,
|
|
778
|
+
...task.desc,
|
|
779
|
+
actions,
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
try {
|
|
783
|
+
logger.log('actionFunction:', actionFunction)
|
|
784
|
+
const func = new Function(`return ${actionFunction}`)()
|
|
785
|
+
func.apply(actionThis)
|
|
786
|
+
} catch (e) {
|
|
787
|
+
logger.error(e)
|
|
788
|
+
logger.log('action:', actionFunction)
|
|
789
|
+
logger.log('this:', actionThis)
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
async performBoardAction(action, task) {
|
|
793
|
+
if (task) task = this.repo.getTask(task.id)
|
|
794
|
+
if (action && action.plugin)
|
|
795
|
+
return await this.pluginManager.performBoardAction(action, task)
|
|
796
|
+
const actions = {
|
|
797
|
+
filter: (filter) => {
|
|
798
|
+
this.setFilter(filter)
|
|
799
|
+
},
|
|
800
|
+
alert: (msg) => {
|
|
801
|
+
this.toast({ message: msg })
|
|
802
|
+
},
|
|
803
|
+
saveFile: ({ file, content }) => {
|
|
804
|
+
this.saveFile(content, file)
|
|
805
|
+
},
|
|
806
|
+
mailto: ({ subject, body, to, cc, bcc }) => {
|
|
807
|
+
const params = []
|
|
808
|
+
if (subject) params.push(`subject=${encodeURIComponent(subject)}`)
|
|
809
|
+
if (body) params.push(`body=${encodeURIComponent(body)}`)
|
|
810
|
+
if (cc) params.push(`cc=${encodeURIComponent(cc)}`)
|
|
811
|
+
if (bcc) params.push(`bcc=${encodeURIComponent(bcc)}`)
|
|
812
|
+
const url = `mailto:${to}?${params.join('&')}`
|
|
813
|
+
logger.log('opening email with:', url)
|
|
814
|
+
this.openUrl(url)
|
|
815
|
+
},
|
|
816
|
+
copy: (content, message) => {
|
|
817
|
+
this.copyToClipboard(content, message || 'Your content has been copied')
|
|
818
|
+
},
|
|
819
|
+
updateCard: (task, content) => {
|
|
820
|
+
this.updateCardContent(task, content)
|
|
821
|
+
},
|
|
822
|
+
}
|
|
823
|
+
const actionFunction = this.boardActions[action.index].action
|
|
824
|
+
const actionThis = { cards: this.lists, ...actions }
|
|
825
|
+
try {
|
|
826
|
+
actionFunction.apply(actionThis)
|
|
827
|
+
} catch (err) {
|
|
828
|
+
logger.error(err)
|
|
829
|
+
logger.log('action:', actionFunction)
|
|
830
|
+
logger.log('this:', actionThis)
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
exec(cmd) {
|
|
835
|
+
return new Promise((resolve, reject) => {
|
|
836
|
+
exec(cmd, (error, stdout, stderr) => {
|
|
837
|
+
if (stderr) logger.warn('cmd stderr:', stderr)
|
|
838
|
+
if (error) return reject(error)
|
|
839
|
+
resolve(stdout)
|
|
840
|
+
})
|
|
841
|
+
})
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
installPlugin({ name, version }) {
|
|
845
|
+
return this.pluginManager.installPlugin({ name, version })
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
uninstallPlugin(name) {
|
|
849
|
+
return this.pluginManager.uninstallPlugin(name)
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async refresh() {
|
|
853
|
+
await this.repo.refresh()
|
|
854
|
+
await this.pluginManager.reloadPlugins()
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
renderMarkdown(content, filePath) {
|
|
858
|
+
return renderMarkdown(content, filePath || this.path)
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
extractWikilinkTopics(markdown) {
|
|
862
|
+
return extractWikilinkTopics(markdown)
|
|
863
|
+
}
|
|
864
|
+
}
|