Skip to content

Commit e427e84

Browse files
committed
Add comment field
1 parent f135dd5 commit e427e84

6 files changed

Lines changed: 48 additions & 1 deletion

File tree

src/checklist.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface ActionItem {
1818
latchable?: boolean;
1919
/** Duration in seconds for timed items. */
2020
timer?: number;
21+
comment?: string;
2122
}
2223

2324
export interface ConditionalItem {

src/components/editor/ItemOverlay.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function ActionOverlay({ item }: { item: ActionItem }) {
3737
<span className="vH-flag">TIMER</span>
3838
<span className="vH-flag">DEFER</span>
3939
<span className="vH-flag">FOLLOW-ON</span>
40+
<span className="vH-flag">COMMENT</span>
4041
</div>
4142
{item.sensed !== undefined && (
4243
<div className="vH-meta" style={sensedStyle}>
@@ -63,6 +64,11 @@ function ActionOverlay({ item }: { item: ActionItem }) {
6364
<span className="vH-meta-v"></span>
6465
</div>
6566
)}
67+
{item.comment && (
68+
<div className="vH-comment" style={{ whiteSpace: "pre", lineHeight: 1.4 }}>
69+
{item.comment}
70+
</div>
71+
)}
6672
</div>
6773
</div>
6874
);

src/components/editor/items/ActionItemView.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function ActionItemView({ item, number }: { item: ActionItem; number: str
1616
const [deferOpen, setDeferOpen] = useState(!!item.defer);
1717
const [followOnOpen, setFollowOnOpen] = useState(!!item.followOn);
1818
const [timerOpen, setTimerOpen] = useState(item.timer !== undefined);
19+
const [commentOpen, setCommentOpen] = useState(item.comment !== undefined);
1920

2021
const otherChecklists = db ? allChecklists(db).filter((cl) => cl.id !== checklist?.id) : [];
2122
const nameOf = (id: string) => otherChecklists.find((cl) => cl.id === id)?.name ?? id;
@@ -33,6 +34,7 @@ export function ActionItemView({ item, number }: { item: ActionItem; number: str
3334
const deferActive = !!item.defer || deferOpen;
3435
const followOnActive = !!item.followOn || followOnOpen;
3536
const timerActive = item.timer !== undefined || timerOpen;
37+
const commentActive = item.comment !== undefined || commentOpen;
3638

3739
const deferUnresolved = !!item.defer && !isResolved(item.defer);
3840
const followOnUnresolved = !!item.followOn && !isResolved(item.followOn);
@@ -64,6 +66,15 @@ export function ActionItemView({ item, number }: { item: ActionItem; number: str
6466
}
6567
}
6668

69+
function toggleComment() {
70+
if (item.comment !== undefined) {
71+
update({ comment: undefined });
72+
setCommentOpen(false);
73+
} else {
74+
setCommentOpen(!commentOpen);
75+
}
76+
}
77+
6778
return (
6879
<RowFrame item={item} number={number} color={color}>
6980
<div className="vH-action">
@@ -136,6 +147,13 @@ export function ActionItemView({ item, number }: { item: ActionItem; number: str
136147
>
137148
FOLLOW-ON{followOnUnresolved && " ⚠"}
138149
</button>
150+
<button
151+
className={`vH-flag${commentActive ? " active" : ""}`}
152+
title="Add a comment"
153+
onClick={toggleComment}
154+
>
155+
COMMENT
156+
</button>
139157
</div>
140158
{item.sensed !== undefined && (
141159
<div className="vH-meta" style={{ "--sensed": color } as React.CSSProperties}>
@@ -211,6 +229,16 @@ export function ActionItemView({ item, number }: { item: ActionItem; number: str
211229
</span>
212230
</div>
213231
)}
232+
{commentActive && (
233+
<EditableText
234+
value={item.comment ?? ""}
235+
onCommit={(v) => update({ comment: v || undefined })}
236+
multiline
237+
autoSize
238+
placeholder="Comment…"
239+
className="vH-comment"
240+
/>
241+
)}
214242
</RowFrame>
215243
);
216244
}

src/components/editor/items/items.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,16 @@
100100
min-width: 16px;
101101
}
102102
.vH-meta {
103-
margin-top: 3px;
103+
margin-top: 6px;
104104
font-family: var(--font-mono);
105105
font-size: 11.5px;
106106
display: flex;
107107
gap: 4px;
108108
align-items: baseline;
109109
}
110+
.vH-meta + .vH-meta {
111+
margin-top: 3px;
112+
}
110113
.vH-meta-k {
111114
color: var(--sensed);
112115
opacity: 0.7;
@@ -180,6 +183,10 @@
180183
border-color: color-mix(in oklab, #ffe300 45%, transparent);
181184
background: color-mix(in oklab, #ffe300 12%, transparent);
182185
}
186+
.vH-comment {
187+
margin-top: 6px;
188+
}
189+
183190
.vH-ext {
184191
margin-top: 3px;
185192
font-family: var(--font-mono);

src/migrations.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ function migrateItem(raw: unknown): ChecklistItem | null {
173173
inverted: typeof it.inverted === "boolean" ? it.inverted : undefined,
174174
latchable: typeof it.latchable === "boolean" ? it.latchable : undefined,
175175
timer: typeof it.timer === "number" && it.timer > 0 ? it.timer : undefined,
176+
comment: typeof it.comment === "string" ? it.comment : undefined,
176177
};
177178
}
178179
case "sensed": {

src/schemas.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const externalItemSchema: z.ZodType<ExternalItem> = z.lazy(() =>
4040
followOn: z.string().optional(),
4141
...baseSensed,
4242
timer: z.number().int().positive().optional(),
43+
comment: z.string().optional(),
4344
}),
4445
// Legacy "sensed" type — accepted on import, converted to "action".
4546
z.object({
@@ -90,6 +91,7 @@ export type ExternalItem =
9091
inverted?: boolean;
9192
latchable?: boolean;
9293
timer?: number;
94+
comment?: string;
9395
}
9496
| {
9597
type: "sensed";
@@ -154,6 +156,7 @@ function stripItem(it: ChecklistItem, nameOf: (id: string) => string | undefined
154156
inverted: it.inverted ? true : undefined,
155157
latchable: it.latchable ? true : undefined,
156158
timer: it.timer,
159+
comment: it.comment,
157160
};
158161
case "conditional":
159162
return {
@@ -213,6 +216,7 @@ function hydrateItem(it: ExternalItem, idOf: (name: string) => string | undefine
213216
inverted: it.inverted,
214217
latchable: it.latchable,
215218
timer: it.timer,
219+
comment: it.comment,
216220
};
217221
case "sensed":
218222
return {

0 commit comments

Comments
 (0)