@tanishqkancharla/diffmap 0.6.0 → 0.6.1
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/README.md +5 -5
- package/package.json +1 -1
- package/src/host/home.md +47 -60
- package/src/host/home.test.ts +11 -0
- package/src/services.ts +2 -2
- package/src/viewer/CallStackDiff.tsx +1 -3
- package/src/viewer/CodeBlock.tsx +0 -1
- package/src/viewer/ServiceDiagramBlock.tsx +3 -2
- package/src/viewer/ServiceStage.tsx +8 -3
- package/src/viewer/notes.tsx +0 -1
package/README.md
CHANGED
|
@@ -258,7 +258,7 @@ service API "HTTP server" [[src/api.ts]]
|
|
|
258
258
|
> Failures since the last success. Five locks the account.
|
|
259
259
|
|
|
260
260
|
start "Alice logs in"
|
|
261
|
-
>
|
|
261
|
+
> The API checks her password with Auth, then sets a session cookie.
|
|
262
262
|
step "The browser submits the form"
|
|
263
263
|
Browser -> API: POST /login
|
|
264
264
|
step "The API asks Auth to verify"
|
|
@@ -279,13 +279,13 @@ step "The API sets the session cookie"
|
|
|
279
279
|
- `start "caption"` at column 0, before the first step, captions the whole
|
|
280
280
|
diagram, such as `start "Alice logs in"`. It shows above the diagram until
|
|
281
281
|
the reader steps, and each step's caption replaces it. A `>` line under it
|
|
282
|
-
|
|
283
|
-
the start caption is its title.
|
|
282
|
+
gives an overview of the whole process. A `services` fence takes no
|
|
283
|
+
`[Title]`; the start caption is its title.
|
|
284
284
|
- `step "caption"` starts a step at column 0. The caption is required: a
|
|
285
285
|
sentence that says what happens, shown under the controls. Indented lines
|
|
286
286
|
under it are `From -> To: label` events and `Service.key = value` updates.
|
|
287
|
-
|
|
288
|
-
|
|
287
|
+
A step's events travel one after another, in the order written, and the
|
|
288
|
+
updates land when the last one arrives.
|
|
289
289
|
- A `>` line adds text to the line above it. Indent it further than that line;
|
|
290
290
|
consecutive `>` lines join.
|
|
291
291
|
- Under a service or state, it is a description: the name gets a dotted
|
package/package.json
CHANGED
package/src/host/home.md
CHANGED
|
@@ -30,70 +30,57 @@ Your agent selects the best diagram for each idea. Each part of the diagram has
|
|
|
30
30
|
|
|
31
31
|
A service diagram shows each service and its state. Each step shows one event and the state that the event changes. Thus, you do not have to remember the state.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
state
|
|
43
|
-
service
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
step "
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
CLI.url = localhost:4321
|
|
66
|
-
step "You open the page"
|
|
67
|
-
> Your browser gets the spec from the server and shows it as a page.
|
|
68
|
-
Page -> CLI: GET /
|
|
69
|
-
CLI -> Page: spec
|
|
70
|
-
Page.page = rendered
|
|
71
|
-
step "You click a call stack row"
|
|
72
|
-
> The Diff panel asks the server for the code that the row refers to.
|
|
73
|
-
Panel -> CLI: /__diffmap/source
|
|
74
|
-
CLI -> Repo: git show
|
|
75
|
-
> The server reads the file from the commit in the row, not from the working tree.
|
|
76
|
-
step "The Diff panel shows the code"
|
|
77
|
-
> The server sends the lines, and the Diff panel shows them next to the spec.
|
|
78
|
-
CLI -> Panel: lines
|
|
79
|
-
Panel.shows = code
|
|
33
|
+
A service diagram can also show a change. Red parts exist only today, and green parts are new. As you step, each value shows today's state crossed out next to the new state.
|
|
34
|
+
|
|
35
|
+
This change moves login sessions from memory to a database. Press **Play** to step through it:
|
|
36
|
+
|
|
37
|
+
```services diff
|
|
38
|
+
service Browser "login page"
|
|
39
|
+
state cookie = none
|
|
40
|
+
service API "HTTP server"
|
|
41
|
+
- service Cache "in-memory sessions"
|
|
42
|
+
- state sessions = 0
|
|
43
|
+
+ service Sessions
|
|
44
|
+
service Postgres
|
|
45
|
+
- state sessions = not used
|
|
46
|
+
+ state sessions = 0 rows
|
|
47
|
+
|
|
48
|
+
start "Sessions move from memory to Postgres"
|
|
49
|
+
> Alice logs in, and the API stores her session. Today it keeps the session in memory, so a server restart signs her out. After the change, Sessions writes it to Postgres, where it survives a restart.
|
|
50
|
+
|
|
51
|
+
step "Alice logs in"
|
|
52
|
+
Browser -> API: POST /login
|
|
53
|
+
-step "The API keeps the session in memory"
|
|
54
|
+
API -> Cache: create(alice)
|
|
55
|
+
Cache.sessions = 1
|
|
56
|
+
> The server loses this when it restarts.
|
|
57
|
+
+step "Sessions writes the session to Postgres"
|
|
58
|
+
API -> Sessions: create(alice)
|
|
59
|
+
Sessions -> Postgres: INSERT s_81f
|
|
60
|
+
Postgres.sessions = 1 row
|
|
61
|
+
> The row survives a restart.
|
|
62
|
+
step "The API sets the session cookie"
|
|
63
|
+
API -> Browser: 200 Set-Cookie
|
|
64
|
+
Browser.cookie = sid=s_81f
|
|
80
65
|
```
|
|
81
66
|
|
|
82
67
|
### Mermaid diagrams
|
|
83
68
|
|
|
84
|
-
The page shows Mermaid diagrams. Each node and each arrow can have a link to its code.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
69
|
+
The page shows Mermaid diagrams. Each node and each arrow can have a link to its code. A diagram can show a change in one picture: green parts are new, and red parts are removed.
|
|
70
|
+
|
|
71
|
+
This diagram shows what occurs when you click a link, and what changed when diffmap.dev began to read code from GitHub:
|
|
72
|
+
|
|
73
|
+
```mermaid diff [diffmap.dev reads linked code from GitHub]
|
|
74
|
+
flowchart LR
|
|
75
|
+
Click[Click a link] --> Find[useSourceReference]
|
|
76
|
+
Find -->|diffmap serve| Local[Read the repo with git]
|
|
77
|
+
+ Find -->|diffmap.dev| GitHub[fetchPinnedBlob]
|
|
78
|
+
Local --> Panel[SourceDiffPanel]
|
|
79
|
+
+ GitHub --> Panel
|
|
80
|
+
%% ref node:Find [[src/viewer/sourceNavigation.ts#useSourceReference]]
|
|
81
|
+
%% ref node:GitHub [[src/github/fetchRepo.ts#fetchPinnedBlob]]
|
|
82
|
+
%% ref node:Panel [[src/viewer/SourceDiffPanel.tsx#SourceDiffPanel]]
|
|
83
|
+
%% ref edge:1 [[src/viewer/sourceNavigation.ts#requestSource]]
|
|
97
84
|
```
|
|
98
85
|
|
|
99
86
|
### Call stacks
|
package/src/host/home.test.ts
CHANGED
|
@@ -37,6 +37,17 @@ test("homepage markdown is a real viewer document", async () => {
|
|
|
37
37
|
const mermaid = fences.find((fence) => fence.kind === "mermaid");
|
|
38
38
|
assert.notEqual(mermaid, undefined);
|
|
39
39
|
if (mermaid?.kind !== "mermaid") return;
|
|
40
|
+
// The homepage shows diagram diffs, not only call stack diffs.
|
|
41
|
+
assert.notEqual(mermaid.sides, undefined);
|
|
42
|
+
assert.equal(
|
|
43
|
+
fences.some(
|
|
44
|
+
(fence) =>
|
|
45
|
+
fence.kind === "services" &&
|
|
46
|
+
fence.parsed.ok &&
|
|
47
|
+
fence.parsed.diagram.diff,
|
|
48
|
+
),
|
|
49
|
+
true,
|
|
50
|
+
);
|
|
40
51
|
assert.equal(
|
|
41
52
|
mermaid.annotations.some((link) =>
|
|
42
53
|
link.annotation.references.some((ref) => ref.kind === "file"),
|
package/src/services.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { parseAnnotation, type SourceAnnotation } from "./annotations.js";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A `services` fence: services that hold state, nest sub-services, and send
|
|
5
|
-
* each other events. Steps replay the events in order.
|
|
6
|
-
*
|
|
5
|
+
* each other events. Steps replay the events in order. A step's events travel
|
|
6
|
+
* one after another; its state updates land when the last one arrives.
|
|
7
7
|
*/
|
|
8
8
|
export type ServiceDiagram = {
|
|
9
9
|
services: Service[];
|
|
@@ -128,9 +128,8 @@ const rowSize = defineVars({
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
const styles = {
|
|
131
|
-
// Sized like Maui's code block,
|
|
131
|
+
// Sized like Maui's code block, but in the proportional UI font.
|
|
132
132
|
shell: style(text({ size: "xs", fontWeight: 400, color: "highContrast" }), {
|
|
133
|
-
fontFamily: codeFontFamily,
|
|
134
133
|
margin: 0,
|
|
135
134
|
paddingBlock: "8px",
|
|
136
135
|
overflowX: "hidden",
|
|
@@ -172,7 +171,6 @@ const styles = {
|
|
|
172
171
|
backgroundColor: colors.red[5],
|
|
173
172
|
boxShadow: `inset 3px 0 ${colors.red[9]}`,
|
|
174
173
|
},
|
|
175
|
-
"&[data-note]": { cursor: "help" },
|
|
176
174
|
// The sign fills the row gutter, so the dot follows the label instead.
|
|
177
175
|
"&[data-note='start'] .stack-label::after": {
|
|
178
176
|
content: '""',
|
package/src/viewer/CodeBlock.tsx
CHANGED
|
@@ -92,14 +92,15 @@ function ServicePlayer(props: { diagram: ServiceDiagram } & SourceNavigation) {
|
|
|
92
92
|
const goTo = (target: number, animate: boolean) =>
|
|
93
93
|
setPosition(positionFor(diagram, target, animate && !reduceMotion));
|
|
94
94
|
|
|
95
|
+
// Events travel one after another, so the step settles when the last lands.
|
|
95
96
|
useEffect(() => {
|
|
96
97
|
if (position.phase !== "travel") return;
|
|
97
98
|
const timer = setTimeout(
|
|
98
99
|
() => setPosition({ step: position.step, phase: "settled" }),
|
|
99
|
-
TRAVEL_MS,
|
|
100
|
+
TRAVEL_MS * (diagram.steps[position.step - 1]?.events.length ?? 1),
|
|
100
101
|
);
|
|
101
102
|
return () => clearTimeout(timer);
|
|
102
|
-
}, [position]);
|
|
103
|
+
}, [position, diagram]);
|
|
103
104
|
|
|
104
105
|
useEffect(() => {
|
|
105
106
|
if (!playing || position.phase !== "settled") return;
|
|
@@ -536,22 +536,27 @@ function EventMarks(
|
|
|
536
536
|
const label = props.event.label === "" ? undefined : pillText(props.event);
|
|
537
537
|
const lengths = cumulativeLengths(props.points);
|
|
538
538
|
const total = lengths.at(-1)! || 1;
|
|
539
|
+
// A step's events leave in order, each when the one before it arrives.
|
|
540
|
+
const delay = props.phase === "travel" ? (props.index * TRAVEL_MS) / 1000 : 0;
|
|
539
541
|
return (
|
|
540
542
|
<>
|
|
541
543
|
{props.phase === "travel" && (
|
|
542
544
|
<motion.div
|
|
543
545
|
className={packet}
|
|
544
546
|
data-change={props.event.sign}
|
|
545
|
-
initial={{ x: props.points[0]!.x, y: props.points[0]!.y }}
|
|
547
|
+
initial={{ x: props.points[0]!.x, y: props.points[0]!.y, opacity: 0 }}
|
|
546
548
|
animate={{
|
|
547
549
|
x: props.points.map((point) => point.x),
|
|
548
550
|
y: props.points.map((point) => point.y),
|
|
551
|
+
opacity: 1,
|
|
549
552
|
}}
|
|
550
553
|
// Even speed along every segment of the route.
|
|
551
554
|
transition={{
|
|
555
|
+
delay,
|
|
552
556
|
duration: TRAVEL_MS / 1000,
|
|
553
557
|
ease: "easeInOut",
|
|
554
558
|
times: lengths.map((length) => length / total),
|
|
559
|
+
opacity: { delay, duration: 0 },
|
|
555
560
|
}}
|
|
556
561
|
/>
|
|
557
562
|
)}
|
|
@@ -563,6 +568,7 @@ function EventMarks(
|
|
|
563
568
|
data-event-label={props.index}
|
|
564
569
|
initial={{ opacity: 0 }}
|
|
565
570
|
animate={{ opacity: 1 }}
|
|
571
|
+
transition={{ delay }}
|
|
566
572
|
>
|
|
567
573
|
{label !== undefined &&
|
|
568
574
|
(annotation === undefined ? (
|
|
@@ -911,7 +917,6 @@ const described = {
|
|
|
911
917
|
textDecorationStyle: "dotted" as const,
|
|
912
918
|
textDecorationColor: colors.gray[8],
|
|
913
919
|
textUnderlineOffset: "3px",
|
|
914
|
-
cursor: "help",
|
|
915
920
|
},
|
|
916
921
|
};
|
|
917
922
|
|
|
@@ -1002,7 +1007,7 @@ const styles = {
|
|
|
1002
1007
|
focusRing(),
|
|
1003
1008
|
{
|
|
1004
1009
|
minWidth: 0,
|
|
1005
|
-
"& > [data-described]": described["&[data-described='true']"],
|
|
1010
|
+
"& > [data-described='true']": described["&[data-described='true']"],
|
|
1006
1011
|
},
|
|
1007
1012
|
),
|
|
1008
1013
|
headerButton: style({
|
package/src/viewer/notes.tsx
CHANGED
|
@@ -105,7 +105,6 @@ export function NoteText(props: { text: string }) {
|
|
|
105
105
|
|
|
106
106
|
const symbolNoteClass = style(focusRing(), {
|
|
107
107
|
borderRadius: "3px",
|
|
108
|
-
cursor: "help",
|
|
109
108
|
textDecorationLine: "underline",
|
|
110
109
|
textDecorationStyle: "dotted",
|
|
111
110
|
textDecorationColor: colors.gray[9],
|