Today screen: tabs (feed, suggestions, in-progress, captured) + inbox sort/group

- Новый экран 'Сегодня' разбит на 4 вкладки: Лента, Предложения,
  В работе, Захвачено
- Лента отображает события за сегодня с кликабельными сущностями
- Предложения вынесены в отдельную вкладку (только предложения)
- В работе: изменённые файлы/заметки/действия за сегодня с сортировками
- Захвачено: захваченные элементы за сегодня с сортировками
- Неразобранное: сортировка по дате/имени/типу с направлением
- Неразобранное: переключатель 'Группировать по месту захвата'
- TodayScreen.svelte: новый компонент с 4 вкладками
- Новые i18n ключи для вкладок и сортировок
- Backend: ListTodayInProgress, ListTodayCaptures bindings
- Все переходы из вкладок ведут в соответствующее место программы
This commit is contained in:
2026-06-05 16:17:22 +08:00
parent c8aaf36533
commit 2ed2ecf77a
9 changed files with 717 additions and 113 deletions
+40
View File
@@ -220,3 +220,43 @@ func (a *App) eventDTOWithPath(e activity.Event) EventDTO {
dto.NodePath = a.nodes.Path(e.NodeID)
return dto
}
// ListTodayInProgress returns today's modification events — items the user worked on.
func (a *App) ListTodayInProgress() ([]EventDTO, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
events, err := a.activity.ListTodayEvents()
if err != nil {
return nil, err
}
modTypes := map[string]bool{
activity.TypeNoteCreated: true,
activity.TypeNoteUpdated: true,
activity.TypeNoteDeleted: true,
activity.TypeFileAdded: true,
activity.TypeFileDeleted: true,
activity.TypeFileRenamed: true,
activity.TypeFileCopied: true,
activity.TypeFileMoved: true,
activity.TypeFolderAdded: true,
activity.TypeFolderDeleted: true,
activity.TypeFolderRenamed: true,
activity.TypeFolderMoved: true,
activity.TypeNodeCreated: true,
activity.TypeNodeUpdated: true,
activity.TypeNodeDeleted: true,
activity.TypeActionCreated: true,
activity.TypeActionDone: true,
}
result := make([]EventDTO, 0, len(events))
for _, e := range events {
if modTypes[e.EventType] {
result = append(result, a.eventDTOWithPath(e))
}
}
sort.Slice(result, func(i, j int) bool {
return result[i].CreatedAt > result[j].CreatedAt
})
return result, nil
}
+34 -1
View File
@@ -1,6 +1,11 @@
package main
import "fmt"
import (
"fmt"
"time"
"verstak/internal/core/activity"
)
type InboxNodeDTO struct {
NodeDTO
@@ -189,6 +194,34 @@ func (a *App) clearCaptureMeta(nodeID string) error {
return err
}
// ListTodayCaptures returns inbox nodes captured today.
func (a *App) ListTodayCaptures() ([]InboxNodeDTO, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
all, err := a.ListInboxNodes()
if err != nil {
return nil, err
}
start, end := activity.TodayBoundaries()
startTime, _ := time.Parse(time.RFC3339, start)
endTime, _ := time.Parse(time.RFC3339, end)
result := make([]InboxNodeDTO, 0, len(all))
for _, item := range all {
if item.CapturedAt == "" {
continue
}
t, err := time.Parse(time.RFC3339, item.CapturedAt)
if err != nil {
continue
}
if (t.Equal(startTime) || t.After(startTime)) && t.Before(endTime) {
result = append(result, item)
}
}
return result, nil
}
func (a *App) captureMeta(nodeID, key string) string {
v, ok, err := a.nodes.MetaGet(nodeID, key)
if err != nil || !ok {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -19,8 +19,8 @@
background: #13131f;
}
</style>
<script type="module" crossorigin src="/assets/main-DzZpCe3r.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-BoZGEeiu.css">
<script type="module" crossorigin src="/assets/main-DNvOo-CH.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-CXWRKHDk.css">
</head>
<body>
<div id="app"></div>