Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ransome
sleek
Commits
c6bdca60
Commit
c6bdca60
authored
May 30, 2021
by
ransome1
Browse files
Added cluster based rendering of todos to improve performance
parent
7b7cb513
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
.github/workflows/github-ci.yml
View file @
c6bdca60
...
...
@@ -95,27 +95,10 @@ jobs:
uses
:
github/codeql-action/autobuild@v1
-
name
:
Perform CodeQL Analysis
uses
:
github/codeql-action/analyze@v1
# mirror:
# needs: codeql
# name: Mirror code to opencode.net
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v1
# - name: Mirror + trigger CI
# uses: SvanBoxel/gitlab-mirror-and-ci-action@master
# with:
# args: "https://www.opencode.net/ransome/sleek.git"
# env:
# GITLAB_HOSTNAME: "opencode.net"
# GITLAB_USERNAME: "ransome"
# GITLAB_PASSWORD: ${{ secrets.gitlab_password }}
# GITLAB_PROJECT_ID: "https://www.opencode.net/ransome/sleek/edit"
# GITHUB_TOKEN: ${{ secrets.github_token }}
mirror
:
name
:
Mirror code to opencode.net
runs-on
:
ubuntu-latest
#
needs: codeql
needs
:
codeql
steps
:
# <-- must use actions/checkout@v1 before mirroring!
-
uses
:
actions/checkout@v1
-
uses
:
pixta-dev/repository-mirroring-action@v1
...
...
@@ -124,21 +107,3 @@ jobs:
git@www.opencode.net:ransome/sleek.git
ssh_private_key
:
# <-- use 'secrets' to pass credential information.
${{ secrets.GITLAB_SSH_PRIVATE_KEY }}
# publish-aur-package:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
#
# - name: Publish AUR package
# uses: KSXGitHub/github-actions-deploy-aur@v2
# with:
# pkgname: sleek
# pkgbuild: ./PKGBUILD
# commit_username: ${{ secrets.AUR_USERNAME }}
# commit_email: ${{ secrets.AUR_EMAIL }}
# ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
# commit_message: Update to latest sleek release
package.json
View file @
c6bdca60
{
"name"
:
"sleek"
,
"productName"
:
"sleek"
,
"version"
:
"1.0.5-
3
"
,
"version"
:
"1.0.5-
4
"
,
"description"
:
"Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source"
,
"synopsis"
:
"Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source"
,
"category"
:
"ProjectManagement"
,
...
...
@@ -53,7 +53,10 @@
"mac"
:
{
"target"
:
{
"target"
:
"default"
,
"arch"
:
[
"arm64"
,
"x64"
]
"arch"
:
[
"arm64"
,
"x64"
]
},
"icon"
:
"assets/icons/sleek.icns"
,
"category"
:
"public.app-category.productivity"
...
...
src/configs/i18next.config.js
View file @
c6bdca60
...
...
@@ -11,7 +11,7 @@ const i18nextOptions = {
},
namespace
:
"
translation
"
,
defaultNS
:
"
translation
"
,
supportedLngs
:
[
"
de
"
,
"
en
"
,
"
it
"
,
"
es
"
,
"
fr
"
],
supportedLngs
:
[
"
de
"
,
"
en
"
,
"
it
"
,
"
es
"
,
"
fr
"
,
"
cn
"
],
debug
:
false
,
preload
:
fs
.
readdirSync
(
path
.
join
(
__dirname
,
"
../locales
"
)).
filter
((
fileName
)
=>
{
const
joinedPath
=
path
.
join
(
path
.
join
(
__dirname
,
"
../locales
"
),
fileName
)
...
...
src/css/style.css
View file @
c6bdca60
...
...
@@ -15,8 +15,7 @@ html {
}
body
{
font-family
:
"FreeSans"
;
font-size
:
16px
;
font
:
16px
"FreeSans"
,
sans-serif
;
height
:
100%
;
margin
:
auto
;
-webkit-user-select
:
none
;
...
...
@@ -157,7 +156,7 @@ svg {
code
,
pre
{
font-family
:
SFMono-Regular
,
Consolas
,
Liberation
Mono
,
Menlo
,
monospace
!important
;
background-color
:
transparent
!important
;
background-color
:
#ebebeb
;
color
:
inherit
!important
;
}
...
...
@@ -1058,7 +1057,7 @@ nav ul:nth-child(2) {
padding-right
:
3em
;
}
.modal.content
.modal-card-body
table
.settings
tr
td
:last-child
{
min-width
:
1
0
em
;
min-width
:
1
2
em
;
text-align
:
center
;
}
.modal.content
.modal-card-body
table
.shortcuts
td
.tag
{
...
...
src/css/style.css.map
View file @
c6bdca60
This diff is collapsed.
Click to expand it.
src/index.html
View file @
c6bdca60
...
...
@@ -155,7 +155,7 @@
</div>
</nav>
</div>
<div
class=
"column content"
>
<div
id=
"todoTableWrapper"
class=
"column content"
>
<div
id=
"todoTableSearchContainer"
class=
"control has-icons-left"
>
<div
class=
"column"
>
<input
id=
"todoTableSearch"
class=
"input is-medium"
type=
"search"
placeholder=
"Search"
tabindex=
"10"
>
...
...
src/js/todos.mjs
View file @
c6bdca60
"
use strict
"
;
import
{
userData
,
appData
,
handleError
,
translations
,
setUserData
,
_paq
}
from
"
../render.js
"
;
import
{
userData
,
appData
,
handleError
,
translations
,
setUserData
,
_paq
,
startBuilding
}
from
"
../render.js
"
;
import
{
RecExtension
}
from
"
./todotxtExtensions.mjs
"
;
import
{
categories
}
from
"
./filters.mjs
"
;
import
{
generateRecurrence
}
from
"
./recurrences.mjs
"
;
import
{
convertDate
,
isToday
,
isTomorrow
,
isPast
}
from
"
./date.mjs
"
;
import
{
show
}
from
"
./form.mjs
"
;
const
modalForm
=
document
.
getElementById
(
"
modalForm
"
);
const
todoTableWrapper
=
document
.
getElementById
(
"
todoTableWrapper
"
);
const
todoTableContainer
=
document
.
getElementById
(
"
todoTableContainer
"
);
// ########################################################################################################################
// CONFIGURE MARKDOWN PARSER
...
...
@@ -44,8 +45,67 @@ const todoTableBodyCellSpacerTemplate = document.createElement("div");
const
todoTableBodyCellDueDateTemplate
=
document
.
createElement
(
"
span
"
);
const
todoTableBodyCellRecurrenceTemplate
=
document
.
createElement
(
"
span
"
);
const
item
=
{
previous
:
""
}
let
items
;
let
visibleRows
;
let
items
,
clusterCounter
,
clusterSize
=
Math
.
ceil
(
window
.
innerHeight
/
35
),
// 35 being the pixel height of one todo in compact mode
clusterThreshold
=
0
,
stopBuilding
=
false
,
visibleRows
=
0
;
todoTableWrapper
.
addEventListener
(
"
scroll
"
,
function
(
event
)
{
if
((
event
.
target
.
scrollHeight
-
event
.
target
.
scrollTop
===
event
.
target
.
clientHeight
)
&&
visibleRows
<
items
.
filtered
.
length
)
{
stopBuilding
=
false
;
startBuilding
(
null
,
true
);
}
});
function
configureTodoTableTemplate
(
append
)
{
try
{
if
(
!
append
)
{
todoTableContainer
.
innerHTML
=
""
;
visibleRows
=
0
;
clusterThreshold
=
0
;
stopBuilding
=
false
;
}
todoTableBodyCellMoreTemplate
.
setAttribute
(
"
class
"
,
"
flex-row todoTableItemMore
"
);
todoTableBodyCellMoreTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellMoreTemplate
.
innerHTML
=
`
<div class="dropdown is-right">
<div class="dropdown-trigger">
<a href="#"><i class="fas fa-ellipsis-v"></i></a>
</div>
<div class="dropdown-menu" role="menu">
<div class="dropdown-content">
<a class="dropdown-item">`
+
translations
.
useAsTemplate
+
`</a>
<a href="#" class="dropdown-item">`
+
translations
.
edit
+
`</a>
<a class="dropdown-item">`
+
translations
.
delete
+
`</a>
</div>
</div>
</div>
`
;
todoTableBodyRowTemplate
.
setAttribute
(
"
role
"
,
"
rowgroup
"
);
todoTableBodyRowTemplate
.
setAttribute
(
"
class
"
,
"
flex-table
"
);
todoTableBodyCellCheckboxTemplate
.
setAttribute
(
"
class
"
,
"
flex-row checkbox
"
);
todoTableBodyCellCheckboxTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
class
"
,
"
flex-row text
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
tabindex
"
,
0
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
href
"
,
"
#
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
title
"
,
translations
.
editTodo
);
tableContainerCategoriesTemplate
.
setAttribute
(
"
class
"
,
"
categories
"
);
todoTableBodyCellPriorityTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellSpacerTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellDueDateTemplate
.
setAttribute
(
"
class
"
,
"
flex-row itemDueDate
"
);
todoTableBodyCellDueDateTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellRecurrenceTemplate
.
setAttribute
(
"
class
"
,
"
flex-row recurrence
"
);
todoTableBodyCellRecurrenceTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
return
Promise
.
resolve
(
"
Success: Table templates set up
"
);
}
catch
(
error
)
{
error
.
functionName
=
configureTodoTableTemplate
.
name
;
return
Promise
.
reject
(
error
);
}
}
function
generateItems
(
content
)
{
try
{
items
=
{
objects
:
TodoTxt
.
parse
(
content
,
[
new
DueExtension
(),
new
RecExtension
(),
new
HiddenExtension
()
])
}
...
...
@@ -99,42 +159,50 @@ function generateGroups(items) {
return
0
;
});
}
// sort the items within the groups
items
.
forEach
((
group
)
=>
{
group
[
1
]
=
sortTodoData
(
group
[
1
]);
});
return
Promise
.
resolve
(
items
)
}
function
generateTable
(
groups
)
{
function
generateTable
(
groups
,
append
)
{
// prepare the templates for the table
return
configureTodoTableTemplate
().
then
(
function
(
response
)
{
return
configureTodoTableTemplate
(
append
).
then
(
function
(
response
)
{
clusterCounter
=
0
;
console
.
info
(
response
);
visibleRows
=
0
;
for
(
let
group
in
groups
)
{
if
(
stopBuilding
)
{
stopBuilding
=
false
;
break
;
}
// create a divider row
let
dividerRow
;
// completed todos
if
(
userData
.
sortCompletedLast
&&
groups
[
group
][
0
]
===
"
completed
"
)
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
></div></div>
"
)
)
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table group
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
></div></div>
"
)
// for priority, context and project
}
else
if
(
groups
[
group
][
0
]
!=
"
null
"
&&
userData
.
sortBy
!=
"
dueString
"
)
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table
"
+
userData
.
sortBy
+
"
group
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
><span class=
\"
button
"
+
groups
[
group
][
0
]
+
"
\"
>
"
+
groups
[
group
][
0
].
replace
(
/,/g
,
'
,
'
)
+
"
</span></div></div>
"
)
)
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table
"
+
userData
.
sortBy
+
"
group
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
><span class=
\"
button
"
+
groups
[
group
][
0
]
+
"
\"
>
"
+
groups
[
group
][
0
].
replace
(
/,/g
,
'
,
'
)
+
"
</span></div></div>
"
)
// if sorting is by due date
}
else
if
(
userData
.
sortBy
===
"
dueString
"
&&
groups
[
group
][
1
][
0
].
due
)
{
if
(
isToday
(
groups
[
group
][
1
][
0
].
due
))
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isToday
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
translations
.
today
+
"
</span></div></div>
"
)
);
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isToday
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
translations
.
today
+
"
</span></div></div>
"
)
}
else
if
(
isTomorrow
(
groups
[
group
][
1
][
0
].
due
))
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isTomorrow
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
translations
.
tomorrow
+
"
</span></div></div>
"
)
);
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isTomorrow
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
translations
.
tomorrow
+
"
</span></div></div>
"
)
}
else
if
(
isPast
(
groups
[
group
][
1
][
0
].
due
))
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isPast
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
groups
[
group
][
0
]
+
"
</span></div></div>
"
)
);
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row isPast
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
groups
[
group
][
0
]
+
"
</span></div></div>
"
)
}
else
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
groups
[
group
][
0
]
+
"
</span></div></div>
"
)
)
dividerRow
=
document
.
createRange
().
createContextualFragment
(
"
<div
id=
\"
"
+
userData
.
sortBy
+
groups
[
group
][
0
]
+
"
\"
class=
\"
flex-table group due
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
><span class=
\"
button
\"
>
"
+
groups
[
group
][
0
]
+
"
</span></div></div>
"
)
}
// create an empty divider row
}
else
{
tableContainerContent
.
appendChild
(
document
.
createRange
().
createContextualFragment
(
"
<div class=
\"
flex-table group
\"
role=
\"
rowgroup
\"
><div class=
\"
flex-row
\"
role=
\"
cell
\"
></div></div>
"
))
}
// sort items within this group
const
sortedGroup
=
sortTodoData
(
groups
[
group
][
1
]);
//const sortedGroup = groups[group][1];
// build the fragments per group
for
(
let
item
in
sortedGroup
)
{
let
todo
=
sortedGroup
[
item
];
}
/*else {
dividerRow = document.createRange().createContextualFragment("<div class=\"flex-table group\" role=\"rowgroup\"><div class=\"flex-row\" role=\"cell\"></div></div>")
}*/
// add divider row only if it doesn't exist yet
if
(
!
document
.
getElementById
(
userData
.
sortBy
+
groups
[
group
][
0
])
&&
dividerRow
)
tableContainerContent
.
appendChild
(
dividerRow
);
for
(
let
item
in
groups
[
group
][
1
])
{
let
todo
=
groups
[
group
][
1
][
item
];
// if this todo is not a recurring one the rec value will be set to null
if
(
!
todo
.
rec
)
{
todo
.
rec
=
null
;
...
...
@@ -163,6 +231,14 @@ function generateTable(groups) {
});
}
}
if
(
clusterCounter
<
clusterThreshold
)
{
clusterCounter
++
;
continue
;
}
else
if
((
visibleRows
===
clusterSize
+
clusterThreshold
)
||
visibleRows
===
items
.
filtered
.
length
)
{
clusterThreshold
=
visibleRows
;
stopBuilding
=
true
;
break
;
}
tableContainerContent
.
appendChild
(
generateTableRow
(
todo
));
}
// TODO add a catch
...
...
@@ -177,6 +253,7 @@ function generateTable(groups) {
}
function
generateTableRow
(
todo
)
{
try
{
clusterCounter
++
;
visibleRows
++
;
// create nodes from templates
let
todoTableBodyRow
=
todoTableBodyRowTemplate
.
cloneNode
(
true
);
...
...
@@ -494,49 +571,6 @@ function checkIsTodoVisible(todo) {
}
return
true
;
}
function
configureTodoTableTemplate
()
{
try
{
todoTableContainer
.
innerHTML
=
""
;
todoTableBodyCellMoreTemplate
.
setAttribute
(
"
class
"
,
"
flex-row todoTableItemMore
"
);
todoTableBodyCellMoreTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellMoreTemplate
.
innerHTML
=
`
<div class="dropdown is-right">
<div class="dropdown-trigger">
<a href="#"><i class="fas fa-ellipsis-v"></i></a>
</div>
<div class="dropdown-menu" role="menu">
<div class="dropdown-content">
<a class="dropdown-item">`
+
translations
.
useAsTemplate
+
`</a>
<a href="#" class="dropdown-item">`
+
translations
.
edit
+
`</a>
<a class="dropdown-item">`
+
translations
.
delete
+
`</a>
</div>
</div>
</div>
`
;
todoTableBodyRowTemplate
.
setAttribute
(
"
role
"
,
"
rowgroup
"
);
todoTableBodyRowTemplate
.
setAttribute
(
"
class
"
,
"
flex-table
"
);
todoTableBodyCellCheckboxTemplate
.
setAttribute
(
"
class
"
,
"
flex-row checkbox
"
);
todoTableBodyCellCheckboxTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
class
"
,
"
flex-row text
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
tabindex
"
,
0
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
href
"
,
"
#
"
);
todoTableBodyCellTextTemplate
.
setAttribute
(
"
title
"
,
translations
.
editTodo
);
tableContainerCategoriesTemplate
.
setAttribute
(
"
class
"
,
"
categories
"
);
todoTableBodyCellPriorityTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellSpacerTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellDueDateTemplate
.
setAttribute
(
"
class
"
,
"
flex-row itemDueDate
"
);
todoTableBodyCellDueDateTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
todoTableBodyCellRecurrenceTemplate
.
setAttribute
(
"
class
"
,
"
flex-row recurrence
"
);
todoTableBodyCellRecurrenceTemplate
.
setAttribute
(
"
role
"
,
"
cell
"
);
return
Promise
.
resolve
(
"
Success: Table templates set up
"
);
}
catch
(
error
)
{
error
.
functionName
=
configureTodoTableTemplate
.
name
;
return
Promise
.
reject
(
error
);
}
}
function
generateNotification
(
todo
,
offset
)
{
try
{
let
notifications
=
userData
.
notifications
;
...
...
src/locales/cn/translation.json
0 → 100644
View file @
c6bdca60
{
"addTodo"
:
"新增任务"
,
"toggleFilter"
:
"显示过滤器"
,
"openFile"
:
"打开todo.txt"
,
"toggleDarkMode"
:
"浅色/深色模式"
,
"viewHeadlineTodoList"
:
"任务列表"
,
"viewHeadlineAppView"
:
"App视图"
,
"toggleCompletedTodos"
:
"显示/隐藏已完成任务"
,
"sortBy"
:
"排序"
,
"completedTodos"
:
"已完成任务"
,
"sortCompletedLast"
:
"已完成任务显示在最后"
,
"hiddenTodos"
:
"隐藏任务"
,
"compactView"
:
"紧凑视图"
,
"priority"
:
"优先度"
,
"dueDate"
:
"任务期限"
,
"resetFilters"
:
"重置过滤和搜索"
,
"contexts"
:
"情境"
,
"projects"
:
"项目"
,
"visibleTodos"
:
"可见任务"
,
"of"
:
"的"
,
"selectedFilters"
:
"选中的过滤器"
,
"inProgress"
:
"进行中"
,
"done"
:
"标记为完成"
,
"editTodo"
:
"编辑任务"
,
"edit"
:
"编辑"
,
"copy"
:
"复制"
,
"cut"
:
"剪切"
,
"paste"
:
"粘贴"
,
"delete"
:
"删除"
,
"useAsTemplate"
:
"保存为模板"
,
"due"
:
"到期"
,
"today"
:
"今天"
,
"tomorrow"
:
"明天"
,
"dueToday"
:
"今天到期"
,
"dueTomorrow"
:
"明天到期"
,
"dueFuture"
:
"之后到期"
,
"duePast"
:
"过期"
,
"formErrorWritingFile"
:
"<strong>错误:</strong> 无法写入文件. 请确保todo.txt文件存在且有足够的写入权限"
,
"formInfoNoInput"
:
"请在文本里添加todo文件, 如果你不清楚如何使用, 请参见<a href=
\"
https://github.com/todotxt/todo.txt
\"
target=
\"
_blank
\"
>todo.txt syntax</a>."
,
"formInfoDuplicate"
:
"该任务已经存在, todo.txt文件里不能写入重复任务. "
,
"formInfoIncomplete"
:
"你的输入不完整, 请输入任务文本. "
,
"formSelectDueDate"
:
"无到期"
,
"cancel"
:
"取消"
,
"save"
:
"保存"
,
"formTodoInputPlaceholder"
:
"(A) 使用 todo.txt 格式 @context +project"
,
"addTodoContainerHeadline"
:
"没有任务"
,
"addTodoContainerSubtitle"
:
"列表为空, 来创建一些任务吧"
,
"welcomeToSleek"
:
"欢迎使用sleek"
,
"onboardingContainerSubtitle"
:
"首先选择 <strong>现有</strong> todo.txt 文件, 或者创建<strong>新文件</strong>. "
,
"createFile"
:
"创建todo.txt"
,
"onboardingContainerBtnOpen"
:
"选择现有todo.txt"
,
"windowTitleCreateFile"
:
"创建todo.txt"
,
"windowButtonCreateFile"
:
"在此处创建todo.txt"
,
"selectFile"
:
"选择todo.txt文件"
,
"select"
:
"选择"
,
"selected"
:
"选中的"
,
"windowButtonOpenFile"
:
"打开"
,
"windowFileformat"
:
"文本文件"
,
"sleekOnGithub"
:
"sleek on Github"
,
"about"
:
"关于"
,
"help"
:
"帮助"
,
"view"
:
"视图"
,
"todos"
:
"任务"
,
"file"
:
"文件"
,
"close"
:
"关闭"
,
"reload"
:
"重载"
,
"settings"
:
"设置"
,
"devTools"
:
"打开/关闭开发者工具"
,
"search"
:
"(A) 使用 todo.txt 格式搜索 @context +project due:"
,
"noResults"
:
"无结果"
,
"noResultContainerSubtitle"
:
"你的搜索或者过滤器无法找到结果"
,
"clear"
:
"清除"
,
"find"
:
"搜索"
,
"every"
:
"每"
,
"day"
:
"天"
,
"day_plural"
:
"天"
,
"daily"
:
"每天"
,
"week"
:
"周"
,
"week_plural"
:
"周"
,
"weekly"
:
"每周"
,
"month"
:
"月"
,
"month_plural"
:
"月"
,
"monthly"
:
"每月"
,
"year"
:
"年"
,
"year_plural"
:
"年"
,
"yearly"
:
"每年"
,
"noRecurrence"
:
"不重复"
,
"errorEventLogging"
:
"错误及事件记录(Log)"
,
"messageLoggingBody"
:
"如果打开匿名的错误/事件报告会有助于未来的开发. 报告里将包括发生的错误和使用的功能. 你可以在设定里打开. "
,
"messageShareTitle"
:
"sleek <i class=
\"
fas fa-heart
\"
></i> 你"
,
"messageShareBody"
:
"这个项目欢迎各位的意见<br><i class=
\"
fas fa-star
\"
></i> <a href=
\"
https://sourceforge.net/projects/sleek/reviews
\"
target=
\"
_blank
\"
>SourceForge</a>, <i class=
\"
fab fa-github
\"
></i> <a href=
\"
https://github.com/ransome1/sleek/issues
\"
target=
\"
_blank
\"
>Github 报告错误</a> 以及推荐"
,
"language"
:
"语言"
,
"settingsTabSettingsLanguageBody"
:
"sleek会按照你的计算机设定自动切换语言, 也可以手动更换语言 <strong>sleek会自动重启. 如果没有自动重启请手动打开sleek. </strong>"
,
"notifications"
:
"提醒"
,
"settingsTabSettingsNotificationsBody"
:
"sleek可以提醒你今天和明天到期的任务. 请保持sleek运行. "
,
"darkmode"
:
"暗色模式"
,
"settingsTabSettingsDarkmodeBody"
:
"如果sleek界面太明亮不适合你的口味/环境, 不妨切换成暗色模式. "
,
"settingsTabSettingsLoggingBody"
:
"如果允许记录匿名错误和事件, 会对开发有帮助. 更多信息请参见' <a href=
\"
https://github.com/ransome1/sleek/blob/master/PRIVACY.md
\"
target=
\"
_blank
\"
>privacy policy</a>."
,
"settingsTabAboutContribute"
:
"sleek是开源软件, 你可以一起帮忙改进它"
,
"settingsTabAboutCopyrightLicense"
:
"版权以及使用许可"
,
"settingsTabAboutCopyrightLicenseBody"
:
"版权 (c) 2021 Robin Ahle. sleek 遵循 <a href=
\"
https://opensource.org/licenses/MIT
\"
target=
\"
_blank
\"
>MIT license</a>. 请参照完整授权文本 <a href=
\"
https://github.com/ransome1/sleek/blob/master/LICENSE
\"
target=
\"
_blank
\"
>LICENSE</a>"
,
"settingsTabAboutPrivacy"
:
"隐私政策"
,
"settingsTabAboutPrivacyBody"
:
"(作者) 无意知道用户身份, 也不会收集不必要的数据. (作者) 希望知道有多少用户在使用sleek, 如果用户允许, 希望知道用户怎样使用sleek. 收集的数据将匿名使用SSL加密链接发送到Matomo实例. 详情请参见<a href=
\"
https://github.com/ransome1/sleek/blob/master/PRIVACY.md
\"
target=
\"
_blank
\"
>Privacy Policy</a>"
,
"settingsTabAboutExternalLibraries"
:
"本软件中使用到的外部软件"
,
"settingsTabSettingsArchive"
:
"存档任务"
,
"settingsTabSettingsArchiveBody"
:
"已完成任务会从当前todo.txt文件移除, 保存到done.txt. 如果还未创建done.txt则会自动创建. "
,
"settingsTabSettingsTray"
:
"最小化到系统托盘"
,
"settingsTabSettingsTrayBody"
:
"选中这个设定会让sleek最小化时隐藏到托盘(而非在任务栏<strong>sleek会自动重启. 如果没有自动重启请手动打开sleek. </strong>"
,
"archive"
:
"存档"
,
"shortcuts"
:
"快捷键"
,
"function"
:
"功能"
,
"priorities"
:
"优先度"
,
"helpTab3Title"
:
"情境和项目"
,
"helpTab4Title"
:
"日期和重复"
,
"helpTabPrioritiesTitle"
:
"添加优先度"
,
"helpTabPrioritiesBody"
:
"重要任务要加重显示在列表上. 你可以在任务前加
\"
(A)
\"
. 你可以选择 (A) 到 (Z), 但是只有A到C有对应颜色. 其他优先度显示为灰色. 可以在新建/编辑窗口里使用Ctrl+Alt+A〜Z设置优先度"
,
"helpTabContextsProjectsTitle"
:
"添加情境和项目"
,
"helpTabContextsProjectsBody"
:
"如果你的项目里有多个任务, 你可以在任务里添加
\"
+
\"
加上项目名. 情境表示该任务和你相关的情形. 根据David Allen著作<a href=
\"
https://en.wikipedia.org/wiki/Getting_Things_Done
\"
target=
\"
_blank
\"
>Getting Things Done</a>,
\"
context
\"
情境可以是家庭, 职场, 外出购物, 打电话, 电脑, 或者某个特定的人. 如果要添加情境, 请输入
\"
@
\"
和情境名. 更多关于todo.txt的清晰请参见<a href=
\"
https://github.com/todotxt/todo.txt
\"
target=
\"
_blank
\"
>click here</a>.<br><br>项目名和情境都不允许使用空格, 所以请使用一个单词. 你可以指定多个项目和情境. "
,
"helpTabDatesRecurrencesTitle1"
:
"添加日期"
,
"helpTabDatesRecurrencesBody1"
:
"sleek会给任务自动添加创建日期. 如果你想要修改可以优先度旁边找到日期选择. 有到期的任务将显示在列表上部, 到期越接近当前日期位置越高. 如果到期设置为今天或者过去的日期, 则会被标记为红色, 且显示在列表最上面. 如果要添加到期可以使用
\"
due:
\"
日期格式为 <strong>YYYY-MM-DD (e.g. due:2021-03-07)</strong>. 你也可以使用到期选择器, 会自动帮你调整日期格式. "
,
"helpTabDatesRecurrencesTitle2"
:
"添加重复"
,
"helpTabDatesRecurrencesBody2"
:
"设置到期后可以定义重复. 如果今天到期任务设置为按周重复, (任务完成时)sleek会复制并设置任务日期. 你可以使用重复选择器, 或者输入 <strong>rec:</strong> 和 <strong>d</strong> (每天), <strong>w</strong> (每周), <strong>m</strong> (每月) or <strong>y</strong> (每年)."
,
"helpTabKeyboardTR7TD1"
:
"设置优先度"
,
"helpTabKeyboardTR8TD1"
:
"Toggle filter drawer"
,
"helpTabKeyboardTR10TD1"
:
"提交任务"
,
"submitIssuesOnGithub"
:
"在Github上提交问题"
,
"reviewSourceforge"
:
"在SourceForge上给我们建议"
,
"reviewWindowsStore"
:
"在Windows Store上给我们建议"
,
"shareTwitter"
:
"通过Twitter分享sleek"
,
"shareFacebook"
:
"通过Facebook分享sleek"
,
"shareLinkedin"
:
"通过LinkedI分享sleek"
}
src/render.js
View file @
c6bdca60
...
...
@@ -56,6 +56,7 @@ const todoTableSearch = document.getElementById("todoTableSearch");
const
todoTableSearchContainer
=
document
.
getElementById
(
"
todoTableSearchContainer
"
);
const
welcomeToSleek
=
document
.
getElementById
(
"
welcomeToSleek
"
);
let
append
=
false
,
_paq
,
a0
,
a1
,
appData
,
...
...
@@ -819,6 +820,9 @@ function setFriendlyLanguageNames() {
case
"
fr
"
:
friendlyLanguageName
=
"
Français
"
break
;
case
"
cn
"
:
friendlyLanguageName
=
"
Simplified Chinese
"
break
;
default
:
return
;
}
...
...
@@ -861,9 +865,9 @@ function showOnboarding(variable) {
function
showResultStats
()
{
try
{
// we show some information on filters if any are set
if
(
todos
.
visibleRows
!=
todos
.
items
.
objects
.
length
)
{
if
(
todos
.
items
.
filtered
.
length
!=
todos
.
items
.
objects
.
length
)
{
resultStats
.
classList
.
add
(
"
is-active
"
);
resultStats
.
firstElementChild
.
innerHTML
=
translations
.
visibleTodos
+
"
<strong>
"
+
todos
.
visibleRows
+
"
</strong>
"
+
translations
.
of
+
"
<strong>
"
+
todos
.
items
.
objects
.
length
+
"
</strong>
"
;
resultStats
.
firstElementChild
.
innerHTML
=
translations
.
visibleTodos
+
"
<strong>
"
+
todos
.
items
.
filtered
.
length
+
"
</strong>
"
+
translations
.
of
+
"
<strong>
"
+
todos
.
items
.
objects
.
length
+
"
</strong>
"
;
return
Promise
.
resolve
(
"
Info: Result box is shown
"
);
}
else
{
resultStats
.
classList
.
remove
(
"
is-active
"
);
...
...
@@ -939,7 +943,7 @@ function showFiles() {
return
Promise
.
reject
(
error
);
}
}
async
function
startBuilding
(
searchString
)
{
async
function
startBuilding
(
searchString
,
append
)
{
try
{
t0
=
performance
.
now
();
...
...
@@ -950,7 +954,7 @@ async function startBuilding(searchString) {
const
groups
=
await
todos
.
generateGroups
(
todos
.
items
.
filtered
);
await
todos
.
generateTable
(
groups
);
await
todos
.
generateTable
(
groups
,
append
);
userData
=
await
getUserData
();
...
...
@@ -958,14 +962,14 @@ async function startBuilding(searchString) {
showResultStats
();
t1
=
performance
.
now
();
console
.
info
(
"
Table build:
"
,
t1
-
t0
,
"
ms
"
);
console
.
info
(
"
Table build:
"
,
performance
.
now
()
-
t0
,
"
ms
"
);
}
catch
(
error
)
{
error
.
functionName
=
startBuilding
.
name
;
return
Promise
.
reject
(
error
);
}
}
window
.
onload
=
async
function
()
{
a0
=
performance
.
now
();
...
...
src/scss/style.scss
View file @
c6bdca60
...
...
@@ -14,8 +14,7 @@ html {
height
:
100%
;
}
body
{
font-family
:
"FreeSans"
;
font-size
:
16px
;
font
:
16px
"FreeSans"
,
sans-serif
;
height
:
100%
;
margin
:
auto
;
-webkit-user-select
:
none
;
...
...
@@ -140,7 +139,7 @@ svg {
}
code
,
pre
{
font-family
:
SFMono-Regular
,
Consolas
,
Liberation
Mono
,
Menlo
,
monospace
!
important
;
background-color
:
transparent
!
important
;
background-color
:
$light-grey
;
color
:
inherit
!
important
;
}
nav
{
...
...
@@ -1033,7 +1032,7 @@ nav {
padding-right
:
3em
;
}
table
.settings
tr
td
:last-child
{
min-width
:
1
0
em
;
min-width
:
1
2
em
;
text-align
:
center
;
}
table
.shortcuts
td
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment