Skip to content

Commit 2fb682a

Browse files
committed
Add sticky note color palette support
1 parent c9550bd commit 2fb682a

19 files changed

Lines changed: 472 additions & 108 deletions

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ import {
5353

5454
The `method-engine` export is intended for reusable APIOps workflow logic. It gives CLIs, AI agents, apps, and APIs the same station recommendation, resource lookup, and canvas generation behavior without reimplementing the method rules.
5555

56+
Sticky note authoring should uses the shared palette exposed by the method engine:
57+
58+
- `benefit`: `#C0EB6A`
59+
- `neutral`: `#DFDDC5`
60+
- `negative`: `#FFAFAF`
61+
- `task`: `#7DC9E7`
62+
- `default`: `#FFF399`
63+
64+
Prefer section-appropriate defaults when the intent is obvious from the canvas structure.
65+
If a note does not fit a clear intent yet, use the generic default rather than guessing.
66+
When using the interactive CLI, explicit note tags such as `[benefit]`, `[task]`, or `[color=#7DC9E7]` are the supported way to override the section default.
67+
5668
Validate the files locally with:
5769

5870
```bash

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"./method/station-criteria.json": "./src/data/method/station-criteria.json"
2727
},
2828
"scripts": {
29-
"test": "node scripts/validate.mjs && node scripts/test-print-method-snippet.mjs",
29+
"test": "node scripts/validate.mjs && node scripts/test-note-colors.mjs && node scripts/test-print-method-snippet.mjs",
3030
"release:create-apiops:pack": "npm pack --workspace packages/create-apiops",
3131
"release:create-apiops:publish": "npm publish --workspace packages/create-apiops --access public",
3232
"check:packaging:skills": "node scripts/check-packaging-skills.mjs",

packages/create-apiops/bin/method-cli.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ function loadCanvasJson(filePath, canvasId, locale) {
9090
return methodEngine.buildCanvasTemplate(canvasId, locale);
9191
}
9292

93+
function formatPaletteHelp() {
94+
return Object.entries(methodEngine.getNoteColorPalette())
95+
.map(([intent, color]) => `${intent}=${color}`)
96+
.join(", ");
97+
}
98+
9399
async function fillCanvasSectionsInteractive(stationId, step, locale, output, rl) {
94100
const outPath = methodEngine.generateCanvasForStationResource(stationId, step.resourceId, locale, output);
95101
const canvasJson = loadCanvasJson(outPath, step.canvasId, locale);
@@ -105,14 +111,17 @@ async function fillCanvasSectionsInteractive(stationId, step, locale, output, rl
105111
console.log("");
106112
console.log(`Filling ${canvasMetadata.title} section by section.`);
107113
console.log("Use `|` to create multiple notes in one section. Leave blank to keep existing notes. Type !clear to remove notes in a section.\n");
114+
console.log("Prefix any note with `[benefit]`, `[neutral]`, `[negative]`, `[task]`, `[default]`, or `[color=#RRGGBB]` to set its color explicitly.");
115+
console.log(`Palette: ${formatPaletteHelp()}\n`);
108116

109117
for (const section of canvasMetadata.sections) {
110118
const existingSection = canvasJson.sections.find((entry) => entry.sectionId === section.id);
111119
const existingNotes = existingSection?.stickyNotes || [];
112120
console.log(`${section.title}`);
113121
console.log(section.description);
122+
console.log(`Default note color here: ${section.defaultNoteIntent} (${section.defaultNoteColor})`);
114123
if (existingNotes.length > 0) {
115-
console.log(`Current notes: ${existingNotes.map((note) => note.content).join(" | ")}`);
124+
console.log(`Current notes: ${existingNotes.map((note) => `${note.content} (${note.color})`).join(" | ")}`);
116125
}
117126

118127
const answer = (await rl.question("Notes: ")).trim();
@@ -131,10 +140,10 @@ async function fillCanvasSectionsInteractive(stationId, step, locale, output, rl
131140
.split("|")
132141
.map((entry) => entry.trim())
133142
.filter(Boolean)
134-
.map((content) => ({
135-
content,
136-
size: methodEngine.DEFAULT_NOTE_SIZE,
137-
color: methodEngine.DEFAULT_NOTE_COLOR
143+
.map((content) => methodEngine.parseStickyNoteInput(content, {
144+
canvasId: step.canvasId,
145+
sectionId: section.id,
146+
defaultIntent: section.defaultNoteIntent
138147
}));
139148

140149
existingSection.stickyNotes = notes;

scripts/test-note-colors.mjs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import assert from "node:assert/strict";
2+
import fs from "node:fs";
3+
import path from "node:path";
4+
import * as methodEngine from "../src/lib/method-engine.js";
5+
6+
const expectedPalette = {
7+
benefit: "#C0EB6A",
8+
neutral: "#DFDDC5",
9+
negative: "#FFAFAF",
10+
task: "#7DC9E7",
11+
default: "#FFF399"
12+
};
13+
14+
assert.deepEqual(methodEngine.getNoteColorPalette(), expectedPalette);
15+
assert.equal(methodEngine.DEFAULT_NOTE_COLOR, expectedPalette.default);
16+
17+
assert.equal(
18+
methodEngine.getDefaultNoteColorForSection("customerJourneyCanvas", "journeySteps"),
19+
expectedPalette.task
20+
);
21+
assert.equal(
22+
methodEngine.getDefaultNoteColorForSection("customerJourneyCanvas", "gains"),
23+
expectedPalette.benefit
24+
);
25+
assert.equal(
26+
methodEngine.getDefaultNoteColorForSection("businessImpactCanvas", "availabilityRisks"),
27+
expectedPalette.negative
28+
);
29+
30+
assert.deepEqual(
31+
methodEngine.parseStickyNoteInput("Map the approval workflow", {
32+
canvasId: "customerJourneyCanvas",
33+
sectionId: "journeySteps"
34+
}),
35+
{
36+
content: "Map the approval workflow",
37+
size: methodEngine.DEFAULT_NOTE_SIZE,
38+
color: expectedPalette.task
39+
}
40+
);
41+
42+
assert.deepEqual(
43+
methodEngine.parseStickyNoteInput("[benefit] Faster onboarding", {
44+
canvasId: "customerJourneyCanvas",
45+
sectionId: "pains"
46+
}),
47+
{
48+
content: "Faster onboarding",
49+
size: methodEngine.DEFAULT_NOTE_SIZE,
50+
color: expectedPalette.benefit
51+
}
52+
);
53+
54+
assert.deepEqual(
55+
methodEngine.parseStickyNoteInput("[color=#7dc9e7] Capture order event", {
56+
canvasId: "eventCanvas",
57+
sectionId: "processingLogic"
58+
}),
59+
{
60+
content: "Capture order event",
61+
size: methodEngine.DEFAULT_NOTE_SIZE,
62+
color: expectedPalette.task
63+
}
64+
);
65+
66+
assert.throws(
67+
() => methodEngine.parseStickyNoteInput("[mystery] Unknown tag", {
68+
canvasId: "domainCanvas",
69+
sectionId: "coreEntitiesAndBusinessMeaning"
70+
}),
71+
/Unknown sticky note tag/
72+
);
73+
74+
const templateDir = path.join(process.cwd(), "src", "data", "canvas", "import-export-templates");
75+
const allowedColors = new Set(Object.values(expectedPalette));
76+
77+
for (const entry of fs.readdirSync(templateDir)) {
78+
if (!entry.endsWith(".json")) {
79+
continue;
80+
}
81+
82+
const filePath = path.join(templateDir, entry);
83+
const json = JSON.parse(fs.readFileSync(filePath, "utf8"));
84+
85+
for (const section of json.sections || []) {
86+
for (const note of section.stickyNotes || []) {
87+
assert.ok(
88+
allowedColors.has(note.color),
89+
`${entry} contains a sticky note color outside the supported palette: ${note.color}`
90+
);
91+
}
92+
}
93+
}
94+
95+
console.log("Sticky note color palette checks passed.");

skills/new-api-guide/SKILL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ Typical engine entry points include:
3838

3939
When the engine is available, use it as the first choice and treat direct JSON reads as implementation detail or fallback.
4040

41+
## Sticky note palette
42+
43+
When authoring or reviewing canvas sticky notes, use the shared palette from `src/lib/method-engine.js` consistently across guidance, generated JSON, and examples.
44+
45+
- `benefit` for benefits, gains, new or positive things: `#C0EB6A`
46+
- `neutral` for neutral or technical notes: `#DFDDC5`
47+
- `negative` for cons, risks, pains, or negative notes: `#FFAFAF`
48+
- `task` for tasks, actions, or journey steps: `#7DC9E7`
49+
- `default` for generic uncategorized notes: `#FFF399`
50+
51+
Prefer section-appropriate defaults when the intent is obvious from the canvas structure.
52+
If a note does not fit a clear intent yet, use the generic default rather than guessing.
53+
When using the interactive CLI, explicit note tags such as `[benefit]`, `[task]`, or `[color=#7DC9E7]` are the supported way to override the section default.
54+
4155
## Built-in helper scripts
4256

4357
Use the local helper scripts in `skills/new-api-guide/scripts/` as fallback and debugging tools when the method engine is unavailable or when you need to inspect one slice of method data quickly.

src/data/canvas/import-export-templates/Canvas_apiBusinessModelCanvas.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{
1515
"content": "Placeholder",
1616
"size": 80,
17-
"color": "#FFF399"
17+
"color": "#DFDDC5"
1818
}
1919
]
2020
},
@@ -24,7 +24,7 @@
2424
{
2525
"content": "Placeholder",
2626
"size": 80,
27-
"color": "#FFF399"
27+
"color": "#7DC9E7"
2828
}
2929
]
3030
},
@@ -34,7 +34,7 @@
3434
{
3535
"content": "Placeholder",
3636
"size": 80,
37-
"color": "#FFF399"
37+
"color": "#DFDDC5"
3838
}
3939
]
4040
},
@@ -44,7 +44,7 @@
4444
{
4545
"content": "Placeholder",
4646
"size": 80,
47-
"color": "#FFF399"
47+
"color": "#C0EB6A"
4848
}
4949
]
5050
},
@@ -54,7 +54,7 @@
5454
{
5555
"content": "Placeholder",
5656
"size": 80,
57-
"color": "#FFF399"
57+
"color": "#DFDDC5"
5858
}
5959
]
6060
},
@@ -64,7 +64,7 @@
6464
{
6565
"content": "Placeholder",
6666
"size": 80,
67-
"color": "#FFF399"
67+
"color": "#7DC9E7"
6868
}
6969
]
7070
},
@@ -74,7 +74,7 @@
7474
{
7575
"content": "Placeholder",
7676
"size": 80,
77-
"color": "#FFF399"
77+
"color": "#DFDDC5"
7878
}
7979
]
8080
},
@@ -84,7 +84,7 @@
8484
{
8585
"content": "Placeholder",
8686
"size": 80,
87-
"color": "#FFF399"
87+
"color": "#FFAFAF"
8888
}
8989
]
9090
},
@@ -94,9 +94,9 @@
9494
{
9595
"content": "Placeholder",
9696
"size": 80,
97-
"color": "#FFF399"
97+
"color": "#C0EB6A"
9898
}
9999
]
100100
}
101101
]
102-
}
102+
}

