docs: record examples to start from, and the pass over the add forms
PRD F13 said the form asked how often after Kind and had no word on examples; F17 and DESIGN.md had no example row. Adds the spec and plan, and ticks the TODO item. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c66b812b1
commit
0234478db0
5 files changed
+296
-4
No files matched your search
@@ -45,7 +45,9 @@
|
||||
`src/ingest/parsers/holodori.ts` against a new fixture, keeping the old one. The wiki stopped
|
||||
being maintained (`holodori.wiki` is a Miraheze wiki) → find another source; assess it in
|
||||
`docs/SOURCES.md` as the others are.
|
||||
- [ ] Give *Add an event* and *Add a task* easy-to-use templates, and give both forms a UI/UX pass.
|
||||
- [x] Give *Add an event* and *Add a task* easy-to-use templates, and give both forms a UI/UX pass.
|
||||
Done 2026-10-14 as *examples to start from* — see PRD F13 and
|
||||
`docs/superpowers/specs/2026-10-14-form-examples-design.md`.
|
||||
- Templates: one tap fills the common shapes so the reader only adjusts the details — for
|
||||
example a weekly points task, a monthly shop reset, a login event, a banner with a known end.
|
||||
A template only pre-fills fields; nothing is saved until the reader saves, and no figure is
|
||||
|
||||
@@ -415,6 +415,10 @@ selected tab fills with `raised` and turns ink; unselected tabs are faint and go
|
||||
label, a small numeric field on `ground`, `/ 2,100 points`, and a near-pill Save. Weekly and
|
||||
monthly counts sit on their own faint line under the strip's heading, so the reset countdown
|
||||
never wraps.
|
||||
- **Example row** (top of *Add an event* and *Add a task*, never on an edit): a label-type "Start
|
||||
from an example", then neutral pills — `near` border and wash when pressed, the form's own pill
|
||||
rather than a game chip, because an example belongs to no game — and the pressed example's
|
||||
one-line hint in faint. The form's choices (how often, repeat, done when) use the same pill.
|
||||
- **What's new card** (top of the main column, never the rail): the eyebrow, up to five lines of
|
||||
kind (faint, fixed width) · title (muted) · date (faint, hidden on phones), then a near-pill
|
||||
**Got it** and a faint **See all**. Shown once per batch of changes; never modal.
|
||||
|
||||
+17
-3
@@ -355,8 +355,10 @@ Four constraints, each protecting something that already exists:
|
||||
no server to restore from.
|
||||
|
||||
A reader's event may also state how it comes round again. The form asks that
|
||||
directly after **Kind**, before it asks for any date, because the answer
|
||||
decides which dates are even questions:
|
||||
(**How often**) directly after the name, before it asks for any date, because
|
||||
the answer decides which dates are even questions. Its five answers read *Once ·
|
||||
Every day · Every week · Every month · On its own cycle* on screen; the table
|
||||
names them by what is stored:
|
||||
|
||||
| Cadence | What it asks for | What it stores |
|
||||
|---|---|---|
|
||||
@@ -379,6 +381,17 @@ Which of the five a saved event opens in is derived from the rule itself, so an
|
||||
event made before this control existed, or one that arrived by import, opens in
|
||||
whichever answer actually describes it.
|
||||
|
||||
**Examples to start from.** Readers who knew what they wanted could not see which answers produced
|
||||
it, so *Add an event* and *Add a task* open with a row of examples — a banner with a known end, a
|
||||
login event, a monthly shop reset, a recurring endgame cycle, maintenance; a weekly points target, a
|
||||
few days each week, a monthly shop buy-out, a monthly target (`src/shared/templates.ts`). One tap
|
||||
sets the form's **shape** — kind, how often, how it repeats, how it is measured — and nothing else:
|
||||
never a name (the example's text is only a placeholder), a date, a time, a target or a game, and no
|
||||
date is guessed, not even today. A figure the reader did not type would read as one we sourced, the
|
||||
bar built-in tasks are held to. Save stays disabled until every date and figure the shape needs is
|
||||
typed, and says what is still needed rather than greying out in silence. Editing never shows the
|
||||
examples.
|
||||
|
||||
The rule is stored; its occurrences are derived, and each one is an ordinary
|
||||
event everywhere in the app: its own countdown, its own completion, its own
|
||||
daily checklist.
|
||||
@@ -560,7 +573,8 @@ Some chores roll weekly or monthly rather than daily — earning ZZZ's weekly ac
|
||||
finishing Endfield's weekly routine — and no source publishes them, so they were nowhere in the app.
|
||||
A reader can now set them up per game in *Your own*: a title, every week (from a weekday they pick)
|
||||
or every month (from a day of the month), and either **tick it off** (N days in the period, one by
|
||||
default) or **reach a number** (a target such as 2,100, with an optional unit).
|
||||
default) or **reach a number** (a target such as 2,100, with an optional unit). The form opens with
|
||||
examples to start from, held to the same bar as the event form's (F13, *Examples to start from*).
|
||||
|
||||
- **They roll on the game's clock.** A week starts at the game's daily reset on the chosen day, on
|
||||
the reader's server — the same clock every daily tick is cut on, so nothing new to get wrong.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# Examples to start from, and a pass over the add forms — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Give *Add an event* and *Add a task* a row of examples that shape the form, and make both
|
||||
forms read as a reader thinks.
|
||||
|
||||
**Architecture:** Examples are pure data in `src/shared/templates.ts`. Each form applies one through a
|
||||
pure function on its state, so the suite (static render, no clicks) can prove what a tap does. The
|
||||
event form's derived state moves into one exported pure function, `eventFormState`, so validity, the
|
||||
draft and the "still needed" line all come from one place. Shared field/pill classes, the example row
|
||||
and the "still needed" sentence live in `src/client/components/formParts.tsx`.
|
||||
|
||||
**Tech Stack:** Bun, React 19 (static render in tests via `react-dom/server`), Tailwind, zod.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-10-14-form-examples-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- An example sets a shape, never a fact: never a title (placeholder only), date, time, target or game. Only exception: *Monthly shop buy-out* sets `required: "1"`.
|
||||
- Nothing is saved until the reader saves; Save stays disabled until every date and figure the shape needs is typed.
|
||||
- No date is guessed — no "today", no "the 1st".
|
||||
- "I don't know when it ends" stays first-class; preset windows, measured cycles, delays, `comesRoundEarly`, the stranded notice and `until` survival behave exactly as before.
|
||||
- No schema, store, id or export change.
|
||||
- The examples row renders only on add (no `initial`), never on edit.
|
||||
- Copy: How often → *Once · Every day · Every week · Every month · On its own cycle*; Repeat → *Doesn't repeat · Comes back as it ends · Comes back after a wait*; Kind → *Banner · Story · Rerun · Challenge / endgame · Login rewards · Shop · Maintenance · Other*; the name field is *Name* in both forms.
|
||||
- Commits wait for the owner's go-ahead (AGENTS.md); work stays uncommitted until asked.
|
||||
|
||||
## Review Focus
|
||||
|
||||
1. A reader types a title and dates, then taps an example — the title and dates must survive (Task 1/2/3 tests on `withEventTemplate` / `withTaskTemplate`).
|
||||
2. Switching from *Recurring endgame cycle* to *Banner with a known end* must leave no repeat on the draft (Task 3 test).
|
||||
3. *A few days each week* leaves the day count empty: Save disabled and the line says "how many days" (Task 2 test).
|
||||
4. Choosing *On its own cycle* from *Doesn't repeat* must land on *Comes back as it ends*; a stored custom-never record must still reopen as never (Task 3 test).
|
||||
5. At 360px the example row and the pills wrap without horizontal scroll (Task 4 browser check).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: The examples, as data
|
||||
|
||||
**Files:**
|
||||
- Create: `src/shared/templates.ts`
|
||||
- Test: `test/templates.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `EventTemplate`, `TaskTemplate`, `EVENT_TEMPLATES`, `TASK_TEMPLATES` (shapes in the spec).
|
||||
|
||||
- [ ] **Step 1: Write the failing test** — ids unique per list; `sets` keys limited to the shape keys;
|
||||
no `sets` carries a title/date/target; only `monthly-shop` sets `required`, to `"1"` (and
|
||||
`few-days` to `""`); placeholders non-empty and none equals a real event name in the list (smoke).
|
||||
- [ ] **Step 2:** `bun test test/templates.test.ts` → FAIL (module not found).
|
||||
- [ ] **Step 3: Implement** `src/shared/templates.ts` with the two tables from the spec verbatim.
|
||||
- [ ] **Step 4:** `bun test test/templates.test.ts` → PASS.
|
||||
|
||||
### Task 2: Task form — shared parts, examples, still-needed
|
||||
|
||||
**Files:**
|
||||
- Create: `src/client/components/formParts.tsx` — `field`, `pill(on)`, `labelClass`, `stillNeededLine(needs: string[]): string | null`, `ExampleRow`.
|
||||
- Modify: `src/client/components/TaskForm.tsx`
|
||||
- Test: `test/custom-ui.test.tsx`, `test/templates.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `TASK_TEMPLATES`, `TaskTemplate`.
|
||||
- Produces:
|
||||
- `stillNeededLine(["a name","a start date"]) === "Still needed: a name and a start date."`; three → `"a, b and c"`; `[]` → `null`.
|
||||
- `ExampleRow<T extends {id:string;label:string;hint:string}>({ examples: readonly T[]; chosen: string | null; onChoose: (t: T) => void })` — renders "Start from an example", one pill per example with `aria-pressed`, and the chosen one's hint.
|
||||
- `withTaskTemplate(f: TaskFields, t: TaskTemplate): TaskFields` — `{...f, unit, kind, required?, amountUnit?}` only.
|
||||
- `taskStillNeeded(f: TaskFields): string[]` — in order "a name", "a day of the month", "how many days", "the target".
|
||||
|
||||
- [ ] **Step 1: Failing tests:**
|
||||
- `stillNeededLine` cases above.
|
||||
- `withTaskTemplate` keeps `game`, `title`, `target`, `weekday`, `monthDay`; sets `unit`/`kind`; *A few days each week* empties `required`.
|
||||
- `taskStillNeeded`: blank title → "a name"; `kind:"days", required:""` → "how many days"; `unit:"week", required:"9"` → "how many days"; amount with `target:""` → "the target"; complete → `[]`.
|
||||
- Every task template, applied to `{title:"x", target:"100", required kept or "2"}` fields, gives `taskStillNeeded === []` and a non-null `taskDraftFrom`.
|
||||
- Render: add form contains "Start from an example" and each label; edit form (`initial`) does not; label "Name" present.
|
||||
- [ ] **Step 2:** `bun test test/custom-ui.test.tsx test/templates.test.ts` → FAIL.
|
||||
- [ ] **Step 3: Implement** — formParts.tsx; TaskForm imports `field`/`pill`, labels "Name", inputs `px-3 py-2`, `chosen` state, `ExampleRow` only when `initial === undefined`, title placeholder from the chosen template, Save `disabled={draft === null || needs.length > 0}`, the still-needed line beside the buttons in `text-faint`.
|
||||
- [ ] **Step 4:** same command → PASS; `bun run typecheck` clean.
|
||||
|
||||
### Task 3: Event form — order, words, pills, messages, examples
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/client/components/CustomForms.tsx`
|
||||
- Test: `test/custom-ui.test.tsx`, `test/templates.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `EVENT_TEMPLATES`, `ExampleRow`, `pill`, `stillNeededLine`.
|
||||
- Produces:
|
||||
- `EventShape = { type: EventType; cadence: Cadence; repeatMode: RepeatMode; endKnown: boolean; ownCadence: boolean }`, held as one `useState`.
|
||||
- `withEventTemplate(shape: EventShape, t: EventTemplate): EventShape` → `{...shape, ...t.sets, ownCadence: false}`.
|
||||
- `withCadence(shape: EventShape, cadence: Cadence): EventShape` — choosing `custom` while `repeatMode === "never"` moves to `"forever"`.
|
||||
- `eventStillNeeded(s: { title; startDate; datedWindow; endKnown; endDate; delayNeedsEnd; needsCycle }): string[]` — "a name", "a start date", "an end date", "a cycle length".
|
||||
- `eventFormState(values: EventFormValues, existingUntil: string | null)` — returns `{ startsAt, endsAt, endMs, preset, datedWindow, measured, measuring, delayNeedsEnd, repeat, backwards, endMissing, earlyReturn, needs, valid, draft }`, the block that is inline today, moved verbatim, plus `needs`. `valid = needs.length === 0 && !backwards && !endMissing && !earlyReturn && !repeatIncomplete`.
|
||||
|
||||
- [ ] **Step 1: Failing tests:**
|
||||
- `withEventTemplate` keeps nothing it shouldn't touch (shape only) and clears `ownCadence`.
|
||||
- `withCadence(never-shape, "custom").repeatMode === "forever"`; a stored custom-never event reopens with *Doesn't repeat* pressed.
|
||||
- Endgame → Banner: `eventFormState` with the banner shape and dates has `repeat === null`.
|
||||
- Each event template, with title "x", start `2026-10-01`, end `2026-10-15`, is `valid` with a draft; a fresh banner with no dates lists "a name", "a start date", "an end date".
|
||||
- Copy: update existing assertions (`Cadence` → `How often`; answers list; `value="daily" selected=""` → `/aria-pressed="true"[^>]*>Every day</`; `with a delay` → `Comes back after a wait`).
|
||||
- Add form shows the example row; edit form does not.
|
||||
- [ ] **Step 2:** `bun test test/custom-ui.test.tsx test/templates.test.ts` → FAIL.
|
||||
- [ ] **Step 3: Implement** in the spec's order with messages beside their fields.
|
||||
- [ ] **Step 4:** same → PASS; full `bun test` and `bun run typecheck` green.
|
||||
|
||||
### Task 4: Browser check and docs
|
||||
|
||||
- [ ] Build (`bun run build:css && bun run build:js && bun run build:static`), serve on a fresh port, puppeteer (scratchpad) at 360px and 1280px: open Settings → Your own → Add an event, tap each example, check no horizontal scroll, fill the banner and save, see the row; same for Add a task.
|
||||
- [ ] PRD F13 and F17: a sentence each on examples and their bar. DESIGN.md chips: the example row. TODO.md: tick the item.
|
||||
- [ ] Final `bun test`, `bun run typecheck`.
|
||||
- [ ] Final whole-change review (fresh reviewer); then report. Changelog entry follows the feature commit when the owner asks to commit.
|
||||
@@ -0,0 +1,161 @@
|
||||
# Examples to start from, and a pass over the add forms — design
|
||||
|
||||
Date: 2026-10-14. Status: approved in conversation (approach A, both design sections).
|
||||
|
||||
## Problem
|
||||
|
||||
A reader who already knows what they want — "a banner that ends on the 28th", "1300 points each
|
||||
week" — cannot see which combination of fields in *Add an event* or *Add a task* produces it. The
|
||||
owner's words: "in the add event/task, its confusing to figure out how to create something you want.
|
||||
Having examples you can start from would be very useful."
|
||||
|
||||
Both forms also grew field by field (TODO.md), and read that way: enum values as copy, every note
|
||||
and error stacked at the bottom, and Save disabled with no reason given.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Each add form opens with a row of examples. One tap shapes the form for a common kind of entry;
|
||||
the reader fills in the name, dates and figures, then saves.
|
||||
- Both forms read as a reader thinks: plain words, choices in view, a message beside the field it is
|
||||
about, and Save that says what it still needs.
|
||||
- Nothing that is stored changes: no schema, store, id or export change.
|
||||
|
||||
## Non-negotiables
|
||||
|
||||
- **An example sets a shape, never a fact.** It may set kind, cadence, repeat mode, whether the end
|
||||
is known, the period unit, the measure kind and the unit word. It never sets a title (the title
|
||||
gets the example's text as a *placeholder* only), a date, a time, a target, or a game. The single
|
||||
exception is *Monthly shop buy-out*'s days-needed of 1, which is the shape "tick it off once", not
|
||||
a figure about any game.
|
||||
- **Nothing is saved until the reader saves.** Save stays disabled until every date and figure the
|
||||
shape needs has been typed, so nothing an example wrote can be saved unread.
|
||||
- **No date is guessed.** No "today", no "the 1st": games reset on different days, and a guessed
|
||||
date that looks deliberate is the failure this product exists to avoid.
|
||||
- **"I don't know when it ends" stays first-class**, and every existing rule of the event form
|
||||
(preset windows, measured cycles, delays, `comesRoundEarly`, the stranded-ticks notice, `until`
|
||||
surviving a save) behaves exactly as before.
|
||||
|
||||
## Part 1 — examples
|
||||
|
||||
### Data
|
||||
|
||||
`src/shared/templates.ts`, pure data with no React and no clock:
|
||||
|
||||
```ts
|
||||
export interface EventTemplate {
|
||||
id: string; // kebab-case, unique within the list
|
||||
label: string; // the chip text
|
||||
placeholder: string; // the title field's placeholder while this example is chosen
|
||||
hint: string; // one line: what is left for the reader
|
||||
sets: { type: EventType; cadence: Cadence; repeatMode: RepeatMode; endKnown: boolean };
|
||||
}
|
||||
|
||||
export interface TaskTemplate {
|
||||
id: string;
|
||||
label: string;
|
||||
placeholder: string;
|
||||
hint: string;
|
||||
sets: {
|
||||
unit: "week" | "month";
|
||||
kind: "days" | "amount";
|
||||
required?: string; // as typed; "" empties the field
|
||||
amountUnit?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const EVENT_TEMPLATES: readonly EventTemplate[];
|
||||
export const TASK_TEMPLATES: readonly TaskTemplate[];
|
||||
```
|
||||
|
||||
| Event example | type | cadence | repeatMode | endKnown | hint |
|
||||
|---|---|---|---|---|---|
|
||||
| Banner with a known end | banner | one-off | never | true | Fill in when it starts and when it ends. |
|
||||
| Login event | login | one-off | never | true | Fill in when it starts and when it ends, or tick that you don't know. |
|
||||
| Monthly shop reset | shop | monthly | never | true | Fill in a day it resets on. Each one runs until the next. |
|
||||
| Recurring endgame cycle | challenge | custom | forever | true | Fill in one cycle's start and end; how often it comes back is worked out from them. |
|
||||
| Maintenance | maintenance | one-off | never | true | Fill in when it starts and ends, with times. |
|
||||
|
||||
| Task example | unit | kind | required | amountUnit | hint |
|
||||
|---|---|---|---|---|---|
|
||||
| Weekly points target | week | amount | — | points | Fill in the target, and the day the week rolls over if it isn't Monday. |
|
||||
| A few days each week | week | days | "" | — | Fill in how many days a week it needs. |
|
||||
| Monthly shop buy-out | month | days | "1" | — | Fill in the day the month rolls over. |
|
||||
| Monthly target | month | amount | — | — | Fill in the target and what it counts. |
|
||||
|
||||
Placeholders are generic ("Limited banner", "Login bonus", "Shop reset", "Endgame cycle",
|
||||
"Maintenance", "Weekly points", "Weekly chore", "Monthly shop", "Monthly target"), never a real
|
||||
event's name.
|
||||
|
||||
### Behaviour
|
||||
|
||||
- The row ("Start from an example") renders only on add: `EventForm` and `TaskForm` without
|
||||
`initial`. Editing never shows it.
|
||||
- A tap applies `sets` and nothing else. Game, title, dates, times, note, target, weekday and month
|
||||
day keep whatever the reader typed. On the event form it also clears `ownCadence`, so a measured
|
||||
cycle is shown rather than a stale hand-stated one.
|
||||
- The last example tapped is pressed (`aria-pressed="true"`); its `placeholder` replaces the title
|
||||
placeholder and its `hint` shows under the row. Editing a field afterwards does not unpress it —
|
||||
it is where the reader started, not a lock.
|
||||
- Applying a shape is a pure function per form (`withEventTemplate`, `withTaskTemplate`) so it is
|
||||
provable without a click: the suite renders statically and cannot click.
|
||||
|
||||
## Part 2 — the pass
|
||||
|
||||
### EventForm
|
||||
|
||||
1. **Order:** Game → Name → How often → Starts → Ends → repeat details → Kind → Note.
|
||||
2. **Words:**
|
||||
- How often (was *Cadence*): *Once · Every day · Every week · Every month · On its own cycle*.
|
||||
- Repeat: *Doesn't repeat · Comes back as it ends · Comes back after a wait*.
|
||||
- Kind: *Banner · Story · Rerun · Challenge / endgame · Login rewards · Shop · Maintenance ·
|
||||
Other*. Stored values are unchanged.
|
||||
- Name (was *What is it*).
|
||||
3. **Pills, not selects, for How often and Repeat**, in the task form's pill style, wrapping at phone
|
||||
width. *Comes back after a wait* is a disabled pill while there is no end to measure from, exactly
|
||||
as the option is disabled today. Choosing *On its own cycle* while the repeat is *Doesn't repeat*
|
||||
moves it to *Comes back as it ends*, because a cycle that never comes back is a contradiction on
|
||||
screen; a stored custom-never record still reopens as it is.
|
||||
4. **Messages beside their field:** the no-time note under Starts; *ends before it starts* and *isn't
|
||||
a real date* under Ends; the unknown-end note under its checkbox; *comes round before it ends*
|
||||
under the repeat details.
|
||||
|
||||
### Both forms
|
||||
|
||||
5. **Save says why it is disabled.** A muted line beside the buttons: "Still needed: a name and a
|
||||
start date." It comes from a pure `eventStillNeeded` / `taskStillNeeded`, and Save's disabled
|
||||
state is derived from the same function, so the line and the button cannot disagree. Errors
|
||||
(backwards dates, comes round early, an unstatable wait) stay where they are, beside their field,
|
||||
in critical.
|
||||
- Event needs, in order: a name; a start date; an end date (a dated window whose end is stated
|
||||
as known but is empty, or a wait with no end); a cycle length (comes back as it ends, not
|
||||
measured, no valid length); a wait (comes back after a wait, no valid length).
|
||||
- Task needs: a name; a day of the month (monthly, unreadable day); how many days (tick it off,
|
||||
not a whole number from 1 to 7 a week or 31 a month); the target (reach a number, unreadable),
|
||||
or a target of at least 1 / a smaller target (outside `AmountTarget`'s 1–1,000,000,000).
|
||||
6. **One field and pill style.** Both forms use the name *Name* and DESIGN.md's input spec (8px
|
||||
corners, 8×12 padding, ground fill, hairline border, faint on focus). The pill and field classes
|
||||
live once in `src/client/components/formParts.tsx`, with the example row and the still-needed line.
|
||||
7. **Phone width.** Checked in a browser at 360px and desktop: no horizontal scroll, and every
|
||||
control usable.
|
||||
|
||||
### Out of scope
|
||||
|
||||
The entry point (Settings → Your own), `GameForm`, and the commit-button padding mismatch with
|
||||
DESIGN.md (app-wide; noted, not fixed here).
|
||||
|
||||
## Testing
|
||||
|
||||
- `test/templates.test.ts`: ids are unique; no template sets a title, date or target; every event
|
||||
template, given a name and dates, yields a valid draft through the same functions the form uses;
|
||||
every task template, given a name and a target or day count, yields a `taskDraftFrom` draft.
|
||||
- `test/custom-ui.test.tsx`: the row renders on add and not on edit; pill copy and pressed states
|
||||
replace the `selected` option assertions; `eventStillNeeded` and `taskStillNeeded` cases; Save is
|
||||
disabled exactly when the list is non-empty or an error stands.
|
||||
- Browser check (puppeteer, scratchpad only): tap each example, fill it, save, see the row; 360px
|
||||
width with no horizontal scroll.
|
||||
|
||||
## Docs
|
||||
|
||||
PRD F13 and F17 gain a sentence on the examples and the bar they are held to; DESIGN.md's chips
|
||||
section names the example row; TODO.md item ticked; a changelog entry (reader-visible) in a
|
||||
follow-up `chore(changelog)` commit.
|
||||
Reference in new issue
Block a user