Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d483431
update mermaid parser with the dom purify version (#1438)
JannikStreek Oct 1, 2026
5cc75a6
test: cover GET /api/maps against forged or empty person_id cookies (…
JannikStreek Oct 2, 2026
92353a5
refactor: single change path in mmp for nodes and a DOM-free node sto…
JannikStreek Oct 3, 2026
de95062
update screenshots
JannikStreek Oct 4, 2026
985f507
refactor: data-driven d3 renderer in mmp with node parts (#1448)
JannikStreek Oct 4, 2026
8c66c74
refactor: remove mmp history as yjs already holds the state (#1449)
JannikStreek Oct 4, 2026
b686d67
refactor: move view state out of data model (#1450)
JannikStreek Oct 4, 2026
30b359f
feat: create the map after the first sync (#1451)
JannikStreek Oct 5, 2026
72ad2ae
feat: add the MapData contract to mmp (#1452)
JannikStreek Oct 5, 2026
6a84b2e
feat: implement MapData over the Y.Doc (#1453)
JannikStreek Oct 5, 2026
4b71e86
refactor: bind node ids in the mmp renderer (#1454)
JannikStreek Oct 5, 2026
a79db70
test: build mmp specs from snapshots (#1455)
JannikStreek Oct 5, 2026
6b3761b
refactor: answer mmp tree queries by node id (#1456)
JannikStreek Oct 5, 2026
1a70f0f
refactor: write mmp nodes through MapData (#1457)
JannikStreek Oct 5, 2026
9638757
refactor: read mmp nodes through MapData (#1458)
JannikStreek Oct 5, 2026
c8790f1
perf: draw only the nodes a change touches (#1459)
JannikStreek Oct 5, 2026
7533e28
fix: assign a main root on load and paste in one change (#1460)
JannikStreek Oct 5, 2026
11e06b4
refactor: let MmpService select and edit new nodes (#1461)
JannikStreek Oct 5, 2026
7e499ff
feat: refresh the toolbar and map cache on mapChange (#1462)
JannikStreek Oct 5, 2026
c410208
Refactor: yjs map data (#1463)
JannikStreek Oct 5, 2026
fe9c1b9
refactor: remove the Yjs mirror helpers (#1464)
JannikStreek Oct 5, 2026
f1dfaf1
test: cover two-client sync in e2e (#1465)
JannikStreek Oct 5, 2026
2f803de
refactor: remove the mmp mirror API (#1466)
JannikStreek Oct 5, 2026
b3983ac
refactor: cleanup after mmp refactoring (#1467)
JannikStreek Oct 5, 2026
52909aa
refactor: fix node defaults after mmp refactoring (#1468)
JannikStreek Oct 5, 2026
aa6126c
Merge tag 'v0.3.6-1' into merge-v0.3.6
JannikStreek Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 68 additions & 12 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ say so.
| fold, collapse, unfold, expand | hide, show |
| clone, fork | duplicate |
| version, revision, snapshot | (none of these exist; see **Undo / Redo**) |
| flag, mark (for a stored value) | attribute |

### Reserved

Expand Down Expand Up @@ -57,8 +58,8 @@ branch to it. A map holds at least one root node, the main root.
### Main root

The one root node per map that the app creates with the map. No one can delete,
copy or cut it. The stored flag `isRoot` marks the main root only, so do
not read `isRoot` to learn whether a node has a parent.
copy or cut it. The stored attribute `isRoot` is true for the main root only,
so do not read `isRoot` to learn whether a node has a parent.

### Orphaned node

Expand Down Expand Up @@ -104,12 +105,12 @@ insert the symbol as a node image. Not available on every server.

### Protected branch

A node and every node below it, which a user marks as finished to tell others
not to change them. The app stores the flag `protected` on the top node of the
branch only and draws a lock badge on that node. A node counts as protected
when it or an ancestor carries the flag. Any writable client protects a branch
or releases it again from the toolbar's lock button, and the flag syncs to
every client.
A node and every node below it, which a user declares finished to tell others
not to change them. The app sets the attribute `protected` on the top node of
the branch only and draws a lock badge on that node. A node counts as protected
when its own `protected` attribute or an ancestor's is true. Any writable
client protects a branch or releases it again from the toolbar's lock button,
and the attribute syncs to every client.

A protected branch refuses local edits: rename, style, image, link, drag, add
child, paste, remove and cut. Peer writes, undo, redo, redistribute and import
Expand All @@ -121,11 +122,27 @@ Write "protect" and "release". Avoid "lock" for the concept, and "freeze" or

### Hidden node

A node the app does not draw because an ancestor's children are hidden. The map
does not store hiding: hiding is how one person is looking at the map right now.
A node the app does not draw because the view state hides the child nodes of
one of its ancestors. The app also hides a node a peer adds below such an
ancestor. A hidden node keeps its room in the layout.

Write "hide" and "show". Avoid "collapse" and "fold".

### View state

The set of nodes whose child nodes one person has hidden. mmp keeps the view
state apart from the map data, so the undo stack never records it. The
frontend sends no record of which child nodes are hidden to the server or to a
peer, and stores none, so a reload shows them again. The view state starts
empty each time the app opens a map.

### Selection, ring

The **selection** is the one node a client has selected. mmp draws a **ring**
around it, and around each node a peer has selected, in that peer's client
color. Each client keeps its own selection apart from the map data and from
the view state. Peers see it through Yjs awareness (see **Presence**).

### Map settings

What a single mind map remembers about how the app draws it, chiefly the font
Expand All @@ -147,6 +164,16 @@ admin the delete action. The map list, which the settings' third tab and the
start page both show, offers the same delete action for every listed map whose
admin id the browser holds, open or not.

### Node mark

One visual element of a drawn node: its background, name, image, link, hidden
eye icon or lock badge. The renderer in `packages/mmp` draws every node from
the marks in `NODE_MARKS`, so a new visual element is a new mark. The term
follows the visualization sense of a mark: a visual element bound to one data
item. A mark can consist of several DOM elements, such as the link's `<a>` and
`<text>`. Write "DOM element" or the tag name when you mean the browser object,
and "node group" for the `<g>` that holds all marks of one node.

## What you can do to a map

### Add, remove, select, deselect
Expand Down Expand Up @@ -231,8 +258,8 @@ or "fork".

### Writable

Whether a connection may edit. The app stores one flag; read-only is its
negation. A connection is writable when it presents the map's modification
Whether a connection may edit. The app stores one boolean attribute, and
read-only is its negation. A connection is writable when it presents the map's modification
secret.

### Viewer link, editor link
Expand Down Expand Up @@ -276,3 +303,32 @@ edits.

Whether this client reaches the server. Losing the connection raises the
"Connection lost" dialog with a reconnect button.

## Words for code

### Attribute

A value a data object holds, such as a node's `isRoot`, `protected` or
`name`. A boolean attribute is still an attribute: write "the `isRoot`
attribute is true", not "the node carries the root flag".

### Flag

A switch that configures the app from outside its data: an environment
variable, a feature flag or a command-line option. A value stored on a node or
a map is an attribute.

### Map data

The nodes of one mind map as every client shares them. The app draws the map
from the map data and keeps no copy of its own, so your edits, a peer's edits
and an undo all change the map data first, and the app then redraws.

In the app, the map data syncs with the server and the other clients. Tests,
and a host without sync, use `InMemoryMapData`. The **view state** and the
**selection** stay with one person and never enter the map data.

### Mark

Reserved for the **node mark**, one visual element of a drawn node. Do not use
"mark" for a stored value: a node holds an attribute, it does not carry a mark.
2 changes: 1 addition & 1 deletion openspec/changes/add-node-protection/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ A node gains `protected: boolean`. The schema declares it `v.optional(v.boolean(
At most one flag lies on any path from a root to a leaf:

- Protecting a node that is already protected is not offered: the button shows "release".
- Protecting a node clears the flag on every descendant. `MapSyncService.toggleBranchProtection` runs the toggle inside one local Y.Doc transaction, so peers receive every flag write as one update and one undo reverts them together.
- Protecting a node clears the flag on every descendant. `MmpService.toggleBranchProtection` calls mmp, which writes the toggle as one map data batch and so one local Y.Doc transaction, so peers receive every flag write as one update and one undo reverts them together.

Releasing from any node of a protected branch clears the flag on `protectingNode`, which releases the whole branch in one write.

Expand Down
2 changes: 1 addition & 1 deletion openspec/changes/add-node-protection/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The three sections shipped together in one pull request. Section 1 removes the o

## 3. Protect, release and refuse

- [x] 3.1 Add `protectBranch` and `releaseBranch` to `packages/mmp`, writing the flag and clearing descendant flags, and export them from the entry point. `MapSyncService.toggleBranchProtection` wraps the toggle in one Y.Doc transaction
- [x] 3.1 Add `protectBranch` and `releaseBranch` to `packages/mmp`, writing the flag and clearing descendant flags, and export them from the entry point. `MmpService.toggleBranchProtection` calls mmp, which writes the toggle as one map data batch and so one Y.Doc transaction
- [x] 3.2 Refuse local rename, style, image, link, font, drag, add child, paste, remove and cut inside a protected branch, emitting `nodeProtected`, and skip the check when `notifyWithEvent` is false
- [x] 3.3 Draw the lock badge on the node carrying the flag and redraw it on local and remote changes
- [x] 3.4 Replace the toolbar slot with the lock and unlock button, disabled with no selection and on read-only clients
Expand Down
10 changes: 5 additions & 5 deletions packages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ Libraries shared between the applications in this repository. Each one is a
pnpm workspace package under the `@teammapper` scope and is built to `dist/`
before the applications compile (`pnpm run build:packages` from the root).

| Package | Purpose | Consumers |
| ------------------------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `shared` (`@teammapper/shared`) | Domain models, validation schemas and algorithms that cross the wire. Runs in Node and in the browser. | `teammapper-backend`, `teammapper-frontend`, `mmp` |
| `mmp` (`@teammapper/mmp`) | The mind map renderer built on d3, forked from cedoor/mmp. Browser-only: it needs a DOM. | `teammapper-frontend` |
| `mermaid-mindmap-parser` | Parser for Mermaid mindmap syntax, vendored from the mermaid project. Has no lint script by design. | `teammapper-frontend` |
| Package | Purpose | Consumers |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `shared` (`@teammapper/shared`) | Domain models, validation schemas and algorithms that cross the wire. Runs in Node and in the browser. | `teammapper-backend`, `teammapper-frontend`, `mmp` |
| `mmp` (`@teammapper/mmp`) | The mind map renderer built on d3, forked from cedoor/mmp. Browser-only: it needs a DOM. `create` takes a `MapData`; mmp exports `InMemoryMapData` and the frontend implements `YjsMapData`. | `teammapper-frontend` |
| `mermaid-mindmap-parser` | Parser for Mermaid mindmap syntax, vendored from the mermaid project. Has no lint script by design. | `teammapper-frontend` |

A type that both applications need belongs in `shared`. Rendering code belongs
in `mmp`. Nothing in `shared` may import from `mmp`, since the backend loads
Expand Down
Loading
Loading