tuiboard 0.8.1 → 0.8.2
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/CHANGELOG.md +12 -0
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/store/parsers.test.ts +64 -0
- package/src/store/parsers.ts +6 -5
- package/src/ui/Modal.tsx +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to **tuiboard** are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.8.2] - 2026-06-04
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Consistent date shortcuts everywhere.** `m` now means "tomorrow" in every
|
|
12
|
+
date input (the schedule modal, the new-event/edit modals, and quick-add),
|
|
13
|
+
matching the board's `m` = tomorrow key — so `t`/`m` = today/tomorrow whether
|
|
14
|
+
you press them on a card or type them into a field. `tm`/`tom`/`tomorrow`/
|
|
15
|
+
`domani` still work as aliases. Hints and the help screen updated to lead with
|
|
16
|
+
`m`. (Audit of all shortcut surfaces found this was the only divergence; the
|
|
17
|
+
rest — `t`, `-`/empty to clear, weekdays, ±N — were already aligned.)
|
|
18
|
+
|
|
8
19
|
## [0.8.1] - 2026-06-04
|
|
9
20
|
|
|
10
21
|
### Added
|
|
@@ -152,6 +163,7 @@ First public release on npm. This entry captures the full feature set at launch.
|
|
|
152
163
|
|
|
153
164
|
Built with [OpenTUI](https://opentui.com) + SolidJS on Bun.
|
|
154
165
|
|
|
166
|
+
[0.8.2]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.8.2
|
|
155
167
|
[0.8.1]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.8.1
|
|
156
168
|
[0.8.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.8.0
|
|
157
169
|
[0.7.3]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.7.3
|
package/README.md
CHANGED
|
@@ -261,14 +261,15 @@ show in the picker. Append tokens to the title to set the **time** and **date**:
|
|
|
261
261
|
|
|
262
262
|
```
|
|
263
263
|
Standup 9:00-9:30 # today (or the viewed day), 09:00–09:30
|
|
264
|
-
Lunch
|
|
264
|
+
Lunch m 12-13 # tomorrow (m), 12:00–13:00
|
|
265
265
|
Review 2026-06-10 15-16 # that date, 15:00–16:00
|
|
266
266
|
Call +3 16:00-16:30 # in 3 days · lun = next Monday also works
|
|
267
267
|
Holiday 2026-12-25 allday # an all-day event (no time)
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
The date defaults to whichever day the Agenda is showing; an explicit date token
|
|
271
|
-
(`t` / `
|
|
271
|
+
(`t` / `m` / `+N` / weekday / `YYYY-MM-DD` — the same `t`/`m` = today/tomorrow as
|
|
272
|
+
the board keys) overrides it. The time is taken from
|
|
272
273
|
the clicked slot, or `HH:MM-HH:MM`. Add **`allday`** (or `all-day`) anywhere in
|
|
273
274
|
the title to create an all-day event instead — it lands in the top chip strip.
|
|
274
275
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tuiboard",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Terminal kanban for markdown task boards, with optional Today/Tomorrow planner, 24h agenda + calendar overlay, and a live Claude Code agent view. Use only the panels you want.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { isoToday, isoTomorrow } from "./index";
|
|
4
|
+
import { parseDateShortcut, parseQuickAdd, parseTimeBlockShortcut } from "./parsers";
|
|
5
|
+
|
|
6
|
+
describe("parseDateShortcut", () => {
|
|
7
|
+
it("maps t / today / oggi to today", () => {
|
|
8
|
+
expect(parseDateShortcut("t")).toBe(isoToday());
|
|
9
|
+
expect(parseDateShortcut("today")).toBe(isoToday());
|
|
10
|
+
expect(parseDateShortcut("oggi")).toBe(isoToday());
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("maps m to tomorrow (consistent with the board's m key)", () => {
|
|
14
|
+
expect(parseDateShortcut("m")).toBe(isoTomorrow());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("keeps tm / tom / tomorrow / domani as tomorrow aliases", () => {
|
|
18
|
+
expect(parseDateShortcut("tm")).toBe(isoTomorrow());
|
|
19
|
+
expect(parseDateShortcut("tom")).toBe(isoTomorrow());
|
|
20
|
+
expect(parseDateShortcut("tomorrow")).toBe(isoTomorrow());
|
|
21
|
+
expect(parseDateShortcut("domani")).toBe(isoTomorrow());
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("is case-insensitive", () => {
|
|
25
|
+
expect(parseDateShortcut("M")).toBe(isoTomorrow());
|
|
26
|
+
expect(parseDateShortcut("T")).toBe(isoToday());
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("clears on empty / dash, fails on garbage", () => {
|
|
30
|
+
expect(parseDateShortcut("")).toBeUndefined();
|
|
31
|
+
expect(parseDateShortcut("-")).toBeUndefined();
|
|
32
|
+
expect(parseDateShortcut("zzz")).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("parses ISO dates literally", () => {
|
|
36
|
+
expect(parseDateShortcut("2026-06-10")).toBe("2026-06-10");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("parseQuickAdd date tokens", () => {
|
|
41
|
+
it("treats a standalone m as tomorrow and strips it from the title", () => {
|
|
42
|
+
const r = parseQuickAdd("Pay invoice m");
|
|
43
|
+
expect(r.scheduled).toBe(isoTomorrow());
|
|
44
|
+
expect(r.title).toBe("Pay invoice");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("treats a standalone t as today", () => {
|
|
48
|
+
const r = parseQuickAdd("Standup t");
|
|
49
|
+
expect(r.scheduled).toBe(isoToday());
|
|
50
|
+
expect(r.title).toBe("Standup");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("parseTimeBlockShortcut", () => {
|
|
55
|
+
it("parses loose H-H ranges and HH:MM-HH:MM", () => {
|
|
56
|
+
expect(parseTimeBlockShortcut("9-11")).toEqual({ startMin: 540, endMin: 660 });
|
|
57
|
+
expect(parseTimeBlockShortcut("09:30-10:45")).toEqual({ startMin: 570, endMin: 645 });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("clears on empty / dash", () => {
|
|
61
|
+
expect(parseTimeBlockShortcut("")).toBeUndefined();
|
|
62
|
+
expect(parseTimeBlockShortcut("-")).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
});
|
package/src/store/parsers.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Date input shortcuts:
|
|
5
5
|
* t → today
|
|
6
|
-
* tm | tom
|
|
6
|
+
* m | tm | tom → tomorrow (m matches the board's `m` = tomorrow key)
|
|
7
7
|
* -N → N days ago
|
|
8
8
|
* +N → N days ahead
|
|
9
9
|
* lun/mar/.../dom → next weekday (Italian short)
|
|
@@ -47,7 +47,8 @@ export function parseDateShortcut(input: string): string | undefined | null {
|
|
|
47
47
|
if (s === "-") return undefined;
|
|
48
48
|
|
|
49
49
|
if (s === "t" || s === "today" || s === "oggi") return isoToday();
|
|
50
|
-
|
|
50
|
+
// `m` mirrors the board's `m` = tomorrow key; `tm`/`tom`/… kept as aliases.
|
|
51
|
+
if (s === "m" || s === "tm" || s === "tom" || s === "tomorrow" || s === "domani") return isoTomorrow();
|
|
51
52
|
|
|
52
53
|
// Relative ±N
|
|
53
54
|
const rel = s.match(/^([+-])(\d+)$/);
|
|
@@ -167,7 +168,7 @@ export interface QuickAddResult {
|
|
|
167
168
|
* Parse a free-form quick-add string. Recognized tokens:
|
|
168
169
|
* @name → assignee
|
|
169
170
|
* #tag → tag
|
|
170
|
-
* t,
|
|
171
|
+
* t, m, +N → scheduled date shortcut (m = tomorrow, matches the board key)
|
|
171
172
|
* YYYY-MM-DD → scheduled date literal
|
|
172
173
|
* HH:MM-HH:MM → time block (also sets scheduled to today if missing)
|
|
173
174
|
* 9-11 → 09:00-11:00 time block
|
|
@@ -220,10 +221,10 @@ export function parseQuickAdd(input: string): QuickAddResult {
|
|
|
220
221
|
}
|
|
221
222
|
continue;
|
|
222
223
|
}
|
|
223
|
-
// Date: t, tm, +N, YYYY-MM-DD
|
|
224
|
+
// Date: t, m/tm, +N, YYYY-MM-DD
|
|
224
225
|
const lower = tok.toLowerCase();
|
|
225
226
|
if (
|
|
226
|
-
lower === "t" || lower === "tm" || lower === "tom" ||
|
|
227
|
+
lower === "t" || lower === "m" || lower === "tm" || lower === "tom" ||
|
|
227
228
|
lower === "today" || lower === "tomorrow" ||
|
|
228
229
|
lower === "oggi" || lower === "domani" ||
|
|
229
230
|
/^[+-]\d+$/.test(lower) ||
|
package/src/ui/Modal.tsx
CHANGED
|
@@ -139,7 +139,7 @@ function AddModal(props: { store: TuiStore; columnIndex: number }) {
|
|
|
139
139
|
return (
|
|
140
140
|
<DialogShell
|
|
141
141
|
title="New task"
|
|
142
|
-
hint="Quick syntax: @assignee #tag t/
|
|
142
|
+
hint="Quick syntax: @assignee #tag t/m/+N HH:MM-HH:MM 🔺 · Enter to add, Esc to cancel"
|
|
143
143
|
width={70}
|
|
144
144
|
>
|
|
145
145
|
<input
|
|
@@ -195,7 +195,7 @@ function ScheduleModal(props: { store: TuiStore; modal: Extract<NonNullable<TuiS
|
|
|
195
195
|
function submit(text: string) {
|
|
196
196
|
const d = parseDateShortcut(text);
|
|
197
197
|
if (d === null) {
|
|
198
|
-
setError(`Cannot parse "${text}". Try: t ·
|
|
198
|
+
setError(`Cannot parse "${text}". Try: t · m · +3 · lun · 2026-06-15`);
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
201
|
const n = props.store.applyToMarkedOr(props.modal.ref, (r) =>
|
|
@@ -208,7 +208,7 @@ function ScheduleModal(props: { store: TuiStore; modal: Extract<NonNullable<TuiS
|
|
|
208
208
|
return (
|
|
209
209
|
<DialogShell
|
|
210
210
|
title={markedCount > 1 ? `Schedule · ${markedCount} tasks` : "Schedule"}
|
|
211
|
-
hint="t = today ·
|
|
211
|
+
hint="t = today · m = tomorrow · +3 = in 3 days · lun = next Monday · 2026-06-15 · empty/-clear · Esc to cancel"
|
|
212
212
|
width={70}
|
|
213
213
|
>
|
|
214
214
|
<input
|
|
@@ -370,7 +370,7 @@ function EventModal(props: { store: TuiStore }) {
|
|
|
370
370
|
fallback={
|
|
371
371
|
<DialogShell
|
|
372
372
|
title="New event"
|
|
373
|
-
hint={`${formatHm(picker()!.startMin)}-${formatHm(picker()!.endMin)} · add a time, a date (
|
|
373
|
+
hint={`${formatHm(picker()!.startMin)}-${formatHm(picker()!.endMin)} · add a time, a date (m · +3 · 2026-06-10), or "allday" · Enter add · Esc`}
|
|
374
374
|
>
|
|
375
375
|
<input
|
|
376
376
|
focused
|
|
@@ -452,7 +452,7 @@ function EventEditModal(props: { store: TuiStore }) {
|
|
|
452
452
|
<Show when={sel()}>
|
|
453
453
|
<DialogShell
|
|
454
454
|
title={`Edit event · ${shortDate(sel()!.dateIso)}`}
|
|
455
|
-
hint="change title, HH:MM-HH:MM, and/or a date (
|
|
455
|
+
hint="change title, HH:MM-HH:MM, and/or a date (m · +3 · lun · 2026-06-10) · Enter save · Esc"
|
|
456
456
|
>
|
|
457
457
|
<input
|
|
458
458
|
focused
|
|
@@ -813,7 +813,7 @@ function HelpModal(props: { store: TuiStore }) {
|
|
|
813
813
|
<span style={{ fg: T.text }}>{" Enter Toggle done\n"}</span>
|
|
814
814
|
<span style={{ fg: T.text }}>{" o Open detail view\n"}</span>
|
|
815
815
|
<span style={{ fg: T.text }}>{" e Edit task text\n"}</span>
|
|
816
|
-
<span style={{ fg: T.text }}>{" s Schedule date modal (t/
|
|
816
|
+
<span style={{ fg: T.text }}>{" s Schedule date modal (t/m/+N/lun/YYYY-MM-DD — same t/m as the board)\n"}</span>
|
|
817
817
|
<span style={{ fg: T.text }}>{" t Set scheduled = today\n"}</span>
|
|
818
818
|
<span style={{ fg: T.text }}>{" m Set scheduled = tomorrow\n"}</span>
|
|
819
819
|
<span style={{ fg: T.text }}>{" . Schedule now — time block at next 15-min slot (30min)\n"}</span>
|