forma-diagrams 0.5.2
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/Dockerfile +16 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/bin/forma +3 -0
- package/deploy/.env.example +11 -0
- package/deploy/Caddyfile +4 -0
- package/deploy/compose.yml +38 -0
- package/dist/assets/IBMPlexSans-Regular-Bl2SjS7V.ttf +0 -0
- package/dist/assets/IBMPlexSans-SemiBold-B9auKknr.ttf +0 -0
- package/dist/assets/elk.bundled-Y7ymokgr.js +24 -0
- package/dist/assets/index-_kg9cdik.css +1 -0
- package/dist/assets/index-al0xDdTG.js +452 -0
- package/dist/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/dist/fonts/IBMPlexSans-SemiBold.ttf +0 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/index.html +14 -0
- package/dist/licenses/FORMA_LICENSE.txt +21 -0
- package/dist/licenses/THIRD_PARTY_LICENSES.txt +11601 -0
- package/docs/api.md +41 -0
- package/docs/decisions/001-foundation.md +57 -0
- package/docs/decisions/002-composition-and-human-edits.md +47 -0
- package/docs/decisions/003-general-composition.md +30 -0
- package/docs/decisions/004-library-and-distribution.md +33 -0
- package/docs/decisions/005-organizational-hosting.md +33 -0
- package/docs/decisions/006-hosted-agent-access.md +25 -0
- package/docs/format.md +132 -0
- package/docs/gallery.md +73 -0
- package/docs/images/architecture.png +0 -0
- package/docs/images/editor.png +0 -0
- package/docs/images/gallery/architecture.png +0 -0
- package/docs/images/gallery/decision.png +0 -0
- package/docs/images/gallery/development-signal.png +0 -0
- package/docs/images/gallery/development.png +0 -0
- package/docs/images/gallery/editor-roundtrip.png +0 -0
- package/docs/images/gallery/entities.png +0 -0
- package/docs/images/gallery/mindmap.png +0 -0
- package/docs/images/gallery/organization.png +0 -0
- package/docs/images/gallery/timeline.png +0 -0
- package/docs/images/release.png +0 -0
- package/docs/install.md +50 -0
- package/docs/plans/mvp.md +24 -0
- package/docs/self-hosting.md +167 -0
- package/docs/verification-v2.md +41 -0
- package/docs/verification-v3.md +16 -0
- package/docs/verification-v4.1.md +35 -0
- package/docs/verification-v4.2.md +20 -0
- package/docs/verification-v4.3.md +5 -0
- package/docs/verification-v4.md +31 -0
- package/docs/verification-v5.1.md +7 -0
- package/docs/verification-v5.2.md +7 -0
- package/docs/verification-v5.md +7 -0
- package/docs/verification.md +91 -0
- package/examples/design-systems/atelier.json +53 -0
- package/examples/design-systems/signal.json +53 -0
- package/examples/gallery/architecture.forma.json +178 -0
- package/examples/gallery/decision.forma.json +182 -0
- package/examples/gallery/development-signal.forma.json +299 -0
- package/examples/gallery/development.forma.json +299 -0
- package/examples/gallery/entities.forma.json +123 -0
- package/examples/gallery/mindmap.forma.json +158 -0
- package/examples/gallery/organization.forma.json +151 -0
- package/examples/gallery/timeline.forma.json +138 -0
- package/examples/platform.forma.json +116 -0
- package/examples/release.forma.json +58 -0
- package/examples/roundtrip/agent-continued.forma.json +310 -0
- package/examples/roundtrip/agent-patch.json +7 -0
- package/examples/roundtrip/human-edited.forma.json +309 -0
- package/package.json +81 -0
- package/packages/cli/bin.mjs +3 -0
- package/packages/cli/src/agent-access.ts +232 -0
- package/packages/cli/src/blob-store.ts +241 -0
- package/packages/cli/src/file-library.ts +179 -0
- package/packages/cli/src/google.ts +51 -0
- package/packages/cli/src/hosted.ts +498 -0
- package/packages/cli/src/http.ts +67 -0
- package/packages/cli/src/index.ts +397 -0
- package/packages/cli/src/mcp.ts +114 -0
- package/packages/cli/src/remote.ts +228 -0
- package/packages/cli/src/server.ts +69 -0
- package/packages/cli/src/signed-cookie.ts +42 -0
- package/packages/core/src/align.ts +237 -0
- package/packages/core/src/document.ts +408 -0
- package/packages/core/src/font-metrics.json +1 -0
- package/packages/core/src/geometry.ts +463 -0
- package/packages/core/src/index.ts +12 -0
- package/packages/core/src/inspect.ts +230 -0
- package/packages/core/src/layout.ts +450 -0
- package/packages/core/src/render.ts +182 -0
- package/packages/core/src/resolve-style.ts +29 -0
- package/packages/core/src/scene.ts +40 -0
- package/packages/core/src/styles.ts +122 -0
- package/packages/core/src/text.ts +81 -0
- package/packages/core/src/theme.ts +33 -0
- package/public/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/public/fonts/IBMPlexSans-SemiBold.ttf +0 -0
- package/public/fonts/OFL.txt +93 -0
- package/schema/design-system.v1.schema.json +734 -0
- package/schema/forma.v1.schema.json +273 -0
- package/schema/forma.v2.schema.json +1756 -0
- package/skills/forma/SKILL.md +20 -0
- package/skills/forma/references/composition.md +11 -0
- package/skills/forma/references/hosted.md +28 -0
- package/skills/forma-architecture/SKILL.md +14 -0
- package/skills/forma-entities/SKILL.md +14 -0
- package/skills/forma-flow/SKILL.md +14 -0
- package/skills/forma-mindmap/SKILL.md +14 -0
- package/skills/forma-organization/SKILL.md +14 -0
- package/skills/forma-timeline/SKILL.md +14 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Host Forma for your organization
|
|
2
|
+
|
|
3
|
+
Hosted Forma supports Google sign-in and a private server-backed diagram library for each admitted user. Your organization operates the server, disk, domain, Google OAuth client, and backups. No Forma-operated service or external database is required. Local `forma serve` and static deployments still work without accounts.
|
|
4
|
+
|
|
5
|
+
This release is **single-instance**, with private libraries rather than shared team folders. Do not run multiple replicas against the same data directory.
|
|
6
|
+
|
|
7
|
+
## Configure Google
|
|
8
|
+
|
|
9
|
+
Create a **Web application** OAuth client in your organization's Google Cloud project. Configure its consent screen and authorized users according to your organization's Google policy. Set the exact authorized redirect URI:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
https://diagrams.example.com/auth/callback
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Use the same origin for `FORMA_PUBLIC_URL`. Request only OpenID, email, and profile; Forma does not need Drive access. Use an Internal consent audience when suitable for your Workspace organization, or explicitly manage your External/test audience. Follow [Google's setup and OpenID Connect documentation](https://developers.google.com/identity/openid-connect/openid-connect).
|
|
16
|
+
|
|
17
|
+
Store the client secret only on the host, never in a diagram, frontend build variable, public repository, or agent prompt. The server requires an allowlist even if the Google consent audience is restricted. Workspace domains use Google's `hd` claim, not email suffix matching.
|
|
18
|
+
|
|
19
|
+
## Docker Compose with HTTPS
|
|
20
|
+
|
|
21
|
+
Requires Docker Compose, a public domain pointing to your host, and inbound ports 80 and 443 available for Caddy. From a checkout:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
cp deploy/.env.example deploy/.env
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Edit `deploy/.env` with the public URL, real client ID/secret, and allowed Workspace domains and/or individual Google emails. The example values are placeholders. Then:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
docker compose --env-file deploy/.env -f deploy/compose.yml up -d --build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Caddy terminates HTTPS and proxies to the unprivileged Forma container. Only Caddy publishes network ports. Persistent named volumes hold diagrams and TLS state. Do not run `down --volumes` unless you intentionally want to delete that data. See [Caddy reverse-proxy documentation](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy) and [Compose environment-file documentation](https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/).
|
|
34
|
+
|
|
35
|
+
Visit the public URL, sign in with an allowed Google account, and save a diagram in Library. Confirm that another allowed account has an independent library. Verify a disallowed account cannot sign in before inviting users.
|
|
36
|
+
|
|
37
|
+
## Run with Node instead
|
|
38
|
+
|
|
39
|
+
Build a source checkout with `npm ci && npm run build`, or install a release package. Configure environment variables through your service manager or a private environment file. For example, from a checkout using Node's environment-file support:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
node --env-file=/etc/forma/forma.env packages/cli/bin.mjs host
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A release installation exposes the same command as `forma host` when the environment is already loaded. A process supervisor should keep it running. Terminate TLS with your existing reverse proxy and preserve the original public `Host` header. Keep the upstream private. Do not expose the unauthenticated `forma serve` command.
|
|
46
|
+
|
|
47
|
+
| Variable | Meaning / default |
|
|
48
|
+
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
|
49
|
+
| `FORMA_PUBLIC_URL` | Required canonical HTTPS origin, without a path, such as `https://diagrams.example.com` |
|
|
50
|
+
| `FORMA_GOOGLE_CLIENT_ID` | Required Google Web client ID |
|
|
51
|
+
| `FORMA_GOOGLE_CLIENT_SECRET` | Required server-only client secret |
|
|
52
|
+
| `FORMA_DATA_DIR` | Required durable directory; Compose sets `/data` |
|
|
53
|
+
| `FORMA_ALLOWED_EMAILS` | Comma-separated explicit Google emails |
|
|
54
|
+
| `FORMA_ALLOWED_DOMAINS` | Comma-separated Google Workspace domains; at least one allowlist must be nonempty |
|
|
55
|
+
| `FORMA_BIND_HOST` | `127.0.0.1`; Compose sets `0.0.0.0` on its private network |
|
|
56
|
+
| `FORMA_PORT` | `4242` |
|
|
57
|
+
| `FORMA_USER_BYTES` | `100000000` bytes per user |
|
|
58
|
+
| `FORMA_USER_FILES` | `1000` diagrams per user, maximum 1000 |
|
|
59
|
+
| `FORMA_MAX_USERS` | `500` user directories |
|
|
60
|
+
| `FORMA_ENABLE_MCP` | `true` enables the optional Streamable HTTP MCP endpoint at `/mcp`; default is off |
|
|
61
|
+
|
|
62
|
+
HTTP is allowed only for `localhost` or `127.0.0.1` development origins and binds to loopback. Google must have the matching loopback callback URI registered. Production uses HTTPS Secure cookies. Forma does not infer its public URL from untrusted forwarded headers. `/healthz` is a public health endpoint, still subject to the canonical Host check.
|
|
63
|
+
|
|
64
|
+
## Vercel (personal hosted editor)
|
|
65
|
+
|
|
66
|
+
The static Vercel deployment can run the same Google sign-in and private library as `forma host`, using [Vercel Blob](https://vercel.com/docs/vercel-blob) instead of a local disk. Sessions are signed cookies, so they work across serverless invocations. This is a personal/small-team host, not a replacement for a dedicated VM when you need a locked data directory.
|
|
67
|
+
|
|
68
|
+
1. Create a Google **Web application** OAuth client. Authorized JavaScript origin and redirect URI:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
https://YOUR-DEPLOYMENT.vercel.app
|
|
72
|
+
https://YOUR-DEPLOYMENT.vercel.app/auth/callback
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
2. Create a private Blob store and set environment variables on the Vercel project (Production and Preview):
|
|
76
|
+
|
|
77
|
+
| Variable | Meaning |
|
|
78
|
+
| ---------------------------- | ------------------------------------------------------------ |
|
|
79
|
+
| `FORMA_HOSTED` | `1` so the editor build includes the hosted sign-in shell |
|
|
80
|
+
| `FORMA_PUBLIC_URL` | The stable `https://….vercel.app` origin (no path) |
|
|
81
|
+
| `FORMA_GOOGLE_CLIENT_ID` | Google Web client ID |
|
|
82
|
+
| `FORMA_GOOGLE_CLIENT_SECRET` | Google client secret |
|
|
83
|
+
| `FORMA_ALLOWED_EMAILS` | Your Google account, comma-separated if several |
|
|
84
|
+
| `FORMA_ALLOWED_DOMAINS` | Optional Workspace `hd` values |
|
|
85
|
+
| `FORMA_SESSION_SECRET` | Long random string; changing it signs everyone out |
|
|
86
|
+
| `BLOB_READ_WRITE_TOKEN` | Set automatically when a Blob store is linked to the project |
|
|
87
|
+
|
|
88
|
+
3. Redeploy. Visit the production origin, sign in with an allowed Google account, and save a diagram in Library. Agent tokens from **Agent access** still work with `FORMA_REMOTE_URL` set to that origin.
|
|
89
|
+
|
|
90
|
+
## Updating a hosted instance
|
|
91
|
+
|
|
92
|
+
The application and the diagram store are separate. An update replaces Forma. It does not replace libraries, Blob objects, Docker volumes, or `FORMA_DATA_DIR` unless you delete those yourself.
|
|
93
|
+
|
|
94
|
+
Check the latest GitHub [release](https://github.com/joeycast/forma/releases) and its notes before you move. Read the notes for env-var or OAuth-redirect changes. Google client secrets, allowlists, and session/Blob tokens stay on the host; they are not in the public repository.
|
|
95
|
+
|
|
96
|
+
### Node (`forma host`)
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
npm install -g https://github.com/joeycast/forma/releases/download/v0.5.2/forma-diagrams-0.5.2.tgz
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart the `forma host` process (or its supervisor). Leave `FORMA_DATA_DIR` pointed at the same disk. Verify `/healthz`, then sign in and open a known diagram.
|
|
103
|
+
|
|
104
|
+
The npm registry package `forma-diagrams` is prepared but not published yet. Until it is, use the GitHub tarball above. Source checkouts update with `git fetch` and a checkout of the release tag, then `npm ci && npm run build`, then restart.
|
|
105
|
+
|
|
106
|
+
### Docker Compose
|
|
107
|
+
|
|
108
|
+
Keep the named volumes. Rebuild and recreate the app container from the tag you want:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
git fetch
|
|
112
|
+
git checkout v0.5.2
|
|
113
|
+
docker compose --env-file deploy/.env -f deploy/compose.yml up -d --build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Do not pass `--volumes` to `docker compose down` unless you intend to delete diagrams and TLS state.
|
|
117
|
+
|
|
118
|
+
### Vercel
|
|
119
|
+
|
|
120
|
+
A Vercel project runs a specific deployment of this repository. Env vars and Blob data survive deploys.
|
|
121
|
+
|
|
122
|
+
If Production is **not** connected to GitHub (CLI deploys only), update by deploying the tree you want:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
git fetch
|
|
126
|
+
git checkout v0.5.2
|
|
127
|
+
vercel --prod --yes
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If you connect GitHub, every push to the linked branch deploys that branch to the project. That is convenient and also means `main` commits go live on your login host. Prefer Production deploys from **release tags** when the project is your own workspace.
|
|
131
|
+
|
|
132
|
+
After changing Vercel env vars (allowlist, OAuth, session secret), trigger a new deployment so the functions load the new values.
|
|
133
|
+
|
|
134
|
+
npm does not update a Vercel site. Registry packages help CLI and `forma host` operators; Vercel still needs a git or CLI deploy of the app.
|
|
135
|
+
|
|
136
|
+
## Daily use and agents
|
|
137
|
+
|
|
138
|
+
After sign-in, Library opens to your private files. Create diagrams, organize them with relative paths such as `engineering/platform.forma.json`, and save explicitly. Reloading opens Library rather than restoring a private draft from browser storage. Unsaved hosted edits do not survive a page reload or sign-out; use Save or export a native copy first. Sign-out also closes authenticated views in other tabs.
|
|
139
|
+
|
|
140
|
+
Use **Agent access** to issue a scoped, revocable token for a user’s own library. The agent never receives Google credentials or browser cookies. Set `FORMA_REMOTE_URL` to this origin and store the token in a private `chmod 600` file referenced by `FORMA_AGENT_TOKEN_FILE` (or `FORMA_AGENT_TOKEN` in the agent’s secret environment). Then:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
forma remote whoami
|
|
144
|
+
forma remote list
|
|
145
|
+
forma remote pull engineering/platform.forma.json --output platform.forma.json
|
|
146
|
+
forma remote push platform.forma.json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`forma mcp` is a local stdio bridge over that same authenticated API. Set `FORMA_ENABLE_MCP=true` only if you also want Streamable HTTP clients to POST to `/mcp` with a Bearer token. This release does not implement MCP OAuth discovery; prefer the stdio bridge when the client cannot set HTTP headers. Revoking a token in the editor blocks new operations immediately. File export/import still works if a user does not want to issue a token.
|
|
150
|
+
|
|
151
|
+
Opening an imported file detaches its old library binding; save to a new path if one already exists. Remote pull/push keep a `.forma-remote.json` sidecar with the server, owner, path, and revision; a conflict means reread and reconcile rather than retrying blindly.
|
|
152
|
+
|
|
153
|
+
Applied design systems live inside saved diagrams. In hosted mode, saved design presets last for the page session; export the design JSON to reuse it elsewhere. Shared organizational preset administration is not included yet. Agent tokens are stored hashed in `agent-tokens.json` at the data-directory root; include that file in backups.
|
|
154
|
+
|
|
155
|
+
## Administration, backup, and recovery
|
|
156
|
+
|
|
157
|
+
Data lives under `users/<opaque-subject-hash>/diagrams/` as native JSON. The sibling `identity.json` identifies the owner for the administrator. Ordinary users cannot access this metadata or other user directories through HTTP. Host administrators can read the disk; this is not end-to-end encrypted storage.
|
|
158
|
+
|
|
159
|
+
Back up the entire data directory/volume, preserving ownership and permissions. Stop Forma for a consistent snapshot. Restore into a stopped instance; omit `.forma-host.lock` from a restored snapshot, then start and verify two users' files. Keep secrets and TLS backups separate from public source control. Test recovery periodically. Stop-before-copy also applies to administrative file moves or deletions; the app currently has no delete/retention UI.
|
|
160
|
+
|
|
161
|
+
A clean shutdown removes `.forma-host.lock`. After an unclean shutdown the lock may remain. Confirm that every Forma process using that directory is stopped before removing that one lock file; never remove a live process's lock. Named Docker volumes can be inspected or mounted in a maintenance container by an administrator. Do not remove the users directory to clear a lock.
|
|
162
|
+
|
|
163
|
+
Admission is `FORMA_ALLOWED_EMAILS` and/or `FORMA_ALLOWED_DOMAINS`. Domain access permits all users whose verified Google `hd` matches; use explicit emails when selective access is required. Existing files remain when admission is removed. On Node/`forma host`, changing the allowlist requires a process restart and in-memory sessions are cleared. On Vercel, change the env vars and redeploy; admission is re-checked on each API request. Sessions expire after 12 hours. Google account changes are checked on the next sign-in. Immediate offboarding is: update the allowlist, then restart or redeploy. Add internet-edge rate limiting/monitoring appropriate to your host; the application bounds sessions, login attempts, users, document sizes, and per-user storage, but is not a general denial-of-service protection service.
|
|
164
|
+
|
|
165
|
+
## Verification boundary
|
|
166
|
+
|
|
167
|
+
Tests and the visibly labeled local preview exercise simulated identities, real cookie sessions, private filesystem storage, quotas, and browser interactions. They do not claim to complete a live Google consent exchange. The deployment owner must supply an OAuth client and validate the real redirect, admission, and TLS configuration. No fake-auth switch is available in `forma host`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# General engine verification
|
|
2
|
+
|
|
3
|
+
Performed locally on 2026-09-20, after validating the original MVP. No remote CI run or external deployment is claimed.
|
|
4
|
+
|
|
5
|
+
## Automated checks
|
|
6
|
+
|
|
7
|
+
`npm run check` runs the original regression suite plus general-engine tests, TypeScript checking, and the static production build. The 19 tests cover v1 compatibility, explicit migration, strict style validation, style cascade precedence, transactional merges, preservation of human overrides, grid reflow, side ports, diagonal obstacle intersection, ellipse attachment, and every rendered gallery artifact. The original CLI file workflow remains covered.
|
|
8
|
+
|
|
9
|
+
Every gallery artifact has zero errors and zero warnings from the inspector. The inspector measures geometric heuristics; it does not certify aesthetic quality. All eight PNGs were also visually reviewed. Refinements included moving a feedback loop to side ports, composing the mind map around its center, reducing excess export whitespace, increasing timeline breathing room, and fixing fixed-color text contrast under a dark design system.
|
|
10
|
+
|
|
11
|
+
The seven repository skills passed the skill-creator validator. The initial Python environment lacked PyYAML; validation used a temporary virtual environment with PyYAML, without adding it to the product dependencies. Skills remain plain Markdown and harness-independent.
|
|
12
|
+
|
|
13
|
+
## Actual graphical round trip
|
|
14
|
+
|
|
15
|
+
1. Rendered `examples/gallery/development.forma.json` through the CLI and inspected the PNG.
|
|
16
|
+
2. Opened that native file through the web editor's file chooser.
|
|
17
|
+
3. Changed `verify` from **Verify & integrate** to **Verify with evidence** using its label control.
|
|
18
|
+
4. Changed its fill to `#ffe5a3` using the appearance control.
|
|
19
|
+
5. Dragged it graphically; the saved position is **x 568, y 336**.
|
|
20
|
+
6. Downloaded the native document through Export. The result is preserved in `examples/roundtrip/human-edited.forma.json`.
|
|
21
|
+
7. Applied `examples/roundtrip/agent-patch.json` via the CLI. It updates descriptions and adds the connector label **Evidence accepted**, without replacing human properties.
|
|
22
|
+
8. Exported the result to SVG and PNG, with strict inspection reporting zero issues.
|
|
23
|
+
9. Reopened it in the editor and verified the new description, label, fill, and pin. The complete result is `examples/roundtrip/agent-continued.forma.json`.
|
|
24
|
+
|
|
25
|
+
Assertions against the actual downloaded file checked that the original node style object survived unchanged, the human fill override survived, and the complete presentation object was identical before and after the agent patch.
|
|
26
|
+
|
|
27
|
+
## Static-client and design-system checks
|
|
28
|
+
|
|
29
|
+
The production build was served at a nested `/forma/` path using Python's static HTTP server. The native v2 round-trip artifact loaded and rendered without an application backend. The visual identity selector changed the imported diagram to Signal while preserving content and explicit node styles. As documented, changing system geometry around absolute human pins can produce layout suggestions; no pin was silently discarded. The separate unpinned Signal gallery artifact demonstrates a clean complete restyle of identical content.
|
|
30
|
+
|
|
31
|
+
CLI `migrate` and `style --system` were also exercised on the original v1 architecture artifact. The resulting document validated as v2. New CLI commands preserve atomic-write behavior.
|
|
32
|
+
|
|
33
|
+
## Remaining limits
|
|
34
|
+
|
|
35
|
+
- Only IBM Plex Sans is bundled and measured. Other font names survive editing but can use platform fallback; inspection warns about them.
|
|
36
|
+
- Group heading dimensions are fixed. Context-specific style properties are documented rather than interpreted as arbitrary CSS.
|
|
37
|
+
- The grid is content-sized and ordinal. Empty index gaps do not reserve tracks; timelines are not calibrated time-axis or scheduling tools.
|
|
38
|
+
- Absolute pins can conflict after structural or design-system changes; inspection makes those conflicts visible.
|
|
39
|
+
- Direct connectors do not avoid obstacles. Orthogonal routing has obstacle avoidance, but dense diagrams can still need refinement.
|
|
40
|
+
- Crow's-foot markers, full sequence lifelines, reusable component definitions, and editable Office/draw.io exporters remain future work.
|
|
41
|
+
- Large build-chunk warnings come from the lazy ELK bundle and editor dependencies. Runtime remains local and static.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Usability release verification
|
|
2
|
+
|
|
3
|
+
Verified locally on 2026-09-20 for v0.3.0.
|
|
4
|
+
|
|
5
|
+
- `npm run check`: 20 tests, TypeScript, and production build pass. File-server integration covers nested files, revision conflicts, external edits, directory changes, stale workspace rejection, traversal/symlink rejection, Origin/Host restrictions, and required mutation headers.
|
|
6
|
+
- Real browser: opened a diagram from a nested local folder, changed its title, saved it to disk, created and applied a custom gold design system, and saved again. Read the disk file to confirm both changes.
|
|
7
|
+
- Changed that file externally. The editor rejected its stale save without discarding the draft. Refreshed Library and reopened the agent-modified document; the custom identity and individual overrides remained intact.
|
|
8
|
+
- Saved a second file under a new subfolder and reloaded the page. The active save path survived the tab reload. Changed the source directory in the UI, created the missing folder, and saved there successfully.
|
|
9
|
+
- Stopped the local server after the diagram loaded. Browser SVG and PNG exports both downloaded successfully; PNG displayed the success notification. Inspected the actual PNG: typography, group bounds, arrow routing, colors, and the individually dashed verification node remained correct.
|
|
10
|
+
- Static-hosted build: saved a diagram to the browser library under a virtual subfolder. No local file server or account was required.
|
|
11
|
+
- The in-app agent prompt includes installation, skill discovery, source folder, CLI verification, and human-edit preservation. Its copy button was exercised.
|
|
12
|
+
- Packed the release and installed it to an isolated npm global prefix. From outside the repository, the installed `forma` created and rendered an architecture diagram with zero inspection warnings/errors, then served the packaged editor. Browser library tests ran against that installed editor.
|
|
13
|
+
|
|
14
|
+
The export change removes the export-time font fetch and rejected-promise cache. The originally reported intermittent failure was not independently reproduced; the now-network-independent export path was directly verified with the server stopped.
|
|
15
|
+
|
|
16
|
+
Known boundaries: one local folder server, explicit saves/refresh, optimistic file revision checks rather than cross-process transactions, and browser-local saved design presets. Local server restarts require reopening the file from Library. Static browser storage is not a backup. Google authentication and organizational server storage are deliberately deferred.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Hosted agent-access verification
|
|
2
|
+
|
|
3
|
+
Verified during the 0.4.1 implementation on 2026-09-21.
|
|
4
|
+
|
|
5
|
+
## Automated checks
|
|
6
|
+
|
|
7
|
+
`npm run check` passes all 27 tests, TypeScript, and the production build. Formatting and schema-regeneration checks also pass. New coverage in `tests/agent-access.test.ts` exercises:
|
|
8
|
+
|
|
9
|
+
- Session-issued tokens that never store or list the secret, with hashed on-disk registry reload.
|
|
10
|
+
- Folder scope, path containment, read-only denial, and cross-user isolation.
|
|
11
|
+
- REST create/read/update with optimistic revision conflicts.
|
|
12
|
+
- `forma remote` pull/push, sidecar binding, and refusal to push another user’s document.
|
|
13
|
+
- Streamable HTTP MCP and stdio `forma mcp` over the same token, including revocation.
|
|
14
|
+
- Token expiry and rejected remote origins.
|
|
15
|
+
|
|
16
|
+
Existing hosted authentication, library isolation, quota, cookie, and local CLI tests remain green.
|
|
17
|
+
|
|
18
|
+
## Browser and deployment boundary
|
|
19
|
+
|
|
20
|
+
The Agent access UI is included in the production editor bundle. End-to-end clicks of token creation were not exercised in a live browser in this environment; the REST routes that UI calls are covered by the automated suite. Live Google sign-in, public TLS, and a production MCP client configuration still require the operator’s OAuth client and host, as in 0.4.0.
|
|
21
|
+
|
|
22
|
+
See [self-hosting](self-hosting.md) and [ADR 006](decisions/006-hosted-agent-access.md).
|
|
23
|
+
|
|
24
|
+
## Follow-up verification — 2026-09-27
|
|
25
|
+
|
|
26
|
+
Revalidated release 0.5.2 at `6f2b611d1c9919d787a8ef02b36c60a03b3be3b2`.
|
|
27
|
+
|
|
28
|
+
- A clean checkout passed all 42 tests, TypeScript, and the production build on Node 26.7.0. This includes REST, remote CLI, HTTP MCP, stdio MCP, user isolation, folder scope, read-only enforcement, revocation, and revision conflicts.
|
|
29
|
+
- Exercised the production editor through `tests/fixtures/hosted-preview.ts`, using its explicitly labeled simulated identity provider. Opened **Agent access**, created a read-only token restricted to `verification` with a seven-day expiry, dismissed the one-time secret, and confirmed the displayed permissions and expiry.
|
|
30
|
+
- Clicked **Revoke** and **Confirm revoke** and verified the connection disappeared. The test token was revoked; no secret was retained in screenshots or verification notes.
|
|
31
|
+
- Visually inspected the rendered connection dialog after hiding the secret. Its fields, scope explanation, connection metadata, and revocation control were visible without clipping.
|
|
32
|
+
|
|
33
|
+
The original browser-verification gap above is now closed. Live Google OAuth and a public deployment were not exercised in this follow-up. MCP currently uses manually configured bearer tokens, not an OAuth discovery/consent flow. Local CLI, local editor, and static use remain account-free.
|
|
34
|
+
|
|
35
|
+
The original workspace had macOS-offloaded dependency and source files, causing reads to stall. Validation therefore ran from an independent checkout of the identical commit outside the synced Documents directory, with dependencies installed from the lockfile. The build emitted non-fatal upstream annotation and bundle-size warnings.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Alignment verification
|
|
2
|
+
|
|
3
|
+
Verified during the 0.4.2 implementation on 2026-09-21.
|
|
4
|
+
|
|
5
|
+
## Automated checks
|
|
6
|
+
|
|
7
|
+
`npm run check` passes all 31 tests, TypeScript, and the production build. Formatting checks also pass. New coverage in `tests/align.test.ts` and the CLI suite exercises:
|
|
8
|
+
|
|
9
|
+
- Left alignment to the selection bounds and even-gap distribution.
|
|
10
|
+
- Pinned near-alignment inspection on rows and columns, with merged x/y `--fix` pins.
|
|
11
|
+
- Snapping an unpinned neighbor to a pinned edge without marking it pinned.
|
|
12
|
+
- `forma align --left --ids` writing shared coordinates.
|
|
13
|
+
|
|
14
|
+
Existing engine, gallery, hosted, and local-library tests remain green. Automatic layout is unchanged; near-alignment warnings apply to pinned nodes.
|
|
15
|
+
|
|
16
|
+
## Browser and deployment boundary
|
|
17
|
+
|
|
18
|
+
The editor bundle includes multi-select, the align bar, inspector actions, 8px snap matching the visible grid, and arrow-key nudge. Those UI paths were not clicked in a live browser in this environment; the core and CLI operations they call are covered by tests.
|
|
19
|
+
|
|
20
|
+
Static hosting of `dist/` remains the account-free editor. Organizational Google sign-in still requires `forma host`.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 0.4.3 distribution verification
|
|
2
|
+
|
|
3
|
+
Verified 2026-09-21.
|
|
4
|
+
|
|
5
|
+
`npm run check` and `npm publish --dry-run` pass on the 0.4.3 tree. Install docs, the agent setup prompt, and hosted update instructions use `npm install -g forma-diagrams` / `npm update -g forma-diagrams`. GitHub releases still attach a checksummed tarball. Publishing the GitHub release is intended to publish the same version to the npm registry via `.github/workflows/publish.yml`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Hosted-mode verification
|
|
2
|
+
|
|
3
|
+
Verified during the 0.4.0 implementation on 2026-09-21.
|
|
4
|
+
|
|
5
|
+
## Automated checks
|
|
6
|
+
|
|
7
|
+
`npm run check` passes all 25 tests, TypeScript, and the production build. Formatting and schema-regeneration checks also pass. The suite runs the existing engine/CLI/local-library coverage and new hosted coverage. The hosted tests exercise:
|
|
8
|
+
|
|
9
|
+
- Google authorization URL identity scopes, callback, S256 challenge, state, and nonce.
|
|
10
|
+
- Browser-bound, single-use, expiring login attempts; provider failures and nonce mismatches fail closed.
|
|
11
|
+
- Verified email and explicit admission allowlists; Workspace admission uses the `hd` claim.
|
|
12
|
+
- Independent Alice/Bob libraries, denied cross-user reads/writes, and no absolute server paths in responses.
|
|
13
|
+
- Native saves, optimistic revision conflicts, external agent edits, and preserved server files.
|
|
14
|
+
- File and byte quotas; rejected writes do not create empty directories; simultaneous conflicting saves have only one winner.
|
|
15
|
+
- Logout, session expiry, unauthenticated API rejection, canonical Host enforcement, required Origin and custom mutation headers, secure production cookie attributes, and data-directory process locking.
|
|
16
|
+
|
|
17
|
+
The cryptographic ID-token verifier is Google's maintained library, not a custom implementation. Deterministic application tests inject an identity-provider boundary; they do not constitute a live Google consent exchange.
|
|
18
|
+
|
|
19
|
+
## Browser checks
|
|
20
|
+
|
|
21
|
+
A real browser used the compiled static editor and real hosted HTTP/file service through `tests/fixtures/hosted-preview.ts`. This fixture is visibly labeled **TEST PREVIEW · simulated identity provider**, alternates Alice and Bob, and is not included in release packages or reachable through `forma host`.
|
|
22
|
+
|
|
23
|
+
Alice signed in, saved `engineering/alice.forma.json`, edited its title, saved again, and exported a PNG successfully under the hosted content policy. The file was confirmed on disk. Restarting the server required sign-in again, then Alice's saved diagram could be reopened from Library. Signing out also returned another open workspace tab to the sign-in page. Bob's subsequent sign-in opened an empty library with the default example, without recovering Alice's private diagram. Signing back in as Alice restored access to Alice's file.
|
|
24
|
+
|
|
25
|
+
The sign-in page and hosted UI were inspected visually. Hosted mode uses server libraries and session-only design presets rather than local browser recovery. Existing local/static modes continue to use their previous storage behavior.
|
|
26
|
+
|
|
27
|
+
## Deployment boundary
|
|
28
|
+
|
|
29
|
+
The Docker build/start/restart/unauthenticated-API check passed in GitHub CI, alongside the Linux test/build checks. The npm release tarball was installed to an isolated prefix outside the repository; its hosted server served the editor and rejected unauthenticated library access, and its local CLI rendered a diagram with zero inspection errors/warnings. Compose and Caddy configuration validation are also included in CI. Docker is not installed on the development Mac, so local Node tests and isolated npm installs provide the local runtime verification.
|
|
30
|
+
|
|
31
|
+
A live deployment still needs the operator's Google Web OAuth client, consent configuration, allowlist, domain, TLS, and durable disk. No such credentials or host were supplied in this phase; live Google sign-in and public TLS issuance are not claimed as verified. The supplied Compose/Caddy configuration and [operator guide](self-hosting.md) document the steps.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.5.1 line editing verification
|
|
2
|
+
|
|
3
|
+
Verified 2026-09-22.
|
|
4
|
+
|
|
5
|
+
41 automated tests, the production build, and `npm publish --dry-run` pass. Install docs, the agent setup prompt, and hosted update instructions use the v0.5.1 GitHub release tarball. `forma-diagrams` is still not on the npm registry.
|
|
6
|
+
|
|
7
|
+
0.5.1 lets a selected line be dragged onto any connection point and lets a straight stretch slide while its ends stay attached. Outline selection no longer blanks the canvas. The element inspector lists that shape's lines and hides document-wide theme and layout controls.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.5.2 editing verification
|
|
2
|
+
|
|
3
|
+
Verified 2026-09-22.
|
|
4
|
+
|
|
5
|
+
42 automated tests, the production build, and `npm publish --dry-run` pass. Install docs, the agent setup prompt, and hosted update instructions use the v0.5.2 GitHub release tarball. `forma-diagrams` is still not on the npm registry.
|
|
6
|
+
|
|
7
|
+
0.5.2 lets you resize a selected box, raises a selected line's handles above overlapping lines, and selects a box on the first click. Shift-click and the drag box select only what you aimed at. The sheet footer can be rewritten or hidden.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.5.0 connector and text verification
|
|
2
|
+
|
|
3
|
+
Verified 2026-09-21.
|
|
4
|
+
|
|
5
|
+
40 automated tests, the production build, and `npm publish --dry-run` pass on the 0.5.0 tree. Install docs, the agent setup prompt, and hosted update instructions still use the GitHub release tarball. `forma-diagrams` is not on the npm registry; the 0.4.3 publish workflow failed with `ENEEDAUTH` because `NPM_TOKEN` is not available to Actions. Publishing the GitHub release will try that workflow again.
|
|
6
|
+
|
|
7
|
+
0.5.0 adds evenly spaced connection points (one per side by default), optional pinned connector paths, routing that keeps existing bends and allows shared horizontal or vertical runs, shift-click multi-select, and text alignment on both axes.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# MVP verification
|
|
2
|
+
|
|
3
|
+
Verified locally on 2026-09-20 with Node 26.7.0 and the Codex in-app Chromium browser.
|
|
4
|
+
The supported minimum runtime is Node 22; the included CI workflow targets Node 22.
|
|
5
|
+
CI has not been run remotely as part of this local implementation.
|
|
6
|
+
|
|
7
|
+
## Automated checks
|
|
8
|
+
|
|
9
|
+
`npm run check` passes 14 tests, the strict TypeScript check and the static Vite build.
|
|
10
|
+
`npm run format:check` passes. The tests include real CLI subprocesses and actual
|
|
11
|
+
PNG signatures, not mocked exports.
|
|
12
|
+
|
|
13
|
+
Covered behavior: schema versions, duplicate IDs, missing references, group cycles,
|
|
14
|
+
transactional patches, cascade deletion, stable serialization, repeatable geometry,
|
|
15
|
+
nested and negative-coordinate pins, long text, group heading bounds, branching,
|
|
16
|
+
cycles, self-loops, disconnected graphs, XML escaping, connector attachment,
|
|
17
|
+
overlap/collision/crossing reports, preservation of unrelated geometry after a pin,
|
|
18
|
+
and rejection of oversized PNG exports.
|
|
19
|
+
|
|
20
|
+
Both shipped examples pass `inspect --strict` with zero errors and zero warnings.
|
|
21
|
+
|
|
22
|
+
## Agent → human → agent → export
|
|
23
|
+
|
|
24
|
+
1. Created and rendered the architecture document using the shared engine/CLI.
|
|
25
|
+
2. Opened the graphical editor. Renamed `gateway` from “API gateway” to “Public API”,
|
|
26
|
+
selected the violet accent, and dragged it to `{x: 440, y: 248}`.
|
|
27
|
+
3. Downloaded the native `.forma.json` through the editor's Export menu.
|
|
28
|
+
4. CLI-patched that actual downloaded file: updated the gateway description,
|
|
29
|
+
added a read replica, and connected the primary database to the replica.
|
|
30
|
+
5. Asserted the original human label, violet color and exact pinned position survived.
|
|
31
|
+
6. Inspected the seven-node, seven-edge result: zero errors and zero warnings.
|
|
32
|
+
7. Exported it through the CLI to SVG and PNG, then reopened the native file in the editor.
|
|
33
|
+
8. Verified the replica and updated description appeared while the pin remained.
|
|
34
|
+
|
|
35
|
+
Local evidence files are under `output/`: `human-edited.forma.json`,
|
|
36
|
+
`agent-update.json`, `roundtrip.forma.json`, `roundtrip.svg`, and `roundtrip.png`.
|
|
37
|
+
These generated files are intentionally ignored by Git.
|
|
38
|
+
|
|
39
|
+
## Graphical editing acceptance
|
|
40
|
+
|
|
41
|
+
Started a blank document, added a service and database, renamed the service,
|
|
42
|
+
connected their ports with a pointer drag, and edited the edge label and line style.
|
|
43
|
+
Deleted the connection and restored it with Undo. Created a group and assigned a
|
|
44
|
+
component to it. Confirmed arrow-key movement creates a persistent pin. Confirmed
|
|
45
|
+
Cmd-S commits the field being edited before downloading the native document.
|
|
46
|
+
|
|
47
|
+
Verified paper and midnight themes, both templates, pan/zoom/fit controls, source
|
|
48
|
+
validation, native import/save, browser SVG/PNG exports and undo/redo. The source
|
|
49
|
+
editor rejected version 99 while retaining the existing document. Undo restored
|
|
50
|
+
the prior title, Redo reapplied it, and reloading recovered the last saved document.
|
|
51
|
+
History itself is session-only and is not restored after a reload.
|
|
52
|
+
|
|
53
|
+
## Static deployment and responsive check
|
|
54
|
+
|
|
55
|
+
Built `dist/`, copied it under a `/forma/` directory, and served it with Python's
|
|
56
|
+
plain HTTP server. There was no Vite runtime, application backend or database.
|
|
57
|
+
The application rendered and exported PNG successfully from this subpath,
|
|
58
|
+
confirming asset and embedded-font paths work on static hosts. No browser warnings
|
|
59
|
+
or errors were recorded in the production check.
|
|
60
|
+
|
|
61
|
+
Checked the normal 1280×720 viewport and a 390×844 narrow viewport. On narrow
|
|
62
|
+
screens the file menu remains available and properties open as an overlay; zoom
|
|
63
|
+
and pan remain available for diagrams wider than the screen. Desktop editing is
|
|
64
|
+
the primary experience. The temporary viewport override was reset afterward.
|
|
65
|
+
|
|
66
|
+
## Visual review
|
|
67
|
+
|
|
68
|
+
Inspected actual exported PNGs and the running editor, rather than relying on
|
|
69
|
+
DOM assertions alone. Reviewed typography/weights, text wrapping, container bounds,
|
|
70
|
+
connector routing, label separation, emphasis, hierarchy and negative space.
|
|
71
|
+
Fixed nested ELK edge coordinate offsets, group-title overflow, excessive compound
|
|
72
|
+
whitespace, inconsistent variable-font rasterization, and lost keyboard positions.
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
## Known limits
|
|
79
|
+
|
|
80
|
+
- Layout inspection is a set of geometric heuristics, not a proof of visual quality.
|
|
81
|
+
Impossible pinned configurations remain pinned and produce diagnostics.
|
|
82
|
+
- Layout runs locally in the browser; very large diagrams can occupy the main thread.
|
|
83
|
+
Small, focused diagrams are the quality target despite higher schema limits.
|
|
84
|
+
- Bundled font coverage and advance metrics target Latin-script professional diagrams.
|
|
85
|
+
Full complex-script shaping and fallback fonts are not implemented.
|
|
86
|
+
- The canonical file supports node color and position overrides; arbitrary vector
|
|
87
|
+
drawing, resizing, manual bend editing and live collaboration are outside this MVP.
|
|
88
|
+
- PNG has explicit raster-size limits. SVG remains the scalable export.
|
|
89
|
+
- VSDX, draw.io, PDF and PowerPoint exporters are extension points, not implemented features.
|
|
90
|
+
- Vite reports the expected large ELK chunk and strips two upstream Zod annotation
|
|
91
|
+
comments during the build. These are build warnings; runtime checks pass.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"id": "atelier",
|
|
4
|
+
"name": "Atelier / editorial",
|
|
5
|
+
"revision": "1.0.0",
|
|
6
|
+
"canvas": {
|
|
7
|
+
"background": "#f7f5ef",
|
|
8
|
+
"text": "#242c29",
|
|
9
|
+
"secondary": "#69736b",
|
|
10
|
+
"border": "#d8ded4"
|
|
11
|
+
},
|
|
12
|
+
"spacing": {
|
|
13
|
+
"node": 44,
|
|
14
|
+
"layer": 80
|
|
15
|
+
},
|
|
16
|
+
"node": {
|
|
17
|
+
"shape": "rect",
|
|
18
|
+
"fill": "#fffef9",
|
|
19
|
+
"stroke": "#c7d0c3",
|
|
20
|
+
"text": "#243b30",
|
|
21
|
+
"secondary": "#657367",
|
|
22
|
+
"radius": 2,
|
|
23
|
+
"fontFamily": "IBM Plex Sans",
|
|
24
|
+
"fontSize": 16,
|
|
25
|
+
"padding": 22
|
|
26
|
+
},
|
|
27
|
+
"edge": {
|
|
28
|
+
"stroke": "#64776a",
|
|
29
|
+
"strokeWidth": 1.6,
|
|
30
|
+
"arrowEnd": "filled"
|
|
31
|
+
},
|
|
32
|
+
"group": {
|
|
33
|
+
"fill": "#eeeee5",
|
|
34
|
+
"stroke": "#cdd3c6",
|
|
35
|
+
"radius": 4,
|
|
36
|
+
"dash": "solid",
|
|
37
|
+
"text": "#526849"
|
|
38
|
+
},
|
|
39
|
+
"roles": {
|
|
40
|
+
"emphasis": {
|
|
41
|
+
"fill": "#dce9b9",
|
|
42
|
+
"stroke": "#a7be78"
|
|
43
|
+
},
|
|
44
|
+
"caution": {
|
|
45
|
+
"fill": "#fae4b6",
|
|
46
|
+
"stroke": "#d8b977"
|
|
47
|
+
},
|
|
48
|
+
"quiet": {
|
|
49
|
+
"fill": "#eeeee8",
|
|
50
|
+
"stroke": "#d0d3c9"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"id": "signal",
|
|
4
|
+
"name": "Signal / technical",
|
|
5
|
+
"revision": "1.0.0",
|
|
6
|
+
"canvas": {
|
|
7
|
+
"background": "#101c2e",
|
|
8
|
+
"text": "#eff6ff",
|
|
9
|
+
"secondary": "#99b0cb",
|
|
10
|
+
"border": "#31455f"
|
|
11
|
+
},
|
|
12
|
+
"spacing": {
|
|
13
|
+
"node": 48,
|
|
14
|
+
"layer": 96
|
|
15
|
+
},
|
|
16
|
+
"node": {
|
|
17
|
+
"shape": "rect",
|
|
18
|
+
"fill": "#192b43",
|
|
19
|
+
"stroke": "#3d5877",
|
|
20
|
+
"text": "#f1f7ff",
|
|
21
|
+
"secondary": "#a3bad3",
|
|
22
|
+
"radius": 16,
|
|
23
|
+
"fontFamily": "IBM Plex Sans",
|
|
24
|
+
"fontSize": 16,
|
|
25
|
+
"padding": 22
|
|
26
|
+
},
|
|
27
|
+
"edge": {
|
|
28
|
+
"stroke": "#77b9d2",
|
|
29
|
+
"strokeWidth": 2,
|
|
30
|
+
"arrowEnd": "open"
|
|
31
|
+
},
|
|
32
|
+
"group": {
|
|
33
|
+
"fill": "#142238",
|
|
34
|
+
"stroke": "#344960",
|
|
35
|
+
"radius": 20,
|
|
36
|
+
"dash": "dashed",
|
|
37
|
+
"text": "#9dcfe0"
|
|
38
|
+
},
|
|
39
|
+
"roles": {
|
|
40
|
+
"emphasis": {
|
|
41
|
+
"fill": "#164857",
|
|
42
|
+
"stroke": "#50b7bc"
|
|
43
|
+
},
|
|
44
|
+
"caution": {
|
|
45
|
+
"fill": "#56402c",
|
|
46
|
+
"stroke": "#cf9d60"
|
|
47
|
+
},
|
|
48
|
+
"quiet": {
|
|
49
|
+
"fill": "#1a2739",
|
|
50
|
+
"stroke": "#344960"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|