Skip to content

Commit 38d2cae

Browse files
committed
Change the release advance model - breaking change in syntax
1 parent 48dbb2d commit 38d2cae

File tree

11 files changed

+488
-305
lines changed

11 files changed

+488
-305
lines changed

README.md

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Generate user story maps from markdown. Write your product spec as a readable
44
document — personas, releases, activities, tasks, and stories — and render it
5-
as a styled HTML page.
5+
as a styled, interactive HTML page.
66

77
## Installation
88

@@ -19,6 +19,9 @@ storymap render mymap.md # → mymap.html (same directory)
1919
storymap render mymap.md -o out # → out/mymap.html
2020
```
2121

22+
The output is a single self-contained HTML file. Local images referenced in
23+
story descriptions are embedded as base64 data URIs — no external files needed.
24+
2225
**PDF output:** open the generated HTML in a browser and use print-to-PDF
2326
(Ctrl+P → Save as PDF). The HTML includes print-optimised CSS for landscape
2427
layout and color preservation.
@@ -27,7 +30,7 @@ layout and color preservation.
2730

2831
A storymap file is a standard markdown document with three reserved top-level
2932
sections: `# Releases`, `# Personas`, and `# Map`. Any other `#` heading is
30-
treated as the document title (first one) or passed through to the output.
33+
treated as the document title (first one) or ignored.
3134

3235
```markdown
3336
# My Product
@@ -50,19 +53,15 @@ Margie manages a team of 8 and primarily uses the app on mobile.
5053
# Map
5154
## User Management
5255
### Authentication
53-
#### Sign in [status:: done] [persona:: Margie the Manager]
56+
#### Sign in [status:: done] [persona:: Margie the Manager] [release:: MVP]
5457
User can log in with email and password.
5558

56-
> release Beta
57-
58-
#### Password reset [status:: in-progress] [deadline:: 2026-03-01]
59+
#### Password reset [status:: in-progress] [deadline:: 2026-03-01] [release:: Beta]
5960

6061
### Profile
61-
#### Edit profile [status:: done]
62-
63-
> release Beta
62+
#### Edit profile [status:: done] [release:: MVP]
6463

65-
#### Upload avatar [status:: blocked]
64+
#### Upload avatar [status:: blocked] [release:: Beta]
6665
Blocked pending storage provider decision.
6766
```
6867

@@ -84,34 +83,36 @@ Section names are case-insensitive.
8483
#### Story (card in a swimlane)
8584
```
8685

87-
Use `> release` on its own line to advance to the next release swimlane within
88-
a task. Annotate it for readability — anything after `release` is ignored:
86+
### Assigning stories to releases
87+
88+
Use the `[release:: name]` field on each story to assign it to a release
89+
swimlane. The name must match a release defined in the `# Releases` section.
90+
Stories without a `[release::]` field are parsed but not shown in any swimlane.
8991

9092
```markdown
9193
### Authentication
92-
#### Sign in ← Release 1 (MVP)
93-
#### Remember me ← Release 1 (MVP)
94-
> release Beta
95-
#### SSO ← Release 2 (Beta)
96-
> release GA
97-
← Release 3 empty for this task
94+
#### Sign in [status:: done] [release:: MVP]
95+
#### Remember me [status:: done] [release:: MVP]
96+
#### SSO [status:: not-started] [release:: Beta]
97+
#### SAML [status:: not-started] [release:: GA]
9898
```
9999

100-
Keep `> release` count consistent across all tasks — mismatched counts produce
101-
misaligned swimlane rows.
100+
This is more explicit than positional separators — each story carries its own
101+
release assignment regardless of order in the file.
102102

103103
### Story fields
104104

105105
Stories support optional inline fields using `[key:: value]` syntax.
106106
Fields appear as badges on the rendered story card.
107107

108108
```markdown
109-
#### Story name [status:: done] [persona:: Margie the Manager] [deadline:: 2026-03-01]
109+
#### Story name [status:: done] [persona:: Margie the Manager] [deadline:: 2026-03-01] [release:: MVP]
110110
```
111111

