lune
created pr with
124.1
changed status to
closed
Closing as superceded by 126
cmds
checkout latest patchset:
ssh pr.pico.sh print 124 | git am -3checkout any patchset in a patch request:
ssh pr.pico.sh print 124.[rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 124set PR to open (enables RSS notifications):
ssh pr.pico.sh pr open 124set PR to draft (stops RSS notifications):
ssh pr.pico.sh pr draft 124
Patchset
124.1
listing real name of tasks
juan
construction of db for management of decrypted names of tasks
2026-04-21T03:21:16Zjuan
fix UTF8 chracteres
2026-04-21T05:43:09Zjuan
filter by status and coloring types of status
2026-04-21T21:13:24Zjuan
update help
2026-04-21T21:18:53Zjuan
list areas names
2026-04-21T21:21:36Zjuan
→ adjust table tasks to truncate name and list area
2026-04-21T21:44:53Zjuan
partial implementation of listing tasks by areas
2026-04-21T21:56:40Zjuan
final implementation of listing tasks by areas with UTF-8 chracteres
2026-04-21T22:05:20Zjuan
sort on task list to show first do now
2026-04-21T22:10:20Zjuan
update with 8 char of id and db.go
2026-04-21T22:33:29Zjuan
partial implementation of TUI menu on task update
2026-04-21T23:27:54Zjuan
task name correcion on TUI menu of Update task
2026-04-21T23:35:35Zjuan
TUI menu for update task with others flags
2026-04-21T23:38:19Zjuan
helper for TUI update task
2026-04-21T23:43:02Zjuan
search by first 8 char of note id for editing it
2026-04-21T23:49:56Zjuan
partial implementation on note TUI menu for update
2026-04-22T00:00:17Zjuan
TUI menu notebook just with tags
2026-04-22T00:05:46Zjuan
Note update now let you insert only the first 8 char of notebook id in update
2026-04-22T00:20:55Zjuan
implementation of (lune note notebook-list) for listing all notebooks and its ID
2026-04-22T00:25:53Zjuan
partial implementation of lune note show-content ID
2026-04-22T01:10:51Zjuan
convert \n in new line and \ in space for lune note show-content ID
2026-04-22T02:01:11Zjuan
2026-04-22T02:18:42Z
adjust table tasks to truncate name and list area
juan
2026-04-21T21:56:40ZSemantic diff summary
1 added,
4 modified,
0 signature changed,
1 removed
across 2 analyzed files
+56
-10
cmd/task/list.go
#
| ... | ... | @@ -246,6 +250,7 @@ func outputJSONEnriched(cmd *cobra.Command, tasks []enrichedTask) error { | |
| 246 | 250 | } | |
| 247 | 251 | ||
| 248 | 252 | func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error { | |
| 253 | + | cfg, _ := config.Load() | |
| 249 | 254 | rows := make([][]string, 0, len(tasks)) | |
| 250 | 255 | ||
| 251 | 256 | for _, task := range tasks { |
| ... | ... | @@ -264,36 +269,77 @@ func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error { | |
| 264 | 269 | name = task.ID[:8] + "..." | |
| 265 | 270 | } | |
| 266 | 271 | ||
| 267 | - | rows = append(rows, []string{name, status, scheduled}) | |
| 272 | + | // Resolve Area name | |
| 273 | + | area := "-" | |
| 274 | + | if task.AreaID != nil { | |
| 275 | + | area = *task.AreaID | |
| 276 | + | if cfg != nil { | |
| 277 | + | if a := cfg.AreaByID(*task.AreaID); a != nil { | |
| 278 | + | area = a.Key | |
| 279 | + | } else if cfg.Experimental.LocalDB { | |
| 280 | + | // Proactive area lookup in local DB | |
| 281 | + | if dbName, _ := db.EnrichTask(*task.AreaID); dbName != "" { | |
| 282 | + | area = dbName | |
| 283 | + | } | |
| 284 | + | } | |
| 285 | + | } | |
| 286 | + | } | |
| 287 | + | ||
| 288 | + | // Truncate name and area for clean table layout | |
| 289 | + | if len(name) > 50 { | |
| 290 | + | name = name[:47] + "..." | |
| 291 | + | } | |
| 292 | + | if len(area) > 20 { | |
| 293 | + | area = area[:17] + "..." | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | rows = append(rows, []string{task.ID[:8], name, status, scheduled, area}) | |
| 268 | 297 | } | |
| 269 | 298 | ||
| 270 | 299 | tbl := table.New(). | |
| 271 | - | Headers("NAME", "STATUS", "SCHEDULED"). | |
| 300 | + | Headers("ID", "NAME", "STATUS", "SCHEDULED", "AREA"). | |
| 272 | 301 | Rows(rows...). | |
| 273 | 302 | StyleFunc(func(row, col int) lipgloss.Style { | |
| 303 | + | style := lipgloss.NewStyle() | |
| 304 | + | ||
| 305 | + | // Set column widths via style | |
| 306 | + | switch col { | |
| 307 | + | case 0: // ID | |
| 308 | + | style = style.Width(10) | |
| 309 | + | case 1: // NAME | |
| 310 | + | style = style.Width(52) | |
| 311 | + | case 2: // STATUS | |
| 312 | + | style = style.Width(12) | |
| 313 | + | case 3: // SCHEDULED | |
| 314 | + | style = style.Width(12) | |
| 315 | + | case 4: // AREA | |
| 316 | + | style = style.Width(22) | |
| 317 | + | } | |
| 318 | + | ||
| 274 | 319 | if row == table.HeaderRow { | |
| 275 | - | return ui.TableHeaderStyle() | |
| 320 | + | return style.Inherit(ui.TableHeaderStyle()) | |
| 276 | 321 | } | |
| 277 | 322 | ||
| 323 | + | // Color logic based on status | |
| 278 | 324 | if row >= 0 && row < len(tasks) { | |
| 279 | 325 | t := tasks[row] | |
| 280 | 326 | if t.Status != nil { | |
| 281 | 327 | switch *t.Status { | |
| 282 | 328 | case lunatask.StatusLater: | |
| 283 | - | return ui.StatusLater | |
| 329 | + | style = style.Inherit(ui.StatusLater) | |
| 284 | 330 | case lunatask.StatusNext: | |
| 285 | - | return ui.StatusNext | |
| 331 | + | style = style.Inherit(ui.StatusNext) | |
| 286 | 332 | case lunatask.StatusInProgress: | |
| 287 | - | return ui.StatusInProgress | |
| 333 | + | style = style.Inherit(ui.StatusInProgress) | |
| 288 | 334 | case lunatask.StatusWaiting: | |
| 289 | - | return ui.StatusWaiting | |
| 335 | + | style = style.Inherit(ui.StatusWaiting) | |
| 290 | 336 | case lunatask.StatusCompleted: | |
| 291 | - | return ui.StatusCompleted | |
| 337 | + | style = style.Inherit(ui.StatusCompleted) | |
| 292 | 338 | } | |
| 293 | 339 | } | |
| 294 | 340 | } | |
| 295 | 341 | ||
| 296 | - | return lipgloss.NewStyle() | |
| 342 | + | return style | |
| 297 | 343 | }). | |
| 298 | 344 | Border(ui.TableBorder()). | |
| 299 | 345 | BorderRow(true) |
+46
-9
internal/db/reader.go
#
| ... | ... | @@ -56,10 +56,20 @@ func SetMasterPassword(password string) error { | |
| 56 | 56 | return os.WriteFile(path, f, 0600) | |
| 57 | 57 | } | |
| 58 | 58 | ||
| 59 | - | func EnrichTask(taskID string) (string, error) { | |
| 59 | + | var ( | |
| 60 | + | nameCache = make(map[string]string) | |
| 61 | + | cacheWarmed = false | |
| 62 | + | ) | |
| 63 | + | ||
| 64 | + | // WarmCache loads all names (tasks, notes, areas, notebooks) into memory. | |
| 65 | + | func WarmCache() error { | |
| 66 | + | if cacheWarmed { | |
| 67 | + | return nil | |
| 68 | + | } | |
| 69 | + | ||
| 60 | 70 | dbPath := GetDefaultPath() | |
| 61 | 71 | if _, err := os.Stat(dbPath); os.IsNotExist(err) { | |
| 62 | - | return "", nil | |
| 72 | + | return nil | |
| 63 | 73 | } | |
| 64 | 74 | ||
| 65 | 75 | o := &opt.Options{ |
| ... | ... | @@ -78,15 +88,42 @@ func EnrichTask(taskID string) (string, error) { | |
| 78 | 88 | ||
| 79 | 89 | for iter.Next() { | |
| 80 | 90 | val := iter.Value() | |
| 81 | - | if bytes.Contains(val, []byte(taskID)) { | |
| 82 | - | re := regexp.MustCompile(`"(title|name)"\s*:\s*"([^"]+)"`) | |
| 83 | - | matches := re.FindSubmatch(val) | |
| 84 | - | if len(matches) > 2 { | |
| 85 | - | return cleanName(string(matches[2])), nil | |
| 91 | + | // Try to find JSON-like blobs | |
| 92 | + | if bytes.Contains(val, []byte("\"id\"")) && bytes.Contains(val, []byte("\"name\"")) { | |
| 93 | + | start := bytes.IndexByte(val, '{') | |
| 94 | + | end := bytes.LastIndexByte(val, '}') | |
| 95 | + | if start != -1 && end != -1 && end > start { | |
| 96 | + | jsonBytes := val[start : end+1] | |
| 97 | + | id := extractField(jsonBytes, "id") | |
| 98 | + | name := extractField(jsonBytes, "name") | |
| 99 | + | if id != "" && name != "" { | |
| 100 | + | nameCache[id] = cleanName(name) | |
| 101 | + | } | |
| 102 | + | } | |
| 103 | + | } else if bytes.Contains(val, []byte("\"id\"")) && bytes.Contains(val, []byte("\"title\"")) { | |
| 104 | + | // Notes use "title" | |
| 105 | + | start := bytes.IndexByte(val, '{') | |
| 106 | + | end := bytes.LastIndexByte(val, '}') | |
| 107 | + | if start != -1 && end != -1 && end > start { | |
| 108 | + | jsonBytes := val[start : end+1] | |
| 109 | + | id := extractField(jsonBytes, "id") | |
| 110 | + | title := extractField(jsonBytes, "title") | |
| 111 | + | if id != "" && title != "" { | |
| 112 | + | nameCache[id] = cleanName(title) | |
| 113 | + | } | |
| 86 | 114 | } | |
| 87 | 115 | } | |
| 88 | 116 | } | |
| 89 | - | return "", nil | |
| 117 | + | cacheWarmed = true | |
| 118 | + | return nil | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | // EnrichTask attempts to find the task or note name in the cache or local database. | |
| 122 | + | func EnrichTask(taskID string) (string, error) { | |
| 123 | + | if !cacheWarmed { | |
| 124 | + | _ = WarmCache() | |
| 125 | + | } | |
| 126 | + | return nameCache[taskID], nil | |
| 90 | 127 | } | |
| 91 | 128 | ||
| 92 | 129 | // Area represents a Lunatask area found in the local database. |