src/data/canvas/import-export-templates/Canvas_apiValuePropositionCanvas.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{
1515
"content": "Placeholder",
1616
"size": 80,
17-
"color": "#FFF399"
17+
"color": "#7DC9E7"
1818
}
1919
]
2020
},
@@ -24,7 +24,7 @@
2424
{
2525
"content": "Placeholder",
2626
"size": 80,
27-
"color": "#FFF399"
27+
"color": "#C0EB6A"
2828
}
2929
]
3030
},
@@ -34,7 +34,7 @@
3434
{
3535
"content": "Placeholder",
3636
"size": 80,
37-
"color": "#FFF399"
37+
"color": "#C0EB6A"
3838
}
3939
]
4040
},
@@ -44,9 +44,9 @@
4444
{
4545
"content": "Placeholder",
4646
"size": 80,
47-
"color": "#FFF399"
47+
"color": "#DFDDC5"
4848
}
4949
]
5050
}
5151
]
52-
}
52+
}

src/data/canvas/import-export-templates/Canvas_businessImpactCanvas.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{
1515
"content": "Placeholder",
1616
"size": 80,
17-
"color": "#FFF399"
17+
"color": "#FFAFAF"
1818
}
1919
]
2020
},
@@ -24,7 +24,7 @@
2424
{
2525
"content": "Placeholder",
2626
"size": 80,
27-
"color": "#FFF399"
27+
"color": "#FFAFAF"
2828
}
2929
]
3030
},
@@ -34,7 +34,7 @@
3434
{
3535
"content": "Placeholder",
3636
"size": 80,
37-
"color": "#FFF399"
37+
"color": "#FFAFAF"
3838
}
3939
]
4040
},
@@ -44,7 +44,7 @@
4444
{
4545
"content": "Placeholder",
4646
"size": 80,
47-
"color": "#FFF399"
47+
"color": "#7DC9E7"
4848
}
4949
]
5050
},
@@ -54,7 +54,7 @@
5454
{
5555
"content": "Placeholder",
5656
"size": 80,
57-
"color": "#FFF399"
57+
"color": "#7DC9E7"
5858
}
5959
]
6060
},
@@ -64,9 +64,9 @@
6464
{
6565
"content": "Placeholder",
6666
"size": 80,
67-
"color": "#FFF399"
67+
"color": "#7DC9E7"
6868
}
6969
]
7070
}
7171
]
72-
}
72+
}

0 commit comments

Comments
 (0)