112112
| Field | Values | Default |
113113
|---|---|---|
114114
| `status` | `not-started`, `in-progress`, `done`, `blocked` | `not-started` |
115+
| `release` | Release name from `# Releases` section ||
115116
| `persona` | Any string matching a persona name ||
116117
| `deadline` | ISO date `YYYY-MM-DD` ||
117118

@@ -120,8 +121,45 @@ Any other `[key:: value]` field is accepted and rendered as a badge.
120121
### Story descriptions
121122

122123
Markdown content following a `#### Story` heading and before the next heading
123-
or `> release` separator is treated as the story description. Descriptions
124-
support standard markdown: bold, italics, links, lists.
124+
is treated as the story description. Descriptions support standard markdown:
125+
bold, italics, links, lists, and images.
126+
127+
The first paragraph is always visible on the story card. Additional paragraphs
128+
(separated by a blank line) are shown only in Detail zoom level.
129+
130+
```markdown
131+
#### Sign in [status:: done] [release:: MVP]
132+
User can log in with email and password.
133+
134+
**Acceptance criteria:**
135+
- Given valid credentials, user is redirected to dashboard
136+
- Given invalid credentials, an error message is shown
137+
138+
![wireframe](./screens/sign-in.png)
139+
```
140+
141+
### Images
142+
143+
Images in story descriptions and persona descriptions are embedded as base64
144+
data URIs in the output HTML, making the file fully self-contained. Paths are
145+
resolved relative to the source `.md` file. Remote URLs are left as-is.
146+
147+
## Interactive HTML features
148+
149+
The rendered HTML includes controls for navigating large maps:
150+
151+
**Zoom levels** — three buttons in the sticky header:
152+
- **Overview** — story names only, compact layout
153+
- **Map** — story names, status badges, and first-paragraph descriptions
154+
- **Detail** — full descriptions and acceptance criteria expanded
155+
156+
**Release focus** — a dropdown to highlight one release swimlane and dim
157+
the rest. Works independently of the zoom level.
158+
159+
**Story Lens** — a toggle that enables click-to-zoom on individual story cards.
160+
When active, clicking a card expands it to ~40% of the viewport width with full
161+
details visible. Clicking outside collapses it. Stories in the focused release
162+
(if any) are the only ones that can be expanded.
125163

126164
## CLI reference
127165

@@ -197,6 +235,8 @@ The template receives:
197235
| `status_colors` | `dict[str, str]` | Resolved status → hex color |
198236
| `ui_colors` | `dict[str, str]` | Resolved UI element → hex color |
199237
| `render_md` | `callable` | Render a markdown string to HTML |
238+
| `render_md_intro` | `callable` | Render first paragraph only |
239+
| `render_md_rest` | `callable` | Render everything after first paragraph |
200240

201241
The `darken` filter is also available: `{{ color | darken }}`.
202242

@@ -217,7 +257,7 @@ just test
217257
storymap/
218258
├── model.py — dataclasses and default color constants
219259
├── parser.py — markdown-it-py state machine parser
220-
├── renderer.py — Jinja2 HTML renderer
260+
├── renderer.py — Jinja2 HTML renderer with base64 image embedding
221261
├── cli.py — click CLI entry point
222262
└── templates/
223263
└── default.html.j2

sample.md

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,18 @@ New user can register with email and password.
8989
Includes email verification flow.
9090
See [issue #12](https://github.com/org/repo/issues/12)
9191

92-
> release MVP
9392

94-
#### Register via SSO [status:: in-progress] [persona:: Dave the Developer] [deadline:: 2026-04-15]
93+
#### Register via SSO [status:: in-progress] [persona:: Dave the Developer] [deadline:: 2026-04-15] [release:: MVP]
9594
Support Google and Microsoft OAuth providers.
9695
See [issue #34](https://github.com/org/repo/issues/34)
9796

98-
> release Beta
9997

100-
#### Register via SAML [status:: not-started] [persona:: Margie the Manager] [deadline:: 2026-09-01]
98+
#### Register via SAML [status:: not-started] [persona:: Margie the Manager] [deadline:: 2026-09-01] [release:: Beta]
10199
Enterprise SAML 2.0 support for large customers.
102100

103101
### Authentication
104102

105-
#### Sign in [status:: done]
103+
#### Sign in [status:: done] [release:: Beta]
106104
Standard email/password login with remember-me option.
107105

108106
**Acceptance criteria:**
@@ -111,20 +109,17 @@ Standard email/password login with remember-me option.
111109
- Given "remember me" checked, session persists for 30 days
112110
- Given 5 failed attempts, account is temporarily locked
113111

114-
> release GA
115112

116-
#### Forgot password [status:: done]
113+
#### Forgot password [status:: done] [release:: GA]
117114
Password reset via email token.
118115
See [issue #15](https://github.com/org/repo/issues/15)
119116

120-
> release MVP
121117

122-
#### MFA via TOTP [status:: in-progress] [deadline:: 2026-04-30]
118+
#### MFA via TOTP [status:: in-progress] [deadline:: 2026-04-30] [release:: MVP]
123119
Time-based one-time password support (Google Authenticator, Authy).
124120

125-
> release Beta
126121

127-
#### MFA via SMS [status:: not-started] [deadline:: 2026-09-01]
122+
#### MFA via SMS [status:: not-started] [deadline:: 2026-09-01] [release:: Beta]
128123
SMS-based one-time password as an alternative to TOTP.
129124

130125
**Acceptance criteria:**
@@ -135,83 +130,73 @@ SMS-based one-time password as an alternative to TOTP.
135130

136131
### Profile
137132

138-
#### View and edit profile [status:: done] [persona:: Nina the New User]
133+
#### View and edit profile [status:: done] [persona:: Nina the New User] [release:: Beta]
139134
User can view and update their display name, email, and bio.
140135

141136
**Acceptance criteria:**
142137
- Changes are saved on submit with a success toast
143138
- Email change triggers a re-verification flow
144139
- Invalid fields show inline validation errors
145140

146-
> release GA
147141

148-
#### Upload avatar [status:: blocked] [persona:: Nina the New User]
142+
#### Upload avatar [status:: blocked] [persona:: Nina the New User] [release:: GA]
149143
Blocked pending storage provider decision.
150144
See [JIRA-456](https://jira.example.com/browse/JIRA-456)
151145

152-
> release MVP
153146

154-
#### Delete account [status:: not-started] [deadline:: 2026-09-01]
147+
#### Delete account [status:: not-started] [deadline:: 2026-09-01] [release:: MVP]
155148
Must comply with GDPR right-to-erasure requirements.
156149
See [issue #78](https://github.com/org/repo/issues/78)
157150

158151
## Reporting
159152

160153
### Dashboard
161154

162-
#### View project summary [status:: done] [persona:: Margie the Manager]
155+
#### View project summary [status:: done] [persona:: Margie the Manager] [release:: MVP]
163156
High-level status cards showing release progress.
164157

165-
> release Beta
166158

167-
#### Filter by release [status:: in-progress] [persona:: Margie the Manager] [deadline:: 2026-04-15]
159+
#### Filter by release [status:: in-progress] [persona:: Margie the Manager] [deadline:: 2026-04-15] [release:: Beta]
168160

169-
> release GA
170161

171-
#### Filter by persona [status:: not-started] [deadline:: 2026-06-01]
162+
#### Filter by persona [status:: not-started] [deadline:: 2026-06-01] [release:: GA]
172163

173-
#### Filter by status [status:: not-started] [deadline:: 2026-06-01]
164+
#### Filter by status [status:: not-started] [deadline:: 2026-06-01] [release:: GA]
174165

175166
### Export
176167

177-
#### Export to PDF [status:: in-progress] [persona:: Margie the Manager] [deadline:: 2026-04-30]
168+
#### Export to PDF [status:: in-progress] [persona:: Margie the Manager] [deadline:: 2026-04-30] [release:: GA]
178169
One-click PDF export of the current story map view.
179170

180-
> release MVP
181171

182-
#### Export to CSV [status:: not-started] [persona:: Dave the Developer] [deadline:: 2026-06-01]
172+
#### Export to CSV [status:: not-started] [persona:: Dave the Developer] [deadline:: 2026-06-01] [release:: MVP]
183173
Machine-readable export for backlog import into Jira or GitHub.
184174

185-
> release Beta
186175

187-
#### Export to PNG [status:: not-started] [deadline:: 2026-09-01]
176+
#### Export to PNG [status:: not-started] [deadline:: 2026-09-01] [release:: Beta]
188177

189178
## Notifications
190179

191180
### Email Notifications
192181

193-
#### Notify on story status change [status:: not-started] [persona:: Margie the Manager]
182+
#### Notify on story status change [status:: not-started] [persona:: Margie the Manager] [release:: Beta]
194183

195-
> release GA
196184

197-
#### Daily digest [status:: not-started] [persona:: Margie the Manager] [deadline:: 2026-06-01]
185+
#### Daily digest [status:: not-started] [persona:: Margie the Manager] [deadline:: 2026-06-01] [release:: GA]
198186
Optional daily summary email of changes since last digest.
199187

200-
> release MVP
201188

202-
#### Notify on blocked stories [status:: not-started] [deadline:: 2026-09-01]
189+
#### Notify on blocked stories [status:: not-started] [deadline:: 2026-09-01] [release:: MVP]
203190
Alert assigned persona when a story is marked as blocked.
204191

205192
### In-app Notifications
206193

207-
#### Show notification badge [status:: blocked]
194+
#### Show notification badge [status:: blocked] [release:: MVP]
208195
Blocked by front-end architecture decision on state management.
209196
See [issue #99](https://github.com/org/repo/issues/99)
210197

211-
> release Beta
212198

213-
#### Mark notifications as read [status:: not-started] [deadline:: 2026-06-01]
199+
#### Mark notifications as read [status:: not-started] [deadline:: 2026-06-01] [release:: Beta]
214200

215-
> release GA
216201

217-
#### Notification preferences [status:: not-started] [deadline:: 2026-09-01]
202+
#### Notification preferences [status:: not-started] [deadline:: 2026-09-01] [release:: GA]

storymap/cli.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@
3737
### Task — column
3838
#### Story [key:: val] — card; supported fields:
3939
[status:: not-started | in-progress | done | blocked]
40+
[release:: Release Name] ← assigns story to a swimlane
4041
[persona:: Persona Name]
4142
[deadline:: YYYY-MM-DD]
4243
43-
Use > release on its own line to advance to the next release swimlane.
44-
You can annotate it for readability — the text after "release" is ignored:
45-
> release Beta
46-
> release end of sprint 3
44+
Each story is assigned to a release swimlane via [release:: name].
45+
The name must match a release defined in # Releases.
46+
Stories without a [release::] field are parsed but not shown in any swimlane.
4747
-->
4848
4949
# My Product
@@ -75,17 +75,15 @@
7575
7676
### Main Task
7777
78-
#### First story [status:: done] [persona:: Alice the User]
78+
#### First story [status:: done] [persona:: Alice the User] [release:: MVP]
7979
Describe what this story delivers.
8080
81-
> release Beta
82-
83-
#### Second story [status:: in-progress] [deadline:: 2026-06-01]
81+
#### Second story [status:: in-progress] [deadline:: 2026-06-01] [release:: Beta]
8482
Describe what this story delivers.
8583
8684
### Another Task
8785
88-
#### A story [status:: not-started]
86+
#### A story [status:: not-started] [release:: MVP]
8987
"""
9088

9189

@@ -194,6 +192,7 @@ def render(
194192
template_path=template_path,
195193
status_colors=status_colors or None,
196194
ui_colors=ui_colors or None,
195+
source_dir=input_file.parent,
197196
)
198197
except Exception as e:
199198
raise click.ClickException(f"Failed to render: {e}")

0 commit comments

Comments
 (0)