grooph 0.4.0 → 0.5.0
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 +33 -31
- package/THIRD-PARTY-NOTICES.md +5 -1
- package/dist/app/assets/App-CT9KxzfQ.css +1 -0
- package/dist/app/assets/App-D1pDFbEY.js +5 -0
- package/dist/app/assets/EmbedApp-CTausQWY.js +1 -0
- package/dist/app/assets/ExportPanel-CbRH4C69.js +8 -0
- package/dist/app/assets/Import-DF0XLxRI.js +1 -0
- package/dist/app/assets/SketchView-BjeUHG4X.js +143 -0
- package/dist/app/assets/brakes-B9b3hDX8.js +1 -0
- package/dist/app/assets/{builtins-Cbs5iQtH.js → builtins-DzyPaYdd.js} +1 -1
- package/dist/app/assets/{compile-BAvi-rd0.js → compile-DL2budwP.js} +8 -8
- package/dist/app/assets/{front-BRl7rl-u.js → front-OxfL-OaD.js} +1 -1
- package/dist/app/assets/full-C0asYORc.js +1 -0
- package/dist/app/assets/graph-more-Dn_hNa20.js +2 -0
- package/dist/app/assets/graph-stage-CwNePnN4.js +4 -0
- package/dist/app/assets/graph-views-CmkQ41zC.js +2 -0
- package/dist/app/assets/{index-BoSi6XYh.js → index-B5ZkSF4L.js} +2 -2
- package/dist/app/assets/mermaid-read-cyqJ32v-.js +17 -0
- package/dist/app/assets/{plan-templates-BQu8G2sX.js → plan-templates-u2fF3GnS.js} +1 -1
- package/dist/app/assets/runs-BA5HKtsx.js +1 -0
- package/dist/app/assets/screens-CMMuexid.js +16 -0
- package/dist/app/assets/share-DV3wsAsJ.js +8 -0
- package/dist/app/assets/site-looks.v1.css +223 -0
- package/dist/app/assets/space-Cuy4YqyZ.js +1 -0
- package/dist/app/assets/styles-Dlp-yYlE.css +1 -0
- package/dist/app/assets/templates-DbAhWvP6.js +2 -0
- package/dist/app/assets/{themes-DtOBrLk0.js → themes-C34TrvOJ.js} +1 -1
- package/dist/app/assets/{units-DRaa5U2m.js → units-DlniUjfH.js} +1 -1
- package/dist/app/assets/{views-DCEDX6Qx.js → views-CFqRShXD.js} +2 -1
- package/dist/app/grooph-app.json +1 -1
- package/dist/app/index.html +14 -11
- package/dist/app/llms.txt +6 -7
- package/dist/app/manifest.webmanifest +1 -1
- package/dist/app/og.png +0 -0
- package/dist/app/routes.json +42 -33
- package/dist/app/sw.js +7 -2
- package/dist/browser/grooph.d.ts +44 -0
- package/dist/browser/grooph.global.js +7 -0
- package/dist/browser/grooph.js +7 -0
- package/dist/bundle/grooph.js +7082 -2606
- package/hooks/grooph-event.mjs +163 -14
- package/package.json +14 -6
- package/dist/app/assets/App-CFO6bPna.js +0 -5
- package/dist/app/assets/App-Cv1auMeh.css +0 -1
- package/dist/app/assets/EmbedApp-B5LWao3x.js +0 -1
- package/dist/app/assets/ExportPanel-VWus7GM3.js +0 -8
- package/dist/app/assets/Import-YEKofx4_.js +0 -1
- package/dist/app/assets/brakes-9E9PsV_Z.js +0 -1
- package/dist/app/assets/graph-more-BKW9C4-h.js +0 -2
- package/dist/app/assets/graph-stage-CWh3OWga.js +0 -4
- package/dist/app/assets/graph-views-tFUJK-be.js +0 -2
- package/dist/app/assets/runs-DcjRi7S6.js +0 -1
- package/dist/app/assets/screens-YUd2HaLm.js +0 -16
- package/dist/app/assets/share-BzIzB4EY.js +0 -8
- package/dist/app/assets/space-DWgrlnfB.js +0 -1
- package/dist/app/assets/styles-BA1aVO9R.css +0 -1
- package/dist/app/assets/templates-DvzLa5sE.js +0 -2
- package/dist/app/demo/run.txt +0 -1
package/README.md
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# grooph
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Lightweight workflow diagrams, in 2D and 3D.** Draw who does the work, who checks it, where a person decides, and when it stops. Draw it yourself, ask an AI agent to draw it, or both. A diagram is one small JSON document; grooph checks it, draws it, and gives you a link that opens it in the [grooph app](https://ryanjosephkamp.github.io/grooph/) on any device.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
This package is the command line, the built-in templates, an MCP server and the app `grooph watch` serves. It has no dependencies, and one optional one: `@resvg/resvg-js`, the PNG renderer, which has no install script.
|
|
5
|
+
grooph never runs an agent and never calls a model. This package is the command line, the built-in templates, and the app that `grooph watch` serves. It has no required dependencies. The one optional dependency, `@resvg/resvg-js`, renders PNGs and has no install script.
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npx grooph --help
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
## A first
|
|
11
|
+
## A first diagram
|
|
14
12
|
|
|
15
|
-
Node 22 or later. This makes a
|
|
13
|
+
Node 22 or later. This makes a diagram from a template, checks it, and draws it:
|
|
16
14
|
|
|
17
15
|
```bash
|
|
18
|
-
npx grooph template use grind-loop --name "Fix the flaky test" --set task="
|
|
19
|
-
npx grooph validate
|
|
16
|
+
npx grooph template use grind-loop --name "Fix the flaky test" --set task="Make the checkout test pass ten times in a row." --set test-command="pnpm test checkout" --out flaky.grooph.json
|
|
17
|
+
npx grooph validate flaky.grooph.json
|
|
20
18
|
npx grooph image flaky.grooph.json --out flaky.svg
|
|
21
|
-
npx grooph
|
|
19
|
+
npx grooph share flaky.grooph.json
|
|
22
20
|
```
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
- `grooph template list` shows every template and when to use it.
|
|
23
|
+
- `grooph share` prints a link that opens the diagram in the app, in 2D or in 3D. The diagram travels after the `#`, which a browser sends to no server.
|
|
24
|
+
- `grooph embed` prints one line of HTML that shows the diagram on any page.
|
|
25
|
+
- `grooph page` writes one HTML file that works offline.
|
|
26
|
+
- `grooph plan` writes a plan people can follow, as Markdown with its picture.
|
|
27
|
+
- `grooph import` reads Mermaid text (a flowchart or an agentflow) into a diagram, and `grooph mermaid` writes one.
|
|
25
28
|
|
|
26
29
|
Install it once to drop the `npx`:
|
|
27
30
|
|
|
@@ -29,35 +32,34 @@ Install it once to drop the `npx`:
|
|
|
29
32
|
npm install --global grooph
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
A PNG (`--out flaky.png`) needs the optional renderer
|
|
35
|
+
A PNG (`--out flaky.png`) needs the optional renderer, which npm installs where a build for your platform exists. The SVG is the same drawing and needs nothing.
|
|
33
36
|
|
|
34
|
-
##
|
|
37
|
+
## On a page, without an iframe
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
From version 0.5.0, the package carries a script that draws a diagram straight into any page, as SVG, with nothing fetched:
|
|
37
40
|
|
|
38
|
-
```
|
|
39
|
-
|
|
41
|
+
```html
|
|
42
|
+
<pre class="grooph">{ "grooph": 0, "id": "review", … }</pre>
|
|
43
|
+
<script src="https://cdn.jsdelivr.net/npm/grooph@0.5.0/dist/browser/grooph.global.js"></script>
|
|
44
|
+
<script>grooph.drawAll();</script>
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
# Codex, in ~/.codex/config.toml
|
|
44
|
-
[mcp_servers.grooph]
|
|
45
|
-
command = "npx"
|
|
46
|
-
args = ["-y", "grooph", "mcp", "--harness", "codex"]
|
|
47
|
-
```
|
|
47
|
+
`import { render, mount, drawAll } from "grooph/browser"` gives the same in a bundler or Node. See [the pictures page](https://ryanjosephkamp.github.io/grooph/docs/exports/#on-any-page-without-an-iframe).
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
## With an agent
|
|
50
|
+
|
|
51
|
+
An agent can draw with the same commands, or through grooph's MCP server. Ask for a diagram in plain words; the agent picks a template or starts from nothing, checks the result, and hands you a link.
|
|
52
|
+
|
|
53
|
+
- [Drawing with an agent](https://ryanjosephkamp.github.io/grooph/docs/agents/): the shortest path, every change by example, and what to do about each rule.
|
|
54
|
+
- [Every rule by its code](https://ryanjosephkamp.github.io/grooph/docs/rules/), and [the document format](https://ryanjosephkamp.github.io/grooph/docs/graph-ir/).
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
## Experimental
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
- [From a chat](https://ryanjosephkamp.github.io/grooph/docs/chat/): what works in Claude's desktop app, in claude.ai and in ChatGPT.
|
|
57
|
-
- [Every rule by its code](https://ryanjosephkamp.github.io/grooph/docs/rules/), and [the graph document](https://ryanjosephkamp.github.io/grooph/docs/graph-ir/).
|
|
58
|
+
These parts work and are tested, and may change, or go, in a later version.
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
- **The MCP server** (`grooph mcp`). An agent with nothing but tool calls can pick a template, fill it, change it, check it, draw it, and hand back a link. `claude mcp add grooph -- npx -y grooph mcp` adds it to Claude Code; `--chat` reads and writes no file of yours. Its `grooph_export` tool still compiles a diagram into a prompt package for Claude Code or Codex, which is the lab's work and experimental.
|
|
61
|
+
- **Watching a session** (`grooph hooks`, `grooph watch`, `grooph sessions`, `grooph events`). A hook records which sessions and subagents start and stop, and nothing they say, and the app draws it live. The hook appends one line per event and returns nothing to the harness. [How it works](https://ryanjosephkamp.github.io/grooph/docs/subagents/).
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
grooph runs no agent, calls no model, and needs no hosted service, and it uploads nothing unless you ask: `grooph events push`, or the hook installed with `--push`, sends what the hook recorded to a branch of its own on the project's own remote, which is public when the repository is.
|
|
62
64
|
|
|
63
|
-
Source
|
|
65
|
+
Source and issues: [github.com/ryanjosephkamp/grooph](https://github.com/ryanjosephkamp/grooph). MIT. The app this package serves includes other people's code; their names and licenses are in `THIRD-PARTY-NOTICES.md`, beside this file.
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
grooph 0.
|
|
3
|
+
grooph 0.5.0 is under the MIT license (`LICENSE`). Some of its files are built files that may hold other people's code, and this page carries those people's notices, as their licenses ask. It is written by the build from what the builds themselves say went into each file, not from a list kept by hand.
|
|
4
4
|
|
|
5
5
|
## In the app (`dist/app/`), which `grooph watch` serves
|
|
6
6
|
|
|
@@ -445,6 +445,10 @@ The app's fonts, Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono, are
|
|
|
445
445
|
|
|
446
446
|
grooph's own code and no other package's.
|
|
447
447
|
|
|
448
|
+
## In the browser build (`dist/browser/`)
|
|
449
|
+
|
|
450
|
+
grooph's own code and no other package's.
|
|
451
|
+
|
|
448
452
|
## Installed beside it, and not in this package
|
|
449
453
|
|
|
450
454
|
- `@resvg/resvg-js` (^2.6.2), an optional dependency npm installs from its own package, under that package's own license (MPL-2.0). grooph copies none of its code.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.site-wrap{width:min(100% - 2 * var(--gutter), 1180px);margin-inline:auto}.site-header [hidden]{display:none!important}.site-header :focus-visible,.site-footer :focus-visible{outline:3px solid var(--bright);outline-offset:3px;border-radius:4px}.site-header{z-index:10;padding-top:env(safe-area-inset-top);background:var(--night);color:var(--night-ink);font:600 .95rem/1.4 var(--font);-webkit-font-smoothing:antialiased;border-bottom:1px solid #ffffff12;border-bottom:1px solid lab(100% 0 0/.07);position:relative}.site-header .site-wrap{flex-wrap:wrap;align-items:center;gap:0 20px;min-height:68px;display:flex}.site-logo{min-height:44px;color:var(--night-ink);letter-spacing:-.01em;align-items:center;gap:10px;margin:0;font-size:1.12rem;font-weight:750;text-decoration:none;display:inline-flex}.site-logo svg{width:32px;height:32px}.site-logo rect{fill:var(--night-line)}.site-logo circle{fill:var(--bright)}.site-logo path{fill:none;stroke:var(--bright);stroke-width:1.6px}.site-nav{margin-left:auto}.site-nav ul{flex-wrap:wrap;align-items:center;gap:4px;margin:0;padding:0;list-style:none;display:flex}.site-nav a{min-height:40px;color:var(--night-muted);border-radius:8px;align-items:center;gap:6px;padding:0 12px;text-decoration:none;transition:color .15s ease-out,background-color .15s ease-out;display:flex}.site-nav a[aria-current]{color:var(--night-ink);background:#ffffff1a;background:lab(100% 0 0/.1)}.site-nav svg{width:14px;height:14px}.site-menu-toggle,.site-theme-toggle{border:1px solid var(--night-line);min-height:44px;color:var(--night-ink);font:inherit;cursor:pointer;background:0 0;border-radius:10px;align-items:center;gap:8px;padding:0 12px;display:inline-flex}.site-menu-toggle{margin-left:auto;padding:0 14px;display:none}.site-menu-toggle svg{width:18px;height:18px}.site-theme{flex:none;position:relative}.site-theme-dot{background:var(--dot,var(--bright));border-radius:50%;flex:none;width:12px;height:12px;box-shadow:0 0 0 1px #ffffff4d;box-shadow:0 0 0 1px lab(100% 0 0/.3)}.site-picture-dot{background:var(--dot,#eef0f4);border-radius:3px;width:14px;height:11px;box-shadow:0 0 0 1px #ffffff4d;box-shadow:0 0 0 1px lab(100% 0 0/.3)}.site-theme-list{z-index:11;border:1px solid var(--night-line);background:var(--night-2);min-width:200px;box-shadow:var(--shadow-night);border-radius:12px;gap:2px;margin:0;padding:6px;list-style:none;display:grid;position:absolute;top:calc(100% + 8px);right:0}.site-theme-list button{width:100%;min-height:44px;color:var(--night-ink);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;align-items:center;gap:10px;padding:0 10px;display:flex}.site-theme-list button svg{width:16px;height:16px;color:var(--bright);visibility:hidden;margin-left:auto}.site-theme-list button[aria-checked=true] svg{visibility:visible}@media (hover:hover){.site-nav a:hover{color:var(--night-ink);background:#ffffff12;background:lab(100% 0 0/.07)}.site-theme-toggle:hover,.site-theme-toggle[aria-expanded=true],.site-menu-toggle:hover,.site-theme-list button:hover{background:#ffffff12;background:lab(100% 0 0/.07)}}@media (max-width:1023px){.site-theme-label{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}}@media (max-width:860px){.site-header .site-wrap{gap:0 10px}.site-nav{flex-basis:100%;margin:0 0 10px}.has-menu .site-menu-toggle{display:inline-flex}.has-menu .site-nav{padding:8px var(--gutter) 18px;border-bottom:1px solid var(--night-line);background:var(--night);box-shadow:var(--shadow-night);margin:0;display:none;position:absolute;top:100%;left:0;right:0}.has-menu .site-nav.is-open{display:block}.has-menu .site-nav ul{flex-direction:column;align-items:stretch;gap:2px}.has-menu .site-nav a{min-height:48px;font-size:1.02rem}}.site-footer{padding:56px 0 calc(40px + env(safe-area-inset-bottom));background:var(--night);color:var(--night-muted);font:400 .94rem/1.6 var(--font);-webkit-font-smoothing:antialiased}.site-footer .site-wrap{grid-template-columns:minmax(0,1.3fr) repeat(2,minmax(0,1fr));gap:32px;display:grid}.site-footer .site-logo{margin-bottom:8px}.site-footer p{margin:0}.site-footer h2{color:var(--night-ink);margin:0 0 10px;font-size:.95rem;font-weight:700}.site-footer ul{gap:6px;margin:0;padding:0;list-style:none;display:grid}.site-footer a{color:var(--night-muted);text-decoration:none}.site-footer li a{padding:2px 0;display:inline-block}.site-credit{border-top:1px solid var(--night-line);flex-wrap:wrap;grid-column:1/-1;align-items:center;gap:12px 20px;padding-top:24px;display:flex}.site-made{color:var(--night-ink);font-weight:600}.site-footer .site-made a{color:var(--night-ink);text-decoration:underline;-webkit-text-decoration-color:color-mix(in oklch, var(--bright) 60%, transparent);text-decoration-color:color-mix(in oklch, var(--bright) 60%, transparent);text-underline-offset:.2em}.site-footer .site-social{flex-wrap:wrap;gap:4px;display:flex}.site-footer .site-social a{border-radius:10px;place-items:center;width:44px;height:44px;padding:0;transition:color .15s ease-out,background-color .15s ease-out;display:grid}.site-social svg{width:19px;height:19px}.site-footer .site-sponsor{border:1px solid var(--night-line);min-height:44px;color:var(--night-ink);border-radius:999px;align-items:center;gap:8px;margin-left:auto;padding:0 14px;font-size:.9rem;font-weight:600;display:inline-flex}.site-sponsor svg{color:#fc93ac;color:lab(72.9096% 42.9118 4.9264);width:17px;height:17px}.site-fine{grid-column:1/-1;font-size:.85rem}.site-footer .site-fine a{text-underline-offset:.2em;text-decoration:underline}@media (hover:hover){.site-footer a:hover{color:var(--night-ink);text-decoration:underline}.site-footer .site-social a:hover{background:#ffffff14;background:lab(100% 0 0/.08);text-decoration:none}.site-footer .site-sponsor:hover{border-color:var(--night-muted);background:#ffffff0d;background:lab(100% 0 0/.05);text-decoration:none}}@media (max-width:720px){.site-footer .site-wrap{grid-template-columns:minmax(0,1fr)}.site-footer .site-sponsor{margin-left:0}}@media (prefers-reduced-motion:reduce){.site-nav a,.site-footer .site-social a{transition:none}}@media print{.site-header,.site-footer{display:none}}.land-play{flex-wrap:wrap;align-items:center;gap:10px;margin:14px 0 0;display:flex}.land-3d{background:var(--bg);height:min(580px,78vh);color:var(--ink);box-shadow:var(--shadow-night);border:1px solid #ffffff1a;border:1px solid lab(100% 0 0/.1);border-radius:16px;padding:12px 0;overflow:auto}.link-button{color:var(--accent);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;text-decoration:underline}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/compile-DL2budwP.js","assets/share-DV3wsAsJ.js","assets/groups-CjEPiwfv.js","assets/graph-stage-CwNePnN4.js","assets/index-B5ZkSF4L.js","assets/full-C0asYORc.js","assets/Import-DF0XLxRI.js","assets/templates-DbAhWvP6.js","assets/runs-BA5HKtsx.js","assets/mermaid-read-cyqJ32v-.js","assets/EmbedApp-CTausQWY.js","assets/EmbedApp-CWb_HGpW.css","assets/screens-CMMuexid.js","assets/screens-DWbqIw6l.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{a as e,c as t,n,r,s as i,t as a}from"./index-B5ZkSF4L.js";import{At as o,B as s,M as c,Ut as l,V as u,a as d,cn as f,fn as p,i as m,n as h,ot as g,pn as _,vn as v,x as y}from"./share-DV3wsAsJ.js";import{d as b,i as x,l as S,m as ee,n as C,o as w,p as T,r as te,t as E,u as D}from"./templates-DbAhWvP6.js";import{A as ne,S as re,c as ie,f as ae,j as O,l as oe,m as se,r as ce,s as le,u as ue}from"./runs-BA5HKtsx.js";var k=t(e(),1),de=String.raw`^(?!#\/(?:run\?live$|live$|run\/[^?]+$|templates$|embed(?:\?|$)|templates\/(?:built-in|yours|plan)\/[^/?]+(?:\/use)?$|g\/[^/?]+(?:\?new|\?sketch)?$|open\?))`,fe=String.raw`^#\/templates(?:$|\/built-in\/[^/?]+(?:\/use)?$)`,pe=`grooph.persistence`;function me(){try{let e=localStorage.getItem(pe);return e?JSON.parse(e):null}catch{return null}}function A(e){try{localStorage.setItem(pe,JSON.stringify(e))}catch{}j=e;for(let e of N)e()}var j=me(),M=!1,N=new Set;async function he(){if(!(j!==null||M)){M=!0;try{let e=typeof navigator<`u`?navigator.storage:void 0;if(!e||typeof e.persist!=`function`){A({result:`unsupported`,seen:!1});return}A({result:typeof e.persisted==`function`&&await e.persisted()||await e.persist()?`granted`:`denied`,seen:!1})}catch{A({result:`denied`,seen:!1})}finally{M=!1}}}function ge(){j&&A({...j,seen:!0})}var _e=e=>(N.add(e),()=>N.delete(e));function ve(){let e=(0,k.useSyncExternalStore)(_e,()=>j);return e&&!e.seen?e:null}ne(()=>void he());var P;function ye(){return n(`compile`,()=>r(()=>import(`./compile-DL2budwP.js`),__vite__mapDeps([0,1,2]))).then(e=>P=e)}var be=()=>P!==void 0;function xe(e){let t=e.target?.harness,n=t!==void 0&&f.includes(t)?t:`claude-code`,r=_(e);if(!r.doc)return{ok:!1,target:t??`(none)`,reason:`schema`,issues:r.issues};let i=g(r.doc,{forExport:!0});if(i.some(e=>e.severity===`error`))return{ok:!1,target:t??`(none)`,reason:`rules`,issues:i};if(!P)return;let a=P.keptFolder(r.doc.id);if(a!==void 0)return{ok:!1,target:n,reason:`id`,issues:[],said:a};let o=P.tryCompile(r.doc,n);return o.ok?{ok:!0,target:n,result:o.result}:{ok:!1,target:t??`(none)`,reason:`rules`,issues:o.issues}}function Se(e){let t={};for(let[n,r]of Object.entries(e))t[n]=m(r);return new Uint8Array(d(t,{level:6}))}var Ce=e=>p(e),we=e=>`${e.id||`graph`}.grooph.json`,Te=(e,t)=>`${e.id||`graph`}-${t}.zip`;function Ee(e,t,n){let r=URL.createObjectURL(new Blob([t],{type:n})),i=document.createElement(`a`);i.href=r,i.download=e,i.rel=`noopener`,document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(r),1e4)}async function De(e){try{return await navigator.clipboard.writeText(e),!0}catch{let t=document.createElement(`textarea`);t.value=e,t.setAttribute(`readonly`,``),t.style.position=`fixed`,t.style.opacity=`0`,document.body.append(t),t.select();let n=document.execCommand(`copy`);return t.remove(),n}}var F=a(),Oe=300,ke=new WeakMap;function Ae(e){let t=ke.get(e);if(!t){let n=y(e);t={svg:n,long:Number(/viewBox="\S+ \S+ (\S+) \S+"/.exec(n)?.[1]??0)>Oe},ke.set(e,t)}return t}var je=e=>Ae(e).long;function I({doc:e,className:t,decorative:n=!1}){return(0,F.jsx)(Me,{...Ae(e),className:t,decorative:n})}function Me({svg:e,long:t,className:n,decorative:r=!1}){return(0,F.jsx)(`span`,{className:`glyph${n?` ${n}`:``}${t?` is-long`:``}`,"aria-hidden":r||void 0,dangerouslySetInnerHTML:{__html:e}})}var L={q:``,kind:[],cost:[],speed:[],rigor:[],tags:[],sort:`name`,open:!1},R={name:`Name`,cost:`Cost, low first`,speed:`Speed, fast first`,rigor:`Rigor, high first`},Ne=e=>e.kind.length+e.cost.length+e.speed.length+e.rigor.length+e.tags.length,Pe=e=>e.q.trim()===``&&Ne(e)===0&&e.sort===`name`,Fe=e=>e.toLowerCase().split(/\s+/).filter(e=>e!==``),Ie=e=>{let t=e.template;return[t.title,t.summary,t.whenToUse,...t.tags??[],e.id].join(`
|
|
3
|
+
`).toLowerCase()};function Le(e,t){let n=e.template;if(t.kind.length>0&&!t.kind.includes(n.kind)||t.cost.length>0&&!t.cost.includes(n.profile.cost)||t.speed.length>0&&!t.speed.includes(n.profile.speed)||t.rigor.length>0&&!t.rigor.includes(n.profile.rigor)||t.tags.length>0&&!t.tags.every(e=>(n.tags??[]).includes(e)))return!1;let r=Fe(t.q);if(r.length===0)return!0;let i=Ie(e);return r.every(e=>i.includes(e))}function Re(e,t,n){let r=e.template,i=t.template,a=r.title.localeCompare(i.title);if(n===`name`)return a;let o=e=>C[n][e.profile[n]],s=n===`cost`?1:-1;return(o(r)-o(i))*s||a}function ze(e,t){return e.filter(e=>Le(e,t)).sort((e,n)=>Re(e,n,t.sort))}function Be(e){let t=new Map;for(let n of e)for(let e of n.template?.tags??[])t.set(e,(t.get(e)??0)+1);return[...t].sort((e,t)=>t[1]-e[1]||e[0].localeCompare(t[0])).map(([e])=>e)}var z=(e,t)=>e.includes(t)?e.filter(e=>e!==t):[...e,t],B=`grooph.templates.browse`,Ve=e=>Array.isArray(e)&&e.every(e=>typeof e==`string`);function He(){try{let e=localStorage.getItem(B);if(e===null)return L;let t=JSON.parse(e),n=(e,t)=>Ve(e)?e.filter(e=>t===void 0||t.includes(e)):[];return{q:typeof t.q==`string`?t.q:``,kind:n(t.kind,[`graph`,`fragment`]),cost:n(t.cost,[`low`,`medium`,`high`]),speed:n(t.speed,[`fast`,`medium`,`slow`]),rigor:n(t.rigor,[`light`,`standard`,`high`]),tags:n(t.tags),sort:typeof t.sort==`string`&&t.sort in R?t.sort:`name`,open:t.open===!0}}catch{return L}}function Ue(e){try{Pe(e)&&!e.open?localStorage.removeItem(B):localStorage.setItem(B,JSON.stringify(e))}catch{}}async function We(){let e=await O();return T((await e.templates.list()).map(e=>e.doc))}async function Ge(e){return(await(await O()).templates.get(e))?.doc}async function Ke(e,t={}){if(!e.template)throw Error(`"${e.id}" has no template block`);if(ee(e))throw Error(`"${e.id}" carries errors; Yours keeps only templates that validate`);let n=await O(),r=await n.templates.get(e.id);if(r&&!t.replace)return{exists:r.doc};let i=r?Math.max(r.doc.version,e.version)+1:e.version,a={id:e.id,doc:{...structuredClone(e),version:i},savedAt:Date.now()};return await n.templates.put(a),{saved:a,replaced:r!==void 0}}async function qe(e){await(await O()).templates.delete(e)}var Je=e=>`${e.id}.grooph.json`,Ye=e=>p(e),V=`https://github.com/ryanjosephkamp/grooph`,H=`/grooph/docs/`,U=[{id:`grooph`,label:`Grooph`,dot:`oklch(0.85 0.14 165)`},{id:`meteor`,label:`Meteor`,dot:`oklch(0.9 0.2 124)`},{id:`harbor`,label:`Harbor`,dot:`#7fb8ff`},{id:`plum`,label:`Plum`,dot:`#d4a8ff`},{id:`ember`,label:`Ember`,dot:`#ffa564`},{id:`graphite`,label:`Graphite`,dot:`#d4d4d8`}],Xe=`groophTheme`,Ze=()=>U.find(e=>e.id===document.documentElement.dataset.theme)?.id??U[0].id;function Qe(e){let t=document.querySelector(`link[data-site-looks]`);if(t?.sheet||t?.dataset.siteLooks===`tried`)return e();t||(t=document.createElement(`link`),t.rel=`stylesheet`,t.dataset.siteLooks=``,t.href=document.querySelector(`link#site-looks`)?.href??`/grooph/assets/site-looks.v1.css`,document.head.append(t));let n=()=>(t.dataset.siteLooks=`tried`,e());t.addEventListener(`load`,n,{once:!0}),t.addEventListener(`error`,n,{once:!0})}function $e(){return(0,F.jsxs)(`svg`,{viewBox:`0 0 32 32`,"aria-hidden":`true`,children:[(0,F.jsx)(`rect`,{width:`32`,height:`32`,rx:`8.5`}),(0,F.jsx)(`path`,{d:`M13.5 12.5 19 21M18.5 12.5 13 21M14 11h4`}),(0,F.jsx)(`circle`,{cx:`10`,cy:`11`,r:`4`}),(0,F.jsx)(`circle`,{cx:`22`,cy:`11`,r:`4`}),(0,F.jsx)(`circle`,{cx:`16`,cy:`23`,r:`4`})]})}var et=()=>(0,F.jsx)(`svg`,{viewBox:`0 0 20 20`,"aria-hidden":`true`,children:(0,F.jsx)(`path`,{d:`m4.5 10.5 3.5 3.5 7.5-8`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})});function tt(){let[e,t]=(0,k.useState)(!1),[n,r]=(0,k.useState)(Ze),i=(0,k.useRef)(null),a=(0,k.useRef)(null),o=()=>{e&&(t(!1),a.current?.focus())};return(0,k.useEffect)(()=>{let e=Array.from(document.querySelectorAll(`meta[name="theme-color"]`)),t=e.map(e=>e.content),n=i.current?getComputedStyle(i.current).backgroundColor:``;if(n)for(let t of e)t.content=n;return()=>e.forEach((e,n)=>e.content=t[n])},[n]),(0,F.jsx)(`header`,{className:`site-header has-menu`,ref:i,onKeyDown:e=>e.key===`Escape`&&o(),children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsxs)(`a`,{className:`site-logo`,href:`#/`,children:[(0,F.jsx)($e,{}),(0,F.jsx)(`span`,{children:`grooph`})]}),(0,F.jsxs)(`button`,{className:`site-menu-toggle`,type:`button`,ref:a,"aria-expanded":e,"aria-controls":`site-nav`,onClick:()=>t(!e),children:[(0,F.jsx)(`svg`,{viewBox:`0 0 20 20`,"aria-hidden":`true`,children:(0,F.jsx)(`path`,{d:`M3.5 6h13M3.5 10h13M3.5 14h13`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`})}),`Menu`]}),(0,F.jsx)(`nav`,{className:`site-nav land-nav${e?` is-open`:``}`,id:`site-nav`,"aria-label":`grooph`,onClick:()=>t(!1),children:(0,F.jsxs)(`ul`,{children:[(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`#/templates`,children:`Templates`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:H,children:`Docs`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${H}field-guide/`,children:`Field guide`})}),(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{href:V,rel:`noopener`,children:[`Source`,(0,F.jsx)(`svg`,{viewBox:`0 0 20 20`,"aria-hidden":`true`,children:(0,F.jsx)(`path`,{d:`M11.5 3.5h5v5M16.5 3.5l-7 7M14.5 12v3.5a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1H8`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,F.jsx)(`span`,{className:`sr-only`,children:` (GitHub)`})]})})]})}),(0,F.jsx)(nt,{theme:n,onTheme:r}),(0,F.jsx)(at,{})]})})}function nt({theme:e,onTheme:t}){let[n,r]=(0,k.useState)(!1),i=(0,k.useRef)(null),a=(0,k.useRef)(null),o=(0,k.useRef)([]),s=U.findIndex(t=>t.id===e);(0,k.useEffect)(()=>{if(!n)return;Qe(()=>void 0),o.current[s]?.focus();let e=e=>{i.current?.contains(e.target)||r(!1)};return document.addEventListener(`pointerdown`,e),()=>document.removeEventListener(`pointerdown`,e)},[n,s]);let c=()=>{r(!1),a.current?.focus()},l=e=>{e===U[0].id?delete document.documentElement.dataset.theme:Qe(()=>document.documentElement.dataset.theme=e);try{localStorage.setItem(Xe,e)}catch{}let n=new URL(location.href);n.searchParams.has(`theme`)&&(n.searchParams.delete(`theme`),history.replaceState(history.state,``,n)),t(e),c()};return(0,F.jsxs)(`div`,{className:`site-theme`,ref:i,children:[(0,F.jsxs)(`button`,{className:`site-theme-toggle`,type:`button`,ref:a,"aria-label":`Theme: ${U[s].label}`,"aria-haspopup":`menu`,"aria-expanded":n,"aria-controls":`site-theme-list`,onClick:()=>r(!n),onKeyDown:e=>{(e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),r(!0))},children:[(0,F.jsx)(`span`,{className:`site-theme-dot`,"aria-hidden":`true`}),(0,F.jsx)(`span`,{className:`site-theme-label`,children:`Theme`})]}),(0,F.jsx)(`ul`,{className:`site-theme-list`,id:`site-theme-list`,role:`menu`,"aria-label":`Theme`,hidden:!n,style:n?void 0:{display:`none`},onKeyDown:e=>{let t=o.current.indexOf(document.activeElement),n=e.key===`ArrowDown`?(t+1)%W:e.key===`ArrowUp`?(t-1+W)%W:e.key===`Home`?0:e.key===`End`?W-1:-1;n>=0?(e.preventDefault(),o.current[n]?.focus()):e.key===`Escape`?(e.stopPropagation(),c()):e.key===`Tab`&&r(!1)},children:U.map((t,n)=>(0,F.jsx)(`li`,{role:`none`,children:(0,F.jsxs)(`button`,{type:`button`,role:`menuitemradio`,"aria-checked":t.id===e,tabIndex:-1,ref:e=>void(o.current[n]=e),onClick:()=>l(t.id),children:[(0,F.jsx)(`span`,{className:`site-theme-dot`,style:{"--dot":t.dot},"aria-hidden":`true`}),t.label,(0,F.jsx)(et,{})]})},t.id))})]})}var W=U.length,rt={"--night-line":`var(--line-strong)`,"--night-2":`var(--surface)`,"--night-ink":`var(--ink)`,"--bright":`var(--accent)`,"--shadow-night":`var(--shadow)`};function it(){let[e,t]=(0,k.useState)(Ze);return(0,F.jsxs)(`div`,{style:{...rt,display:`flex`,gap:6,flex:`none`},children:[(0,F.jsx)(nt,{theme:e,onTheme:t}),(0,F.jsx)(`div`,{style:{"--dot":`var(--line-strong)`},children:(0,F.jsx)(at,{})})]})}function at(){return(0,F.jsx)(`div`,{className:`site-theme site-pictures`,children:(0,F.jsxs)(`button`,{className:`site-theme-toggle`,type:`button`,"data-pictures":``,"aria-label":`Picture theme`,"aria-haspopup":`menu`,"aria-expanded":`false`,onClick:e=>void(e.currentTarget.dataset.pressed=``),children:[(0,F.jsx)(`span`,{className:`site-picture-dot`,"aria-hidden":`true`}),(0,F.jsx)(`span`,{className:`site-theme-label`,children:`Pictures`})]})})}var ot=`/grooph/assets/site-icons.v1.svg`,st=[{href:`https://ryanjosephkamp.github.io/`,name:`Ryan Kamp’s website`,title:`Website`,icon:`globe`},{href:`https://github.com/ryanjosephkamp/`,name:`Ryan Kamp on GitHub`,title:`GitHub`,icon:`github`},{href:`https://www.linkedin.com/in/rjk1999`,name:`Ryan Kamp on LinkedIn`,title:`LinkedIn`,icon:`linkedin`},{href:`https://x.com/ryanjosephkamp`,name:`Ryan Kamp on X`,title:`X`,icon:`x`},{href:`https://m.youtube.com/@RyanJosephKamp`,name:`Ryan Kamp on YouTube`,title:`YouTube`,icon:`youtube`}];function ct(){return(0,F.jsx)(`footer`,{className:`site-footer land-foot`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`a`,{className:`site-logo`,href:`#/`,children:[(0,F.jsx)($e,{}),(0,F.jsx)(`span`,{children:`grooph`})]}),(0,F.jsx)(`p`,{children:`Workflow diagrams in 2D and 3D. Graphs live in this browser on this device. Nothing is sent anywhere.`})]}),(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h2`,{children:`Use it`}),(0,F.jsxs)(`ul`,{children:[(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`#/templates`,children:`Templates`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${H}quickstart/`,children:`Quickstart`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${H}field-guide/`,children:`Field guide`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:H,children:`Docs`})})]})]}),(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h2`,{children:`Help and contact`}),(0,F.jsxs)(`ul`,{children:[(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${V}/issues`,rel:`noopener`,children:`Report a bug or suggest a feature`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${H}community/`,children:`Community diagrams`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:V,rel:`noopener`,children:`Source on GitHub`})})]})]}),(0,F.jsxs)(`div`,{className:`site-credit`,children:[(0,F.jsxs)(`p`,{className:`site-made`,children:[`Made by `,(0,F.jsx)(`a`,{href:`https://ryanjosephkamp.github.io/`,children:`Ryan Kamp`})]}),(0,F.jsx)(`ul`,{className:`site-social`,"aria-label":`Ryan Kamp online`,children:st.map(e=>(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:e.href,"aria-label":e.name,title:e.title,children:(0,F.jsx)(`svg`,{"aria-hidden":`true`,children:(0,F.jsx)(`use`,{href:`${ot}#${e.icon}`})})})},e.href))}),(0,F.jsxs)(`a`,{className:`site-sponsor`,href:`https://github.com/sponsors/ryanjosephkamp`,children:[(0,F.jsx)(`svg`,{"aria-hidden":`true`,children:(0,F.jsx)(`use`,{href:`${ot}#heart`})}),`Sponsor on GitHub`]})]}),(0,F.jsxs)(`p`,{className:`site-fine`,children:[`Every feature is free; sponsorship is optional and never unlocks anything. MIT license, © 2026 Ryan Kamp. This site uses no cookies, analytics or third-party requests. Fonts: Atkinson Hyperlegible Next and Mono, SIL Open Font License.`,` `,(0,F.jsx)(`a`,{href:`${V}#license-and-author`,rel:`noopener`,children:`Credits`})]})]})})}var G=(e,t,n=!1)=>`#/templates/${e}/${encodeURIComponent(t)}${n?`/use`:``}`,lt=[];function ut(){let[e,t]=(0,k.useState)(w);return(0,k.useEffect)(()=>{if(e!==void 0)return;let n=!0;return S().then(e=>n&&t(e),()=>n&&t(null)),()=>{n=!1}},[]),e}function dt(){let[e,t]=(0,k.useState)(null),[n,r]=(0,k.useState)(He);(0,k.useEffect)(()=>Ue(n),[n]),(0,k.useEffect)(()=>{let e=!0;return We().then(n=>e&&t(n)),()=>{e=!1}},[]);let i=ut(),a=i??lt,o=(0,k.useMemo)(()=>[...e??[],...a],[e,a]),s=(0,k.useMemo)(()=>Be(o),[o]),c=(0,k.useMemo)(()=>ze(e??[],n),[e,n]),l=(0,k.useMemo)(()=>ze(a,n),[a,n]),u=c.length+l.length,[d,f]=(0,k.useState)(b),p=(0,k.useRef)(null),m=()=>{f(`asking`),D().then(f,()=>f(null)).then(()=>setTimeout(()=>p.current?.focus()))},h=o.length,g=Ne(n),[_,v]=(0,k.useState)(!1),y=_?s:s.filter((e,t)=>t<ft||n.tags.includes(e)),S=(e,t)=>r(n=>({...n,[e]:t}));return i===void 0?(0,F.jsx)(`div`,{className:`loading`,children:`Opening…`}):(0,F.jsxs)(`div`,{className:`library templates`,children:[(0,F.jsxs)(`header`,{className:`screen-head`,children:[(0,F.jsx)(`a`,{className:`icon-btn`,href:`#/`,"aria-label":`All graphs`,children:(0,F.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,F.jsx)(`path`,{d:`M15 5 8 12l7 7`})})}),(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h1`,{children:`Templates`}),(0,F.jsx)(`p`,{className:`muted`,children:`Start a graph from one, or insert one into a graph you have open.`})]}),(0,F.jsx)(`div`,{style:{marginLeft:`auto`},children:(0,F.jsx)(it,{})})]}),(0,F.jsxs)(`div`,{className:`browse`,role:`search`,children:[(0,F.jsxs)(`div`,{className:`browse-row`,children:[(0,F.jsx)(`input`,{type:`search`,className:`input browse-q`,placeholder:`Search templates`,"aria-label":`Search templates`,value:n.q,onChange:e=>S(`q`,e.target.value),autoComplete:`off`,enterKeyHint:`search`}),(0,F.jsxs)(`button`,{type:`button`,className:`btn browse-toggle${g>0?` is-on`:``}`,"aria-expanded":n.open,"aria-controls":`browse-filters`,onClick:()=>S(`open`,!n.open),children:[`Filters`,g>0?(0,F.jsx)(`span`,{className:`browse-count`,children:g}):null]})]}),(0,F.jsxs)(`div`,{className:`browse-row browse-meta`,children:[(0,F.jsx)(`span`,{className:`muted browse-shown`,role:`status`,"aria-live":`polite`,children:u===h?`${h} templates`:`${u} of ${h}`}),(0,F.jsxs)(`label`,{className:`browse-sort`,children:[(0,F.jsx)(`span`,{className:`muted`,children:`Sort`}),(0,F.jsx)(`select`,{className:`input`,"aria-label":`Sort by`,value:n.sort,onChange:e=>S(`sort`,e.target.value),children:Object.keys(R).map(e=>(0,F.jsx)(`option`,{value:e,children:R[e]},e))})]}),Pe(n)?null:(0,F.jsx)(`button`,{type:`button`,className:`link browse-clear`,onClick:()=>r({...L,open:n.open}),children:`Clear`})]}),n.open?(0,F.jsxs)(`div`,{className:`browse-filters`,id:`browse-filters`,children:[(0,F.jsx)(K,{label:`Kind`,options:[`graph`,`fragment`],text:e=>e===`graph`?`Whole graph`:`Fragment`,on:n.kind,onToggle:e=>S(`kind`,z(n.kind,e))}),[`cost`,`speed`,`rigor`].map(e=>(0,F.jsx)(K,{label:e[0].toUpperCase()+e.slice(1),options:te[e],text:e=>e[0].toUpperCase()+e.slice(1),title:t=>x[e][t],level:t=>C[e][t],on:n[e],onToggle:t=>S(e,z(n[e],t))},e)),s.length>0?(0,F.jsx)(K,{label:`Tags`,options:y,text:e=>e,on:n.tags,onToggle:e=>S(`tags`,z(n.tags,e)),more:s.length>ft?(0,F.jsx)(`button`,{type:`button`,className:`chip chip-small chip-more`,"aria-expanded":_,onClick:()=>v(e=>!e),children:_?`Fewer tags`:`All ${s.length} tags`}):null}):null]}):null]}),e&&e.length>0&&c.length>0?(0,F.jsxs)(`section`,{"aria-labelledby":`yours-title`,children:[(0,F.jsx)(`h2`,{className:`list-title`,id:`yours-title`,children:`Yours`}),(0,F.jsx)(q,{source:`yours`,docs:c})]}):null,l.length>0?(0,F.jsxs)(`section`,{"aria-labelledby":`builtin-title`,children:[(0,F.jsx)(`h2`,{className:`list-title`,id:`builtin-title`,children:`Built-in`}),(0,F.jsx)(q,{source:`built-in`,docs:l})]}):null,i===null?(0,F.jsx)(`p`,{className:`muted templates-hint`,role:`status`,children:`The built-in templates could not be fetched. They need a connection the first time. Yours are listed.`}):null,u===0?(0,F.jsxs)(`p`,{className:`muted templates-hint browse-none`,role:`status`,children:[`No template matches.`,` `,(0,F.jsx)(`button`,{type:`button`,className:`link`,onClick:()=>r({...L,open:n.open}),children:`Clear the search and filters`})]}):null,(0,F.jsxs)(`section`,{"aria-labelledby":`plans-title`,children:[(0,F.jsx)(`h2`,{className:`list-title`,id:`plans-title`,children:`Plans`}),(0,F.jsx)(`p`,{className:`muted templates-hint`,children:E}),(0,F.jsx)(`p`,{className:`muted templates-hint`,role:`status`,tabIndex:-1,ref:p,children:d&&d!==`asking`?`${d.length} plans, listed whole: the search and filters above are for the templates.`:(0,F.jsxs)(F.Fragment,{children:[d===null?`The plans could not be fetched. They need a connection the first time. `:null,(0,F.jsx)(`button`,{type:`button`,className:`btn`,disabled:d===`asking`,onClick:m,children:d===null?`Try again`:d===`asking`?`Fetching the plans…`:`Show the plans`})]})}),d&&d!==`asking`?(0,F.jsx)(q,{source:`plan`,docs:d}):null]}),e&&e.length===0?(0,F.jsxs)(`p`,{className:`muted templates-hint`,children:[(0,F.jsx)(`strong`,{children:`Yours`}),` is empty. Save a graph, or some of its nodes, as a template from the Graph panel of the editor, or import a template file from the graph list.`]}):null]})}var ft=8;function K({label:e,options:t,text:n,title:r,level:i,on:a,onToggle:o,more:s}){return(0,F.jsxs)(`div`,{className:`browse-group`,role:`group`,"aria-label":e,children:[(0,F.jsx)(`span`,{className:`browse-group-label`,children:e}),(0,F.jsxs)(`div`,{className:`chips browse-chips`,children:[t.map(e=>(0,F.jsxs)(`button`,{type:`button`,className:`chip chip-small${a.includes(e)?` chip-on`:``}`,"aria-pressed":a.includes(e),"aria-label":r?r(e):void 0,onClick:()=>o(e),children:[i?(0,F.jsxs)(`span`,{className:`meter`,"aria-hidden":`true`,"data-level":i(e),children:[(0,F.jsx)(`i`,{}),(0,F.jsx)(`i`,{}),(0,F.jsx)(`i`,{})]}):null,n(e)]},e)),s]})]})}function q({source:e,docs:t}){return(0,F.jsx)(`ul`,{className:`template-list`,"aria-label":e===`yours`?`Your templates`:e===`plan`?`Plan templates`:`Built-in templates`,children:t.map(t=>{let n=t.template;return(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{className:`template-row${je(t)?` has-long-glyph`:``}`,href:G(e,t.id),"data-template":t.id,children:[(0,F.jsx)(I,{doc:t,className:`template-glyph`,decorative:!0}),(0,F.jsxs)(`span`,{className:`template-text`,children:[(0,F.jsxs)(`span`,{className:`template-title`,children:[n.title,n.kind===`fragment`?(0,F.jsx)(`span`,{className:`badge badge-quiet`,children:`fragment`}):null,e===`plan`?(0,F.jsx)(`span`,{className:`badge badge-quiet`,children:`plan`}):null]}),(0,F.jsxs)(`span`,{className:`template-when`,children:[(0,F.jsx)(`span`,{className:`template-label`,children:`Use when`}),` `,n.whenToUse]}),e===`plan`?null:(0,F.jsx)(pt,{profile:n.profile})]})]})},t.id)})})}function pt({profile:e}){return(0,F.jsx)(`ul`,{className:`template-meters`,"aria-label":`Profile`,children:[`cost`,`speed`,`rigor`].map(t=>(0,F.jsxs)(`li`,{className:`template-meter`,"aria-label":x[t][e[t]],title:x[t][e[t]],children:[(0,F.jsxs)(`span`,{className:`meter`,"aria-hidden":`true`,"data-level":C[t][e[t]],children:[(0,F.jsx)(`i`,{}),(0,F.jsx)(`i`,{}),(0,F.jsx)(`i`,{})]}),(0,F.jsx)(`span`,{className:`template-meter-word`,"aria-hidden":`true`,children:t})]},t))})}function mt({id:e,children:t}){let[i,a]=(0,k.useState)(`idle`),[o,s]=(0,k.useState)(null);return o?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`div`,{className:`land-3d`,children:(0,F.jsx)(o.stage.Stage3,{doc:o.doc,kind:`panes`,wide:innerWidth,of:{},play:!0})}),(0,F.jsxs)(`figcaption`,{className:`muted`,children:[`The same template in 3D, as every canvas in the app can show it: drag to turn it.`,` `,(0,F.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>s(null),children:`Back to the picture`})]})]}):(0,F.jsxs)(F.Fragment,{children:[t,(0,F.jsxs)(`p`,{className:`land-play`,children:[(0,F.jsxs)(`button`,{type:`button`,className:`btn`,disabled:i===`loading`,onClick:async()=>{a(`loading`);try{let[t,i]=await Promise.all([n(`graph-stage`,()=>r(()=>import(`./graph-stage-CwNePnN4.js`),__vite__mapDeps([3,4,1,5]))),S()]),o=i.find(t=>t.id===e);if(!o)throw Error(`no built-in template ${e}`);s({stage:t,doc:o}),a(`idle`)}catch{a(`failed`)}},children:[(0,F.jsx)(`span`,{"aria-hidden":`true`,children:`▶`}),` See it in 3D`]}),(0,F.jsx)(`span`,{className:`muted`,role:`status`,children:i===`loading`?`Fetching the 3D view…`:i===`failed`?`The 3D view could not be fetched. It needs a network the first time.`:``})]})]})}var J=`review-gate`,ht=`/grooph-design a builder and a critic that loop until the checkout tests pass, and ask me before merging`,gt,_t=()=>n(`front`,()=>r(()=>import(`./front-OxfL-OaD.js`),[])).then(e=>gt=e),vt=600;function yt(){let[e,t]=(0,k.useState)(gt),[n,r]=(0,k.useState)(!1);return(0,k.useEffect)(()=>{if(e)return;let n=!0,i=setTimeout(()=>n&&r(!0),vt);return _t().then(e=>n&&t(e),()=>n&&r(!0)),()=>{n=!1,clearTimeout(i)}},[]),e??(n?null:void 0)}var bt=[`No account`,`Built for a phone's screen`,`Opens offline after a first visit`,`Graphs stay on your device`],xt=`${H}field-guide/poster.svg`;function St({device:e}){let[t,n]=(0,k.useState)(!0),r=yt();return r===void 0?(0,F.jsx)(`div`,{className:`loading`,children:`Opening…`}):(0,F.jsxs)(`div`,{className:`land`,children:[(0,F.jsx)(tt,{}),(0,F.jsxs)(`main`,{children:[(0,F.jsx)(`section`,{className:`land-hero`,"aria-label":`What grooph is`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsxs)(`div`,{className:`land-hero-title`,children:[(0,F.jsx)(`p`,{className:`land-chip`,children:`Free and open source`}),(0,F.jsxs)(`h1`,{className:`land-headline`,children:[`Workflow diagrams `,(0,F.jsx)(`span`,{children:`in 2D and 3D.`})]})]}),(0,F.jsxs)(`div`,{className:`land-hero-text`,children:[(0,F.jsx)(`p`,{className:`land-lede`,children:`Draw who does the work, who checks it, where a person decides, and when it stops. Draw it yourself, ask an AI agent to draw it, or both. grooph checks the diagram and draws it, and a link from the app, the command line, or your agent opens it on any device.`}),(0,F.jsxs)(`div`,{className:`land-cta`,children:[(0,F.jsx)(`a`,{className:`btn btn-primary`,href:G(`built-in`,J),children:`Open the template`}),(0,F.jsx)(`a`,{className:`btn`,href:`#/templates`,children:`See all twenty`})]}),(0,F.jsx)(`ul`,{className:`land-promise`,"aria-label":`About the app`,children:bt.map(e=>(0,F.jsxs)(`li`,{children:[(0,F.jsx)(et,{}),e]},e))})]}),r?(0,F.jsx)(`figure`,{className:`land-figure`,children:(0,F.jsxs)(mt,{id:J,children:[(0,F.jsx)(`div`,{className:`land-picture`,role:`img`,"aria-label":`The review gate template as a diagram: a builder, a critic, a human merge approval, and a stop, in one loop of at most four rounds`,dangerouslySetInnerHTML:{__html:r.HERO_SVG}}),(0,F.jsxs)(`figcaption`,{className:`muted`,children:[`The `,(0,F.jsx)(`a`,{href:G(`built-in`,J),children:`review gate`}),` template, drawn by grooph.`]})]})}):null]})}),(0,F.jsx)(`section`,{className:`land-section`,children:(0,F.jsx)(`div`,{className:`site-wrap`,children:(0,F.jsxs)(`ul`,{className:`land-claims`,"aria-label":`What grooph does`,children:[(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`strong`,{children:`Draw it, or ask for it.`}),` Build a diagram in the app, on a phone or a computer, start from one of twenty templates, or ask an agent in Claude Code or a chat app to draw it. Either way it is one small JSON file, so a person and an agent can take turns on it.`]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`strong`,{children:`See it in 2D and 3D.`}),` The same diagram as a flat picture, as an outline to read, or as one of five 3D scenes you can turn and step through, one edge at a time. Six themes, all but one in light and dark.`]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`strong`,{children:`Checked, shared, kept.`}),` grooph checks that a diagram is well formed, and gives advice on whether every loop says when it stops. A link opens it on any device, one line of HTML puts it on a page, and one file keeps it offline.`]})]})})}),(0,F.jsx)(`section`,{className:`land-section land-alt land-start`,"aria-labelledby":`land-start-title`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsx)(`h2`,{id:`land-start-title`,className:`land-h2`,children:`Two ways to start`}),(0,F.jsxs)(`div`,{className:`land-ways`,children:[(0,F.jsxs)(`div`,{className:`land-way`,children:[(0,F.jsx)(`h3`,{children:`Ask your agent`}),(0,F.jsxs)(`p`,{children:[`In Claude Code, with grooph `,(0,F.jsx)(`a`,{href:`${H}quickstart/`,children:`installed`}),`, describe the work. The session proposes one to three diagrams, sends a link to compare them on your phone, and saves the one you pick.`]}),(0,F.jsx)(wt,{text:ht})]}),(0,F.jsxs)(`div`,{className:`land-way`,children:[(0,F.jsx)(`h3`,{children:`Open a template`}),(0,F.jsx)(`p`,{children:`Twenty templates of agent workflows, and four plans for people. Open one, fill in its blanks, and change it to fit.`}),(0,F.jsxs)(`div`,{className:`land-way-actions`,children:[(0,F.jsx)(`a`,{className:`btn btn-primary`,href:G(`built-in`,J),children:`Open the review gate`}),(0,F.jsx)(`a`,{className:`btn`,href:`#/templates`,children:`Browse all`})]})]})]}),(0,F.jsx)(`div`,{className:`land-device`,children:e??(0,F.jsx)(Ct,{})})]})}),(0,F.jsx)(`section`,{className:`land-section land-strip`,"aria-labelledby":`land-strip-title`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsx)(`h2`,{id:`land-strip-title`,className:`land-h2`,children:`Twenty shapes`}),r?(0,F.jsx)(`ul`,{className:`land-strip-list`,"aria-label":`Templates`,children:r.TILES.map(e=>(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{className:`land-tile`,href:G(`built-in`,e.id),children:[(0,F.jsx)(Me,{svg:e.glyph,long:e.long,className:`land-tile-glyph${e.long?` is-wide`:``}`,decorative:!0}),(0,F.jsx)(`span`,{className:`land-tile-title`,children:e.title})]})},e.id))}):null,(0,F.jsxs)(`div`,{className:`land-poster`,children:[t?(0,F.jsx)(`a`,{href:xt,"aria-label":`Open the poster of the twenty loop shapes`,children:(0,F.jsx)(`img`,{src:xt,width:`1200`,height:`800`,loading:`lazy`,decoding:`async`,alt:`The poster: twenty loop shapes, each drawn with its name and when to reach for it`,onError:()=>n(!1)})}):null,(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h3`,{children:`Twenty shapes on one page`}),(0,F.jsxs)(`p`,{className:`land-guide-line`,children:[`All twenty, with when to use each: `,(0,F.jsx)(`a`,{href:`${H}field-guide/`,children:`the field guide`}),`, and`,` `,(0,F.jsx)(`a`,{href:xt,children:`a poster of the shapes`}),`.`]})]})]})]})}),(0,F.jsx)(`section`,{className:`land-section land-alt land-more`,"aria-labelledby":`land-more-title`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsx)(`h2`,{id:`land-more-title`,className:`land-h2`,children:`More than a picture`}),(0,F.jsxs)(`ul`,{className:`land-more-list`,children:[(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`h3`,{children:`Put a diagram on any page`}),(0,F.jsxs)(`p`,{children:[`One line of HTML shows a live diagram a reader can pan, zoom, and tap. `,(0,F.jsx)(`a`,{href:`${H}exports/`,children:`How to embed`}),`.`]})]}),(0,F.jsxs)(`li`,{children:[(0,F.jsxs)(`h3`,{children:[`See what is running `,(0,F.jsx)(`span`,{className:`badge badge-quiet`,children:`Experimental`})]}),(0,F.jsxs)(`p`,{children:[`A hook records when each session and subagent starts and stops, and nothing they say. A screen draws it live.`,` `,(0,F.jsx)(`a`,{href:`${H}subagents/`,children:`How it works`}),`.`]})]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`h3`,{children:`Map work across sessions`}),(0,F.jsxs)(`p`,{children:[`Sessions, the people they work with, and what carries work between them, drawn and checked.`,` `,(0,F.jsx)(`a`,{href:`${H}operation-map/`,children:`Operation maps`}),`.`]})]})]})]})}),(0,F.jsx)(`section`,{className:`land-section land-honest`,"aria-labelledby":`land-honest-title`,children:(0,F.jsxs)(`div`,{className:`site-wrap`,children:[(0,F.jsxs)(`h2`,{id:`land-honest-title`,className:`land-h2`,children:[`A picture, `,(0,F.jsx)(`mark`,{children:`not an instruction`})]}),(0,F.jsxs)(`p`,{children:[`grooph runs no agent, calls no model, and needs no hosted service, and nothing in it makes a model follow a diagram. It began as a way to compile diagrams into prompt packages for Claude Code. In two paired comparisons, 51 runs on small tasks, the same design written as a plain prompt scored the same as the compiled one, and in the second study it cost less every time. So grooph now draws, and the rest is experimental.`,` `,(0,F.jsx)(`a`,{href:`${H}experiments/`,children:`What the experiments found`}),`.`]})]})})]}),(0,F.jsx)(ct,{})]})}function Ct(){return(0,F.jsx)(`p`,{className:`land-device-line`,children:(0,F.jsx)(`a`,{className:`btn`,href:`#/`,children:`Your graphs`})})}function wt({text:e}){let[t,n]=(0,k.useState)(null);return(0,F.jsxs)(`div`,{className:`copy-line`,children:[(0,F.jsx)(`code`,{className:`copy-line-text`,children:e}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-small`,"aria-label":`Copy the line for Claude Code`,onClick:async()=>n(await De(e)?`Copied. Paste it into Claude Code.`:`Could not copy. Select the line and copy it.`),children:`Copy`}),(0,F.jsx)(`span`,{className:`copy-line-said muted`,role:`status`,children:t})]})}function Tt({className:e=``}){let t=ve();if(!t)return null;let n=t.result===`granted`;return(0,F.jsxs)(`div`,{className:`persist-notice ${n?`is-granted`:`is-denied`} ${e}`,role:`status`,children:[(0,F.jsx)(`p`,{children:n?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`strong`,{children:`Storage kept.`}),` This browser agreed not to clear grooph’s data to free space.`]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`strong`,{children:`Storage not guaranteed.`}),` This browser may clear grooph’s data when space runs low. Keep backups: Export, then Download graph.`]})}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-small`,onClick:ge,children:`Got it`})]})}function Et({toast:e,onDone:t}){return(0,k.useEffect)(()=>{if(!e)return;let n=window.setTimeout(t,6e3);return()=>window.clearTimeout(n)},[e,t]),e?(0,F.jsxs)(`div`,{className:`toast`,role:`status`,children:[(0,F.jsx)(`span`,{children:e.text}),e.undo?(0,F.jsx)(`button`,{type:`button`,className:`toast-action`,onClick:()=>{e.undo(),t()},children:`Undo`}):null]},e.id):null}var Dt=e=>{let t=Date.now()-e;return t<6e4?`just now`:t<36e5?`${Math.floor(t/6e4)} min ago`:t<864e5?`${Math.floor(t/36e5)} h ago`:new Date(e).toLocaleDateString()},Ot=()=>n(`Import`,()=>r(()=>import(`./Import-DF0XLxRI.js`),__vite__mapDeps([6,4,1,7,8,9]))),kt=e=>e?.scrollIntoView({block:`nearest`});function At({runs:e}){return(0,F.jsx)(`ul`,{className:`run-rows`,"aria-label":`Runs`,children:e.map(e=>{let t=u(e.bundle.notes,e.bundle.working);return(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{className:`run-row`,href:re(e.key),children:[(0,F.jsxs)(`span`,{className:`run-row-name`,children:[`Run `,(0,F.jsx)(`span`,{className:`mono`,children:e.run})]}),(0,F.jsx)(`span`,{className:`run-row-meta`,children:s(t)})]})},e.key)})})}var Y=(e,t)=>`${e} ${t}${e===1?``:`s`}`;function jt({open:e}){let[t,n]=(0,k.useState)(null),[r,i]=(0,k.useState)([]),[a,o]=(0,k.useState)(!0),[s,c]=(0,k.useState)(null),[l,u]=(0,k.useState)(null),[d,f]=(0,k.useState)(null),[p,m]=(0,k.useState)(null),[g,_]=(0,k.useState)(null),[y,b]=(0,k.useState)(null),x=(0,k.useRef)(null),[S,ee]=(0,k.useState)(null),C=(0,k.useCallback)(async()=>{n(await ae()),i(await ce()),o((await O()).persistent)},[]);(0,k.useEffect)(()=>void C(),[C]);let w={open:e,route:h,problem:c,offer:_,review:b,close:()=>ee(null)},T=(e,t)=>void Ot().then(t,()=>c({name:e,issues:[],what:`The part of grooph that opens it could not be fetched. It needs a connection the first time: try again when you have one.`})),te=e=>{e&&T(e.name,async t=>t.importText(await e.text(),e,w))};(0,k.useEffect)(()=>{let e=e=>{let t=e.clipboardData?.getData(`text/plain`);t&&e.target instanceof Element&&!e.target.closest(`input, textarea, select`)&&Ot().then(e=>e.openPasted(t,w,!0),()=>void 0)};return window.addEventListener(`paste`,e),()=>window.removeEventListener(`paste`,e)});let E=async(e,t)=>{let n=await Ke(e,{replace:t});if(`exists`in n){_(e=>e&&{...e,exists:n.exists});return}_(null),location.hash=G(`yours`,n.saved.id)},D=(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`library-actions`,children:[(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary btn-large`,onClick:async()=>{e((await le()).key,!0)},children:`New graph`}),(0,F.jsx)(`a`,{className:`btn btn-large`,href:`#/templates`,children:`Templates`}),(0,F.jsxs)(`label`,{className:`btn btn-large file-btn`,children:[`Import a file`,(0,F.jsx)(`input`,{ref:x,type:`file`,accept:`.json,application/json,.mmd,.mermaid,.md,text/markdown,text/plain`,onChange:e=>{te(e.target.files?.[0]),e.target.value=``}})]}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-large`,onClick:()=>T(`what you paste`,e=>ee(t=>t?null:e.PasteBox)),children:`Paste a document`}),(0,F.jsxs)(`button`,{type:`button`,className:`btn btn-large`,onClick:async()=>{let e=await le();location.hash=`#/g/${encodeURIComponent(e.key)}?sketch`},children:[`Sketch it `,(0,F.jsx)(`span`,{className:`badge badge-quiet`,children:`Experimental`})]})]}),S?(0,F.jsx)(S,{host:w}):null,a?null:(0,F.jsx)(`p`,{className:`notice`,role:`status`,children:`This browser is not letting grooph store anything, so graphs last until the tab closes. Download them from Export to keep them.`}),(0,F.jsx)(Tt,{}),g?(0,F.jsxs)(`div`,{className:`offer`,role:`alert`,ref:kt,children:[(0,F.jsxs)(`p`,{children:[(0,F.jsxs)(`strong`,{children:[g.name,` is a template:`]}),` `,g.doc.template.title,g.doc.template.kind===`fragment`?` (a fragment)`:``,`.`,` `,g.refusal?`It carries errors, so it cannot go into Yours; grooph template add refuses it too. Open it as a graph to fix it.`:`Add it to Yours to use it from Templates.`]}),g.refusal?(0,F.jsx)(`pre`,{className:`issue-lines`,children:g.refusal.issues.map(v).join(`
|
|
4
|
+
`)}):null,g.exists?(0,F.jsxs)(`p`,{className:`field-hint`,children:[`Yours already has a template with the id `,(0,F.jsx)(`span`,{className:`mono`,children:g.doc.id}),` (version `,g.exists.version,`). Replacing it makes version `,Math.max(g.exists.version,g.doc.version)+1,`.`]}):null,(0,F.jsxs)(`div`,{className:`offer-actions`,children:[g.refusal?null:g.exists?(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>void E(g.doc,!0),children:`Replace yours`}):(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>void E(g.doc,!1),children:`Add to Yours`}),(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:async()=>{let t=await ue(g.doc);_(null),e(t.key)},children:`Open it as a graph`}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-quiet`,onClick:()=>_(null),children:`Cancel`})]})]}):null,y?(0,F.jsxs)(`div`,{className:`offer`,role:`status`,ref:kt,children:[(0,F.jsxs)(`p`,{children:[(0,F.jsxs)(`strong`,{children:[`Read `,y.name,` as Mermaid:`]}),` `,Y(y.doc.nodes.length,`step`),`, `,Y(y.doc.edges.length,`edge`),` and`,` `,Y(y.doc.loops.length,`loop`),`.`,` `,y.guesses.length>0?`What grooph guessed, to check once it is open:`:`Nothing had to be guessed.`]}),y.guesses.length>0?(0,F.jsx)(`ul`,{className:`field-hint`,style:{margin:0,paddingLeft:`1.2em`},children:y.guesses.map((e,t)=>(0,F.jsx)(`li`,{children:e},t))}):null,(0,F.jsxs)(`div`,{className:`offer-actions`,children:[(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:async()=>{let t=await ue(y.doc);b(null),e(t.key)},children:`Open it`}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-quiet`,onClick:()=>b(null),children:`Cancel`})]})]}):null,s?(0,F.jsxs)(`div`,{className:`refusal`,role:`alert`,ref:kt,children:[(0,F.jsxs)(`p`,{children:[(0,F.jsxs)(`strong`,{children:[`Could not import `,s.name,`.`]}),` `,s.what??`It is not a graph document grooph can open.`]}),(0,F.jsx)(`pre`,{className:`issue-lines`,children:s.issues.map(v).join(`
|
|
5
|
+
`)}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-small`,onClick:()=>c(null),children:`Dismiss`})]}):null]});return t===null?null:t.length===0&&r.length===0?(0,F.jsx)(St,{device:(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`library-empty land-empty`,children:[(0,F.jsx)(`p`,{children:`No graphs on this device yet.`}),(0,F.jsxs)(`p`,{className:`muted`,children:[`Start one here, or import a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph.json`}),` graph, a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-proposals.json`}),` set, a`,` `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-run.json`}),` run, a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-map.json`}),` operation map, or Mermaid text.`]})]}),D]})}):(0,F.jsxs)(`div`,{className:`library`,children:[(0,F.jsxs)(`header`,{className:`library-head`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h1`,{className:`wordmark`,children:`grooph`}),(0,F.jsx)(`p`,{className:`muted`,children:`Workflow diagrams in 2D and 3D. Draw, check, and share.`})]}),(0,F.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,alignItems:`flex-end`,gap:4},children:[(0,F.jsx)(it,{}),(0,F.jsx)(`a`,{className:`library-about`,href:`#/about`,children:`What is grooph?`})]})]}),D,t.length===0?(0,F.jsxs)(`div`,{className:`library-empty`,children:[(0,F.jsx)(`p`,{children:`No graphs on this device yet.`}),(0,F.jsxs)(`p`,{className:`muted`,children:[`Start a new one, or import a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph.json`}),` graph, a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-proposals.json`}),` set, a`,` `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-run.json`}),` run, a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-map.json`}),` operation map, or Mermaid text.`]})]}):(0,F.jsx)(`ul`,{className:`graph-list`,"aria-label":`Graphs on this device`,children:t.map(t=>(0,F.jsxs)(`li`,{className:`graph-row`,children:[d?.key===t.key?(0,F.jsx)(Mt,{record:t,name:d.name,onName:e=>f({key:t.key,name:e}),onCancel:()=>f(null),onSave:async e=>{await se(t.key,d.name,{keepId:e}),f(null),await C()}}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`button`,{type:`button`,className:`graph-open${je(t.doc)?` has-long-glyph`:``}`,onClick:()=>e(t.key),children:[(0,F.jsx)(I,{doc:t.doc,className:`graph-glyph`,decorative:!0}),(0,F.jsxs)(`span`,{className:`graph-text`,children:[(0,F.jsx)(`span`,{className:`graph-name`,children:t.doc.name||`Untitled`}),(0,F.jsxs)(`span`,{className:`graph-meta`,children:[(0,F.jsx)(`span`,{className:`mono`,children:t.doc.id}),` · `,t.doc.nodes.length,` node`,t.doc.nodes.length===1?``:`s`,` ·`,` `,t.doc.loops.length,` loop`,t.doc.loops.length===1?``:`s`,` · `,Dt(t.updatedAt)]})]})]}),(0,F.jsx)(`button`,{type:`button`,className:`icon-btn`,"aria-label":`Actions for ${t.doc.name}`,"aria-expanded":l===t.key,onClick:()=>{u(l===t.key?null:t.key),m(null)},children:(0,F.jsxs)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:[(0,F.jsx)(`circle`,{cx:`5`,cy:`12`,r:`1.6`}),(0,F.jsx)(`circle`,{cx:`12`,cy:`12`,r:`1.6`}),(0,F.jsx)(`circle`,{cx:`19`,cy:`12`,r:`1.6`})]})})]}),r.some(e=>e.graphId===t.doc.id)?(0,F.jsx)(At,{runs:r.filter(e=>e.graphId===t.doc.id)}):null,l===t.key&&d?.key!==t.key?(0,F.jsxs)(`div`,{className:`row-actions`,children:[(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>{f({key:t.key,name:t.doc.name}),u(null)},children:`Rename`}),(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:async()=>{await oe(t.key),u(null),await C()},children:`Duplicate`}),p===t.key?(0,F.jsx)(`button`,{type:`button`,className:`btn btn-danger`,onClick:async()=>{await ie(t.key),u(null),m(null),await C()},children:`Delete for good`}):(0,F.jsx)(`button`,{type:`button`,className:`btn btn-danger-text`,onClick:()=>m(t.key),children:`Delete`})]}):null]},t.key))}),r.some(e=>!t.some(t=>t.doc.id===e.graphId))?(0,F.jsxs)(`section`,{className:`orphan-runs`,"aria-label":`Runs of graphs not on this device`,children:[(0,F.jsx)(`h2`,{className:`list-title`,children:`Runs of graphs not on this device`}),(0,F.jsx)(At,{runs:r.filter(e=>!t.some(t=>t.doc.id===e.graphId))})]}):null,(0,F.jsxs)(`footer`,{className:`library-foot muted`,children:[`Graphs live in this browser on this device. Nothing is sent anywhere.`,` `,(0,F.jsx)(`a`,{href:`https://github.com/ryanjosephkamp/grooph`,rel:`noopener`,children:`Source`})]})]})}function Mt(e){let{record:t}=e,n=o(t.doc,e.name).id,r=t.exported?.id,i=r!==void 0&&n!==r&&l(n,e.name);return(0,F.jsxs)(`form`,{className:`rename`,onSubmit:t=>{t.preventDefault(),e.onSave()},children:[(0,F.jsxs)(`div`,{className:`rename-row`,children:[(0,F.jsx)(`input`,{className:`input`,"aria-label":`Graph name`,value:e.name,autoFocus:!0,onChange:t=>e.onName(t.target.value)}),(0,F.jsx)(`button`,{type:`submit`,className:`btn btn-primary`,children:`Save`}),(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:e.onCancel,children:`Cancel`})]}),i?(0,F.jsx)(Nt,{exportedAs:r,nextId:n,onKeep:()=>e.onSave(r)}):null]})}function Nt({exportedAs:e,nextId:t,onKeep:n}){return(0,F.jsxs)(`div`,{className:`rename-warning`,role:`alert`,children:[(0,F.jsxs)(`p`,{children:[`This graph was exported from this device as `,(0,F.jsxs)(`span`,{className:`mono`,children:[`.grooph/`,e,`/`]}),`. With this name its id becomes`,` `,(0,F.jsx)(`span`,{className:`mono`,children:t}),`, so the next export writes a new package folder, `,(0,F.jsxs)(`span`,{className:`mono`,children:[`.grooph/`,t,`/`]}),`, beside the old one.`]}),(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:n,children:`Keep the old id`})]})}var Pt=i({App:()=>Ht,loadScreens:()=>Z,ready:()=>Bt}),Ft=(0,k.lazy)(()=>n(`EmbedApp`,()=>r(()=>import(`./EmbedApp-CTausQWY.js`),__vite__mapDeps([10,4,1,11]))).then(e=>({default:e.EmbedApp}))),X;function Z(){return n(`screens`,()=>r(()=>import(`./screens-CMMuexid.js`).then(e=>e.t),__vite__mapDeps([12,4,1,7,8,13]))).then(e=>X=e)}function It(e){return!Lt.has($(e).name)}var Lt=new Set([`library`,`about`,`templates`,`embed`]),Rt=e=>e.name===`template`&&e.source===`built-in`,zt=300;function Bt(e){let t=[];return It(e)&&t.push(Z()),new RegExp(fe).test(e)&&t.push(S()),new RegExp(de).test(e)&&t.push(Promise.race([_t(),new Promise(e=>setTimeout(e,zt))])),Promise.all(t.map(e=>e.catch(()=>void 0)))}function Q(e){try{return decodeURIComponent(e)}catch{return e}}function $(e){if(e===`#/run?live`)return{name:`live-run`};if(e===`#/live`)return{name:`live`};let t=/^#\/run\/([^?]+)$/.exec(e);if(t)return{name:`run`,key:Q(t[1])};if(e===`#/about`)return{name:`about`};if(e===`#/templates`)return{name:`templates`};if(e===`#/embed`||e.startsWith(`#/embed?`))return{name:`embed`};let n=/^#\/templates\/(built-in|yours|plan)\/([^/?]+)(\/use)?$/.exec(e);if(n)return{name:`template`,source:n[1],id:Q(n[2]),use:n[3]!==void 0};let r=/^#\/g\/([^/?]+)(\?new|\?sketch)?$/.exec(e);if(r)return{name:`graph`,key:Q(r[1]),fresh:r[2]===`?new`,sketch:r[2]===`?sketch`};if(e.startsWith(`#/open?`)){let t=/[?&]c=([^&]*)/.exec(e)?.[1];return{name:`open`,payload:c(e)??``,...t?{candidate:Q(t)}:{}}}return{name:`library`}}function Vt(e,t,n){let[r,i]=(0,k.useState)(()=>e()?`yes`:`no`),[a,o]=(0,k.useState)(null),s=(0,k.useRef)(n);return s.current=n,(0,k.useEffect)(()=>{if(r!==`no`)return;let e=!1;return t().then(()=>!e&&i(`yes`),()=>{e||(o(s.current),i(`failed`))}),()=>{e=!0}},[r]),(0,k.useEffect)(()=>{r===`failed`&&a!==n&&i(`no`)},[n]),r===`failed`&&a!==n?`no`:r}function Ht(){let[e,t]=(0,k.useState)(()=>$(location.hash));(0,k.useEffect)(()=>{let e=()=>{t($(location.hash)),window.scrollTo(0,0)};return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]);let n=Vt(()=>X!==void 0,Z,e),r=Vt(()=>w()!==void 0,S,e);(0,k.useEffect)(()=>void _t().catch(()=>void 0),[]);let i=e=>e===`failed`?(0,F.jsxs)(`div`,{className:`notfound`,children:[(0,F.jsx)(`p`,{children:`This screen could not be fetched. It needs a connection the first time.`}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>location.reload(),children:`Load the page again`}),(0,F.jsx)(`a`,{className:`btn`,href:`#/`,children:`Back to the library`})]}):(0,F.jsx)(`div`,{className:`loading`,children:`Opening…`});if(e.name===`about`)return(0,F.jsx)(St,{});if(Rt(e)&&r!==`yes`)return i(r);if(e.name===`templates`)return(0,F.jsx)(dt,{});if(e.name===`library`)return(0,F.jsx)(jt,{open:(e,t)=>{location.hash=`#/g/${encodeURIComponent(e)}${t?`?new`:``}`}});if(e.name===`embed`)return(0,F.jsx)(k.Suspense,{fallback:null,children:(0,F.jsx)(Ft,{})});if(!X)return i(n);let{EditorScreen:a,LiveRun:o,LiveSessions:s,OpenScreen:c,StoredRun:l,TemplateView:u,UseTemplate:d}=X;if(e.name===`graph`)return(0,F.jsx)(a,{graphKey:e.key,fresh:e.fresh,sketch:e.sketch},e.key);if(e.name===`open`)return(0,F.jsx)(c,{payload:e.payload,candidate:e.candidate});if(e.name===`live-run`)return(0,F.jsx)(o,{});if(e.name===`live`)return(0,F.jsx)(s,{});if(e.name===`run`)return(0,F.jsx)(l,{runKey:e.key},e.key);let f=`${e.source}/${e.id}`;return e.use?(0,F.jsx)(d,{source:e.source,id:e.id},f):(0,F.jsx)(u,{source:e.source,id:e.id},f)}export{Ht as App,Se as C,Te as S,De as _,G as a,Ce as b,qe as c,Ke as d,Je as f,be as g,xe as h,Et as i,Ge as l,Z as loadScreens,I as m,Nt as n,ut as o,Ye as p,Tt as r,Bt as ready,rt as s,Pt as t,We as u,Ee as v,ye as x,we as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,n,r,t as i}from"./index-B5ZkSF4L.js";import{D as a,E as o,G as s,H as c,K as l,W as u,d,dn as f,l as p,qt as m,rn as h,t as g}from"./share-DV3wsAsJ.js";var _=t(e(),1),v=i(),y={agent:`Agent`,"human-gate":`Human gate`,check:`Check`,merge:`Merge`,stop:`Stop`};function b({rows:e}){let t=e.filter(([,e])=>e!=null&&e!==``&&!(Array.isArray(e)&&e.length===0));return t.length===0?null:(0,v.jsx)(`dl`,{className:`gx-rows`,children:t.map(([e,t])=>(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`dt`,{children:e}),(0,v.jsx)(`dd`,{children:t})]},e))})}var x=e=>e&&e.length>0?(0,v.jsx)(`ul`,{children:e.map((e,t)=>(0,v.jsx)(`li`,{children:e},t))}):void 0;function S({doc:e,node:t,run:n}){let r=e.loops.filter(e=>e.members.includes(t.id)).map(e=>e.name||e.id),i=[];switch(t.kind){case`agent`:{let e=typeof t.role==`string`?t.role:t.role.custom,n=t.by!==`person`;i.push([`Role`,n?[e,t.model?.tier,t.effort].filter(Boolean).join(` · `):e],[`Brief`,(0,v.jsx)(`p`,{className:`gx-prose`,children:t.brief})],[`Reads`,x(t.inputs)],[`Leaves`,x(t.outputs)],[`May`,n?t.allow?.join(`, `):void 0],[`May not`,n?t.deny?.join(`, `):void 0],[`Irreversible`,t.irreversible?.join(`, `)]);break}case`human-gate`:i.push([`Asks`,(0,v.jsx)(`p`,{className:`gx-prose`,children:t.prompt})],[`Answers`,t.options?.join(` / `)]);break;case`check`:i.push([`Kind`,t.check.kind],[`Runs`,t.check.run?(0,v.jsx)(`code`,{children:t.check.run}):void 0],[`Passes when`,(0,v.jsx)(`p`,{className:`gx-prose`,children:t.check.pass})]);break;case`merge`:i.push([`Merges`,t.merges.join(`, `)],[`How`,t.strategy]);break;case`stop`:i.push([`Ends the run`,t.outcome??`yes`])}return i.push([`About`,t.description?(0,v.jsx)(`p`,{className:`gx-prose`,children:t.description}):void 0],[`In loop`,r.join(`, `)]),(0,v.jsxs)(v.Fragment,{children:[n?(0,v.jsx)(C,{run:n}):null,(0,v.jsx)(b,{rows:i})]})}function C({run:e}){let t=[e.state,e.round===void 0?``:`round ${e.round}`,e.runs>1?`${e.runs} dispatches`:``].filter(Boolean).join(` · `);return(0,v.jsxs)(`div`,{className:`gx-runline`,"data-state":e.state,children:[(0,v.jsxs)(`strong`,{children:[`At this step: `,t]}),e.note?.text?(0,v.jsx)(`p`,{children:e.note.text}):null]})}function w({doc:e,loop:t,round:n}){return(0,v.jsxs)(v.Fragment,{children:[n?(0,v.jsx)(`div`,{className:`gx-runline`,children:(0,v.jsxs)(`strong`,{children:[`At this step: `,n]})}):null,(0,v.jsx)(b,{rows:[[`Kind`,`${h(f(e),t)} loop`],[`Members`,t.members.map(t=>e.nodes.find(e=>e.id===t)?.name||t).join(`, `)],[`Bar`,t.bar?(0,v.jsx)(`p`,{className:`gx-prose`,children:`${t.bar.name}. ${t.bar.acceptance}`}):void 0],[`Stops, in order`,x(t.stops.map(m))]]})]})}function T({map:e,picked:t}){if(t.kind===`session`){let n=e.sessions.find(e=>e.id===t.id);if(!n)return null;let{out:r,in:i}=l(e,n.id);return(0,v.jsx)(b,{rows:[[`Does`,n.role],[`Runs in`,[n.harness,n.model,n.lifetime,n.count&&n.count>1?`${n.count} like it`:``].filter(Boolean).join(` · `)],[`Lane`,e.lanes.find(e=>e.id===n.lane)?.name??n.lane],[`Repository`,n.repo],[`Graph`,n.graph],[`About`,n.description?(0,v.jsx)(`p`,{className:`gx-prose`,children:n.description}):void 0],[`Hands to`,x(r.map(t=>`${u(e,t.to)}${t.what?`: ${t.what}`:``}`))],[`Takes from`,x(i.map(t=>`${u(e,t.from)}${t.what?`: ${t.what}`:``}`))]]})}if(t.kind===`person`){let n=(e.people??[]).find(e=>e.id===t.id);return n?(0,v.jsx)(b,{rows:[[`Does`,n.role],[`About`,n.description?(0,v.jsx)(`p`,{className:`gx-prose`,children:n.description}):void 0]]}):null}let n=e.handoffs.find(e=>e.id===t.id);return n?(0,v.jsx)(b,{rows:[[`From`,u(e,n.from)],[`To`,u(e,n.to)],[`What`,n.what],[`Carried by`,n.carrier?`${c[n.carrier.kind]}: ${s(e,n)}`:`not named`]]}):null}function E(e,t){if(`grooph`in e){if(t.kind===`node`){let n=e.nodes.find(e=>e.id===t.id);return n?{kind:n.kind===`agent`&&n.by===`person`?`Person`:y[n.kind],name:n.name||n.id}:void 0}if(t.kind===`loop`){let n=e.loops.find(e=>e.id===t.id);return n?{kind:`Loop`,name:n.name||n.id}:void 0}return}if(t.kind===`session`)return{kind:`Session`,name:u(e,t.id)};if(t.kind===`person`)return{kind:`Person`,name:u(e,t.id)};if(t.kind===`handoff`){let n=e.handoffs.findIndex(e=>e.id===t.id),r=e.handoffs[n];return r?{kind:`Handoff ${n+1}`,name:`${u(e,r.from)} → ${u(e,r.to)}`}:void 0}}function D({doc:e,picked:t,run:n,loopRound:r,onClose:i}){let a=E(e,t),o=(0,_.useRef)(null),s=(0,_.useRef)(i);s.current=i;let c=(0,_.useRef)(t);if(c.current=t,(0,_.useEffect)(()=>{o.current?.focus({preventScroll:!0})},[t.kind,t.id]),(0,_.useEffect)(()=>{let e=document.activeElement,t=e=>{e.key===`Escape`&&s.current()};return document.addEventListener(`keydown`,t),()=>{document.removeEventListener(`keydown`,t);let{kind:n,id:r}=c.current,i=document.querySelector(n===`handoff`?`[data-handoff-row="${CSS.escape(r)}"]`:`[data-${n}="${CSS.escape(r)}"]`)??e;(i instanceof HTMLElement||i instanceof SVGElement)&&i.focus({preventScroll:!0})}},[]),!a)return null;let l=null;if(`grooph`in e){if(t.kind===`node`){let r=e.nodes.find(e=>e.id===t.id);l=(0,v.jsx)(S,{doc:e,node:r,...n?{run:n}:{}})}else t.kind===`loop`&&(l=(0,v.jsx)(w,{doc:e,loop:e.loops.find(e=>e.id===t.id),...r?{round:r}:{}}))}else l=(0,v.jsx)(T,{map:e,picked:t});return(0,v.jsxs)(`aside`,{className:`gx-brief`,role:`dialog`,"aria-modal":`false`,"aria-labelledby":`gx-brief-title`,children:[(0,v.jsxs)(`header`,{children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{className:`gx-brief-kind`,children:a.kind}),(0,v.jsx)(`h2`,{id:`gx-brief-title`,children:a.name})]}),(0,v.jsx)(`button`,{ref:o,type:`button`,className:`gx-btn gx-icon`,"aria-label":`Close brief`,onClick:i,children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`M6 6l12 12M18 6 6 18`})})})]}),(0,v.jsx)(`div`,{className:`gx-brief-body`,children:l})]})}var O=`http://www.w3.org/2000/svg`,k={agent:`Agent`,"human-gate":`Human gate`,check:`Check`,merge:`Merge`,stop:`Stop`},A=`gx-inj`;function j(e,t){e.setAttribute(`tabindex`,`0`),e.setAttribute(`role`,`button`),e.setAttribute(`aria-label`,t)}var M=(e,t)=>Number(e?.getAttribute(t)??0);function N(e,t,n,r,i){let a=document.createElementNS(O,`g`);a.setAttribute(`class`,`${A} gx-pill`),a.setAttribute(`data-state`,i),a.setAttribute(`aria-hidden`,`true`);let o=document.createElementNS(O,`rect`),s=document.createElementNS(O,`text`);s.setAttribute(`font-size`,`9.5`),s.setAttribute(`font-weight`,`700`),s.setAttribute(`text-anchor`,`end`),s.setAttribute(`x`,String(t-6)),s.setAttribute(`y`,String(n)),s.textContent=r,a.append(o,s),e.append(a);let c=(s.getComputedTextLength?.()||r.length*5.6)+12;o.setAttribute(`x`,String(t-c)),o.setAttribute(`y`,String(n-10)),o.setAttribute(`width`,String(c)),o.setAttribute(`height`,`13.5`),o.setAttribute(`rx`,`6.75`)}var P={running:`running`,passed:`passed`,failed:`failed`,halted:`halted`};function F(e,t,n){for(let t of e.querySelectorAll(`.${A}`))t.remove();let r=n?.step.summary,i=n?.step.focus;for(let n of e.querySelectorAll(`g[data-node]`)){let e=n.dataset.node,a=t.nodes.find(t=>t.id===e),o=r?.nodes[e];if(j(n,`${a?a.kind===`agent`&&a.by===`person`?`Person`:k[a.kind]??a.kind:`Node`} ${a?.name||e}${o&&o.state!==`pending`?`, ${o.state}`:o?`, not reached yet`:``}`),!o){delete n.dataset.state,delete n.dataset.focus;continue}n.dataset.state=o.state,i&&i.kind===`node`&&i.id===e?n.dataset.focus=``:delete n.dataset.focus;let s=P[o.state];if(!s)continue;let c=n.querySelector(`rect[data-card]`);if(!c)continue;let l=t.loops.filter(t=>t.members.includes(e)).length;N(n,M(c,`x`)+M(c,`width`)-10-l*10-(l>0?4:0),M(c,`y`)+18,`${s}${o.runs>1?` ×${o.runs}`:``}`,o.state)}let a=e.viewBox.baseVal?.width||400;for(let n of e.querySelectorAll(`g[data-loop]`)){let e=n.dataset.loop,o=t.loops.find(t=>t.id===e),s=r?.loops[e];if(j(n,`Loop ${o?.name||e}${s?`, ${I(s)}`:``}`),i&&i.kind===`loop`&&i.id===e?n.dataset.focus=``:delete n.dataset.focus,!s||s.round===null)continue;let c=n.querySelector(`text`),l=s.lastStop?.fired;N(n,a-12,M(c,`y`),I(s),l?`stopped`:`round`)}for(let t of e.querySelectorAll(`g[data-edge]`))i&&i.kind===`edge`&&i.id===t.dataset.edge?t.dataset.focus=``:delete t.dataset.focus}function I(e){return e.round===null?`not entered`:`round ${e.round}${e.lastStop?.fired?` · ${o[e.lastStop.fired]}`:``}`}function L(e,t){let n=e=>t.handoffs.findIndex(t=>t.id===e)+1;for(let n of e.querySelectorAll(`[data-session]`))j(n,`Session ${u(t,n.dataset.session)}`);for(let n of e.querySelectorAll(`[data-person]`))j(n,`Person ${u(t,n.dataset.person)}`);for(let r of e.querySelectorAll(`[data-handoff-row]`)){let e=r.dataset.handoffRow,i=t.handoffs.find(t=>t.id===e);j(r,i?`Handoff ${n(e)}: ${u(t,i.from)} to ${u(t,i.to)}`:`Handoff ${e}`)}}var ee=e=>{try{return decodeURIComponent(e)}catch{return e}};function te(e){let t=e.includes(`?`)?e.slice(e.indexOf(`?`)+1):``,n=new Map;for(let e of t.split(`&`)){if(e===``)continue;let t=e.indexOf(`=`),r=t<0?e:e.slice(0,t);n.has(r)||n.set(r,t<0?``:e.slice(t+1))}let r=/(?:^|-)(light|dark)$/.exec(n.get(`theme`)??``)?.[1],i=/^([a-z]+?)(?:-(?:light|dark|auto))?$/.exec(n.get(`theme`)??``)?.[1],a=n.get(`c`);return{payload:n.get(`d`)??n.get(`run`)??``,...r===`light`||r===`dark`?{theme:r}:{},frame:n.get(`frame`)===`1`,play:n.get(`play`)===`1`,...a?{candidate:ee(a)}:{},...i&&![`light`,`dark`,`auto`].includes(i)?{look:i}:{}}}var R=e=>e===`#/embed`||e.startsWith(`#/embed?`);function z(e,t=location.href){return`${t.split(`#`)[0]}#/open?d=${e.payload}${e.candidate?`&c=${encodeURIComponent(e.candidate)}`:``}${e.look?`&theme=${e.look}`:``}`}var B=e=>e===void 0||e.round===null?`not entered`:`round ${e.round}${e.lastStop?.fired?` · ${o[e.lastStop.fired]}`:``}`,V=()=>typeof matchMedia==`function`&&matchMedia(`(prefers-reduced-motion: reduce)`).matches;function H({replay:e,doc:t,step:n,setStep:r,autoplay:i}){let a=e.steps.length-1,[o,s]=(0,_.useState)(()=>i&&!V()&&a>0),c=e.steps[n],l=n===a,u=(0,_.useRef)(null);(0,_.useEffect)(()=>{if(!o)return;if(n>=a){s(!1);return}let e=setTimeout(()=>r(e=>Math.min(e+1,a)),900);return()=>clearTimeout(e)},[o,n,a,r]);let d=()=>{if(o)return s(!1);l&&r(0),s(!0)},f=e=>{s(!1),r(t=>Math.min(Math.max(t+e,0),a))},p=l&&n>0?e.end.line:c.caption;return(0,v.jsxs)(`section`,{className:`gx-replay`,"aria-label":`Replay of the run`,children:[(0,v.jsxs)(`div`,{className:`gx-replay-row`,children:[(0,v.jsx)(`button`,{ref:u,type:`button`,className:`gx-btn gx-play`,"aria-label":o?`Pause`:l?`Play the run from the start`:`Play`,onClick:d,disabled:a===0,children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:o?(0,v.jsx)(`path`,{d:`M8 5v14M16 5v14`}):l?(0,v.jsx)(`path`,{d:`M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5`}):(0,v.jsx)(`path`,{d:`M7 5v14l12-7z`,className:`gx-fill`})})}),(0,v.jsx)(`button`,{type:`button`,className:`gx-btn gx-icon`,"aria-label":`Previous step`,onClick:()=>f(-1),disabled:n===0,children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`M17 6 9 12l8 6zM7 6v12`,className:`gx-fill`})})}),(0,v.jsx)(`button`,{type:`button`,className:`gx-btn gx-icon`,"aria-label":`Next step`,onClick:()=>f(1),disabled:l,children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`m7 6 8 6-8 6zM17 6v12`,className:`gx-fill`})})}),(0,v.jsx)(`input`,{className:`gx-scrub`,type:`range`,min:0,max:a,step:1,value:n,"aria-label":`Replay position`,"aria-valuetext":`Step ${n} of ${a}: ${c.caption}`,onChange:e=>{s(!1),r(Number(e.target.value))}}),(0,v.jsxs)(`span`,{className:`gx-count`,"aria-hidden":`true`,children:[n,`/`,a]})]}),(0,v.jsx)(`p`,{className:`gx-caption${l&&n>0?` is-end`:``}`,"aria-live":`polite`,"data-testid":`replay-caption`,children:p}),t.loops.length>0?(0,v.jsx)(`ul`,{className:`gx-loops`,"aria-label":`Loops`,children:t.loops.map((e,t)=>{let n=c.summary.loops[e.id];return(0,v.jsxs)(`li`,{className:`gx-loop-chip${n?.lastStop?.fired?` is-stopped`:``}`,"data-loop-chip":e.id,children:[(0,v.jsx)(`span`,{className:`gx-dot gx-loop-${t%4}`,"aria-hidden":`true`}),e.name||e.id,` · `,(0,v.jsx)(`span`,{className:`gx-round`,children:B(n)})]},e.id)})}):null]})}var U=6,W=.35,G=4;function K({ref:e,svg:t,width:n,height:r,baseScale:i,label:a,onTap:o,decorate:s,onMoved:c}){let l=(0,_.useRef)(null),u=(0,_.useRef)(null),d=(0,_.useRef)({x:0,y:0,s:i}),f=(0,_.useRef)({x:0,y:0,s:i}),p=(0,_.useRef)(!1),m=(0,_.useRef)(c);m.current=c;let h=(0,_.useRef)(o);h.current=o;let g=()=>({w:l.current?.clientWidth??0,h:l.current?.clientHeight??0}),y=(0,_.useCallback)(e=>{let{w:t,h:i}=g(),a=n*e.s,o=r*e.s;return{x:Math.min(Math.max(e.x,Math.min(0,t-a)-24),Math.max(0,t-a)+24),y:Math.min(Math.max(e.y,Math.min(0,i-o)-24),Math.max(0,i-o)+24),s:e.s}},[n,r]),b=(0,_.useCallback)((e,t)=>{d.current=e;let i=u.current,a=l.current;if(!i||!a)return;i.style.transform=`translate(${e.x}px, ${e.y}px)`;let o=i.querySelector(`svg`);o&&(o.setAttribute(`width`,String(n*e.s)),o.setAttribute(`height`,String(r*e.s)));let{w:s,h:c}=g();a.dataset.overflow=n*e.s>s+1||r*e.s>c+1?`1`:`0`,t!==p.current&&(p.current=t,m.current?.(t))},[n,r]),x=(0,_.useCallback)(()=>{let{w:e,h:t}=g(),a=i;t>0&&r*a>t&&(a=Math.max(t/r,i*.55));let o={x:(e-n*a)/2,y:r*a<=t?(t-r*a)/2:0,s:a};f.current=o,b(o,!1)},[b,i,n,r]),S=(0,_.useCallback)((e,t,n)=>{let r=d.current,a=Math.min(Math.max(r.s*e,i*W),i*G),o=a/r.s;b(y({x:t-(t-r.x)*o,y:n-(n-r.y)*o,s:a}),!0)},[b,y,i]),C=(0,_.useCallback)(e=>{if(!(e instanceof SVGGraphicsElement))return;let t=e.getBBox();if(t.width===0)return;let n=d.current,{w:r,h:i}=g(),a=n.x+t.x*n.s,o=n.y+t.y*n.s,s=a+t.width*n.s,c=o+t.height*n.s,{x:l,y:u}=n;a<8?l+=8-a:s>r-8&&(l-=Math.min(s-(r-8),a-8)),o<8?u+=8-o:c>i-8&&(u-=Math.min(c-(i-8),o-8)),(l!==n.x||u!==n.y)&&b(y({x:l,y:u,s:n.s}),!0)},[b,y]);return(0,_.useImperativeHandle)(e,()=>({zoom:e=>{let{w:t,h:n}=g();S(e,t/2,n/2)},fit:x,reveal:C}),[x,C,S]),(0,_.useLayoutEffect)(()=>{p.current=!1,x()},[t,x]),(0,_.useEffect)(()=>{let e=l.current;if(!e)return;let t=new ResizeObserver(()=>p.current?b(y(d.current),!0):x());return t.observe(e),()=>t.disconnect()},[b,y,x]),(0,_.useLayoutEffect)(()=>{let e=u.current?.querySelector(`svg`);e&&s&&s(e)},[t,s]),(0,_.useEffect)(()=>{let e=l.current;if(!e)return;let t=new Map,n=null,r=null,a=t=>{let n=e.getBoundingClientRect();return{x:t.clientX-n.left,y:t.clientY-n.top}},o=()=>{let[e,n]=[...t.values()];return{d:Math.hypot(e.x-n.x,e.y-n.y),mx:(e.x+n.x)/2,my:(e.y+n.y)/2}},s=e=>{(e.button===0||e.pointerType!==`mouse`)&&(e.target instanceof Element&&e.target.closest(`a, button, input`)||(t.set(e.pointerId,a(e)),t.size===1?(n={...a(e),target:e.target instanceof Element?e.target:null,moved:!1,from:d.current},r=null):t.size===2&&(n=null,r={...o(),from:d.current})))},c=s=>{if(!t.has(s.pointerId))return;if(t.set(s.pointerId,a(s)),r&&t.size>=2){let e=o(),t=Math.min(Math.max(r.from.s*e.d/Math.max(r.d,1),i*W),i*G),n=t/r.from.s;b(y({x:e.mx-(r.mx-r.from.x)*n,y:e.my-(r.my-r.from.y)*n,s:t}),!0);return}if(!n)return;let c=a(s),l=c.x-n.x,u=c.y-n.y;!n.moved&&Math.hypot(l,u)<U||(n.moved||(n={...n,x:c.x,y:c.y,from:d.current,moved:!0},e.setPointerCapture(s.pointerId),e.dataset.dragging=`1`),b(y({x:n.from.x+(c.x-n.x),y:n.from.y+(c.y-n.y),s:n.from.s}),!0))},u=i=>{t.delete(i.pointerId)&&(n&&!n.moved&&i.type===`pointerup`&&n.target&&h.current(n.target),t.size<2&&(r=null),n=null,delete e.dataset.dragging)},f=t=>{let n=a(t);if(t.ctrlKey||t.metaKey){t.preventDefault(),S(Math.exp(-t.deltaY*.01),n.x,n.y);return}let r=d.current,i=y({x:r.x-t.deltaX,y:r.y-t.deltaY,s:r.s});Math.abs(i.x-r.x)<.5&&Math.abs(i.y-r.y)<.5||e.dataset.overflow===`1`&&(t.preventDefault(),b(i,!0))};return e.addEventListener(`pointerdown`,s),e.addEventListener(`pointermove`,c),e.addEventListener(`pointerup`,u),e.addEventListener(`pointercancel`,u),e.addEventListener(`wheel`,f,{passive:!1}),()=>{e.removeEventListener(`pointerdown`,s),e.removeEventListener(`pointermove`,c),e.removeEventListener(`pointerup`,u),e.removeEventListener(`pointercancel`,u),e.removeEventListener(`wheel`,f)}},[b,y,S,i]),(0,v.jsx)(`div`,{ref:l,className:`gx-stage`,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&e.target instanceof Element&&e.target!==l.current&&o(e.target)&&e.preventDefault()},onFocus:e=>C(e.target),children:(0,v.jsx)(`div`,{ref:u,className:`gx-canvas`,"aria-label":a,role:`group`,dangerouslySetInnerHTML:{__html:t}})})}var q=e=>Math.round(Math.min(Math.max(e,300),600)),J=(e,t)=>Math.min(e/t,1.25),Y=e=>{let t=/viewBox="0 0 ([\d.]+) ([\d.]+)"/.exec(e);return{w:Number(t?.[1]??400),h:Number(t?.[2]??400)}};function X(e){let t=g(e.payload);if(!t.ok)return{kind:`problem`,message:t.message};let{envelope:n}=t;if(n.kind===`graph`)return{kind:`graph`,doc:n.doc};if(n.kind===`map`)return{kind:`map`,doc:n.doc};if(n.kind===`run`)return{kind:`run`,doc:n.doc.working,bundle:n.doc};let r=n.doc,i=r.candidates.find(t=>t.id===e.candidate)??r.candidates.find(e=>e.id===r.recommendation?.candidate)??r.candidates[0];return i&&`grooph`in i.graph?{kind:`graph`,doc:i.graph}:{kind:`problem`,message:`This comparison has no graph that can be drawn here. Open it in grooph to see the candidates.`}}function Z(e){window.parent!==window&&window.parent.postMessage({grooph:`embed-height`,height:e},`*`)}function ne({link:e}){let t=(0,_.useMemo)(()=>X(e),[e]),n=(0,_.useRef)(null),[r,i]=(0,_.useState)(()=>document.documentElement.clientWidth||400);(0,_.useLayoutEffect)(()=>{let e=n.current;if(!e)return;let t=new ResizeObserver(()=>i(e.clientWidth));return t.observe(e),i(e.clientWidth),()=>t.disconnect()},[]);let a={ref:n,className:`gx`,...e.theme?{"data-theme":e.theme}:{},...e.frame?{"data-frame":``}:{}};return t.kind===`problem`?(0,v.jsx)(`div`,{...a,children:(0,v.jsxs)(`div`,{className:`gx-problem`,role:`alert`,children:[(0,v.jsx)(`strong`,{children:`This embed could not be drawn.`}),(0,v.jsx)(`p`,{children:t.message})]})}):(0,v.jsx)(`div`,{...a,...t.kind===`run`?{"data-replay":``}:{},children:(0,v.jsx)(re,{shown:t,link:e,frameWidth:r,root:n})})}function re({shown:e,link:t,frameWidth:n,root:r}){let i=q(n),o=J(n,i),s=(0,_.useMemo)(()=>e.kind===`map`?d(e.doc,{width:i}):p(e.doc,{width:i}),[e,i]),c=(0,_.useMemo)(()=>Y(s),[s]),l=e.doc.name||e.doc.id,u=(0,_.useRef)(null),f=(0,_.useRef)(null),[m,h]=(0,_.useState)(null),[g,y]=(0,_.useState)(!1),b=(0,_.useMemo)(()=>e.kind===`run`?a(e.bundle.notes,e.doc):void 0,[e]),[x,S]=(0,_.useState)(()=>b&&(!t.play||V())?b.steps.length-1:0),C=b?.steps[x],w=(0,_.useCallback)(n=>{t.theme&&n.setAttribute(`data-theme`,t.theme),n.removeAttribute(`role`),n.setAttribute(`aria-hidden`,`false`),e.kind===`map`?L(n,e.doc):F(n,e.doc,C?{step:C,last:x===b.steps.length-1}:void 0);for(let e of n.querySelectorAll(`.is-picked`))e.classList.remove(`is-picked`);if(m){let e=m.kind===`handoff`?`[data-handoff-row="${CSS.escape(m.id)}"], [data-handoff="${CSS.escape(m.id)}"]`:`[data-${m.kind}="${CSS.escape(m.id)}"]`;for(let t of n.querySelectorAll(e))t.classList.add(`is-picked`)}},[e,t.theme,C,x,b,m]),T=(0,_.useCallback)(e=>{let t=e.closest(`[data-node], [data-loop], [data-session], [data-person], [data-handoff], [data-handoff-row], [data-plate], [data-number]`);if(!(t instanceof SVGElement))return!1;let n=t.dataset,r=n.node?{kind:`node`,id:n.node}:n.loop?{kind:`loop`,id:n.loop}:n.session?{kind:`session`,id:n.session}:n.person?{kind:`person`,id:n.person}:{kind:`handoff`,id:n.handoff??n.handoffRow??n.plate??n.number};return h(e=>e&&e.kind===r.kind&&e.id===r.id?null:r),!0},[]);(0,_.useEffect)(()=>{let e=r.current,t=f.current;if(!e||!t)return;let n=()=>Z(Math.ceil(c.h*o+t.offsetHeight+(e.offsetHeight-e.clientHeight)));n();let i=new ResizeObserver(n);i.observe(t);let a=[1e3,3e3].map(e=>setTimeout(n,e));return window.addEventListener(`load`,n),()=>{i.disconnect(),a.forEach(clearTimeout),window.removeEventListener(`load`,n)}},[c.h,o,r]);let E=(0,_.useMemo)(()=>{if(!b||!C||m?.kind!==`node`)return;let e=C.summary.nodes[m.id];if(!e)return;let t=[...b.steps.slice(1,x+1)].reverse().find(e=>e.note?.at===`node:${m.id}`)?.note;return{state:e.state,runs:e.runs,...e.round===void 0?{}:{round:e.round},...t?{note:t}:{}}},[b,C,m,x]),O=b&&C&&m?.kind===`loop`?B(C.summary.loops[m.id]):void 0;return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`div`,{className:`gx-main`,children:[(0,v.jsx)(K,{ref:u,svg:s,width:c.w,height:c.h,baseScale:o,label:`${l}. Drag to move, pinch or ctrl and scroll to zoom, tap ${e.kind===`map`?`a session or a handoff`:`a node`} to read about it.`,onTap:T,decorate:w,onMoved:y}),m?(0,v.jsx)(D,{doc:e.doc,picked:m,...E?{run:E}:{},...O?{loopRound:O}:{},onClose:()=>h(null)}):null]}),(0,v.jsxs)(`div`,{ref:f,className:`gx-bars`,children:[b?(0,v.jsx)(H,{replay:b,doc:e.doc,step:x,setStep:S,autoplay:t.play}):null,(0,v.jsxs)(`div`,{className:`gx-bar`,role:`toolbar`,"aria-label":`Picture`,children:[(0,v.jsx)(`button`,{type:`button`,className:`gx-btn gx-icon`,"aria-label":`Zoom out`,onClick:()=>u.current?.zoom(1/1.25),children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`M6 12h12`})})}),(0,v.jsx)(`button`,{type:`button`,className:`gx-btn gx-icon`,"aria-label":`Zoom in`,onClick:()=>u.current?.zoom(1.25),children:(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`M6 12h12M12 6v12`})})}),(0,v.jsx)(`button`,{type:`button`,className:`gx-btn`,"aria-label":`Fit the picture`,disabled:!g,onClick:()=>u.current?.fit(),children:`Fit`}),(0,v.jsx)(`span`,{className:`gx-spacer`}),(0,v.jsxs)(`a`,{className:`gx-btn gx-open`,href:z(t),target:`_blank`,rel:`noopener noreferrer`,children:[`Open in grooph`,(0,v.jsx)(`svg`,{viewBox:`0 0 24 24`,"aria-hidden":`true`,children:(0,v.jsx)(`path`,{d:`M14 5h5v5M19 5l-8 8M17 14v5H5V7h5`})})]})]})]})]})}var ie=/^#\/embed\?(?:[^&]*&)*?theme=(?:blueprint|ink|phosphor|transit|chalk)(?:-(?:light|dark|auto))?(?:&|$)/,Q=()=>ie.test(location.hash)?n(`themes`,()=>r(()=>import(`./themes-C34TrvOJ.js`),[])).catch(()=>void 0):void 0;addEventListener(`hashchange`,()=>void Q());var $=Q();if($){let e=document.createElement(`style`);e.textContent=`.gx{visibility:hidden}`,document.head.append(e);let t=()=>e.remove();$.then(t),setTimeout(t,800)}function ae(){let[e,t]=(0,_.useState)(()=>location.hash);(0,_.useEffect)(()=>{let e=()=>{!R(location.hash)&&document.documentElement.classList.contains(`gx-page-only`)?location.reload():t(location.hash)};return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useLayoutEffect)(()=>{let e=document.documentElement;return e.classList.add(`gx-page`),()=>e.classList.remove(`gx-page`)},[]);let n=(0,_.useMemo)(()=>te(e),[e]);return(0,v.jsx)(ne,{link:n},n.payload)}export{ae as EmbedApp};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{a as e,c as t,t as n}from"./index-B5ZkSF4L.js";import{A as r,c as i,fn as a,j as o,k as s,l as c,ot as l,r as u,s as d,un as f,vn as p}from"./share-DV3wsAsJ.js";import{C as m,S as h,_ as g,b as _,g as v,h as y,v as b,x,y as S}from"./App-D1pDFbEY.js";var C=t(e(),1),w=e=>e.replace(/[\s\u0000-\u001f\u007f-\u009f\u200b-\u200d\u2028\u2029\u2060]+/g,` `).trim(),T=e=>w(e).replace(/[\\`*_[\]<>&|~#]/g,`\\$&`),E=e=>T(e)||` `,D=e=>w(e)===e&&!/[`|<>\\]/.test(e)?`runs \`${e}\``:`runs a command, given in full below`,O=e=>typeof e.role==`string`?e.role:e.role.custom;function k(e){return e.nodes.map(e=>{let t={id:e.id,name:w(e.name??``)||e.id};switch(e.kind){case`agent`:return e.by===`person`?{...t,whose:`a person`,does:w(O(e)),leaves:w(e.outputs.join(`; `))}:{...t,whose:O(e)===`lead`?`the lead`:`an agent`,does:w([O(e),e.model?.tier,e.effort?`${e.effort} effort`:void 0].filter(Boolean).join(`, `)),leaves:w(e.outputs.join(`; `))};case`human-gate`:{let n=(e.options??[]).map(w).filter(Boolean);return{...t,whose:`a person`,does:n.length>0?`decides: ${n.join(` / `)}`:w(e.prompt)?`is asked: ${w(e.prompt)}`:`is asked`,leaves:`their answer`}}case`check`:{let n=w(e.check.pass)?`pass or fail (${w(e.check.pass)})`:`pass or fail`;return e.check.run&&w(e.check.run)?{...t,whose:`a command`,does:`runs a command`,leaves:n,command:e.check.run}:{...t,whose:`the lead`,does:`judges a check of kind ${e.check.kind}`,leaves:n}}case`merge`:return{...t,whose:`the lead`,does:e.merges.length>0?w(`merges ${e.merges.join(`, `)}`):`merges what it is handed`,leaves:w(e.merges.join(`; `))};case`stop`:return{...t,whose:`nobody`,does:`the run ends here${e.outcome===`halt`?`, halted`:e.outcome===`success`?`, in success`:``}`,leaves:``}}})}var A=(e,t,n)=>`${e} ${e===1?t:n}`,j=(e,t)=>e([{...t,title:`x`}]).replace(/^# x\n*/,``).trimEnd();function M(e,t,n,r,i){let[,a,o,s]=e,c=T(t.name??``)||t.id,l=n.filter(e=>e.severity===`error`),u=n.filter(e=>e.severity!==`error`),d=s(t).filter(e=>e.severity===`error`).length,f=k(t),p=new Map(f.map(e=>[e.id,e.name])),m=e=>f.filter(t=>t.whose===e).length,h=[...t.edges.filter(e=>e.approval===!0).map(e=>`- to approve the work going from ${T(p.get(e.from)??e.from)} to ${T(p.get(e.to)??e.to)}, each time`),...t.loops.flatMap(e=>e.stops.filter(e=>e.kind===`human`).map(t=>`- in the loop ${T(e.name??``)||e.id}, ${t.kind===`human`&&t.every!==void 0?t.every===1?`every round`:`every ${t.every} rounds`:`when it stops for them`}`))],g=e=>`- \`${e.code}\` ${T(e.message)}${e.at.length>0?` (at: ${e.at.map(T).join(`, `)})`:``}`,_=t.notes?.length??0,[v,...y]=a(t);return`${[`# ${c}`,``,`A plan, kept by grooph: the picture, who does what, and every step in full. grooph runs nothing: a plan is for people to read and follow. The document it is drawn from is \`${i}\`, and that is the one to edit.${_>0?` It carries ${A(_,`note`,`notes`)} from runs, which ${_===1?`is`:`are`} in that file and not shown here.`:``}`,``,`${d>0?`As a plan this is not whole yet: it breaks ${d===1?`a rule a graph itself is held to`:`the rules a graph itself is held to in ${d} places`}, and until ${d===1?`that is`:`those are`} fixed, parts of what follows may be missing or drawn wrong.`:f.length===0?`This plan has no steps yet.`:`As a plan for people to read and follow, this is whole.`} ${l.length===0?`A coding harness could run it as it is: nothing in it is in error.`:`**To run it in a coding harness, ${A(l.length,`thing is`,`things are`)} to be fixed first**${d>0&&d<l.length?` (${d===1?`that one`:`those ${d}`} among them)`:``}, listed under "To fix before a harness can run this".`}`,``,...w(t.goal??``)?[`**Goal:** ${T(t.goal)}`,``]:[],``,``,`## Who does what`,``,...f.length===0?[`No steps yet.`]:[`| Step | Whose | What it does or asks | Leaves behind |`,`|---|---|---|---|`,...f.map(e=>`| ${E(e.name)} | ${e.whose===`nobody`?` `:e.whose} | ${e.command===void 0?E(e.does):D(e.command)} | ${E(e.leaves)} |`),``,`Of ${A(f.length,`step`,`steps`)}: ${m(`an agent`)} by an agent, ${m(`a person`)} by a person, ${m(`a command`)} by a command${m(`the lead`)>0?`, ${m(`the lead`)} by the lead`:``}${m(`nobody`)>0?`; ${m(`nobody`)} ${m(`nobody`)===1?`ends`:`end`} the run`:``}.`],...h.length>0?[``,`A person is also asked, apart from the steps above:`,``,...h]:[],``,`## To fix before a harness can run this`,``,...l.length>0?[`Each of these stops a package from being written. The code names the rule, and grooph's rule reference has each rule in full.`,``,...l.map(g)]:[`Nothing.`],...u.length>0?[``,l.length>0?`And these are warnings: a package is written with them, and carries them in its lead's brief.`:`These are warnings: a package is written with them, and carries them in its lead's brief.`,``,...u.map(g)]:[],``,`## In full`,``,...v?[j(o,v),``]:[],...y.flatMap(e=>[`### ${e.kind}: ${T(e.title)||e.id}`,``,`\`${e.id}\``,``,j(o,e),``])].join(`
|
|
2
|
+
`).replace(/\n{3,}/g,`
|
|
3
|
+
|
|
4
|
+
`).trimEnd()}\n`}function N(e,t){let[n,,,r,i]=e,a=r(t,{forExport:!0,loops:`errors`}),o=`${t.id}.svg`,s=`${t.id}.grooph.json`;return{files:{"PLAN.md":M(e,t,a,o,s),[o]:i(t),[s]:n(t)},toFix:a}}var P=e=>N([a,d,i,l,e=>c(e)],e),F=e=>`${e.id||`graph`}-plan.zip`,I=(e,t)=>Object.fromEntries(Object.entries(t.files).map(([t,n])=>[`${e.id||`graph`}-plan/${t}`,n])),L=e=>e.endsWith(`.svg`)?`image/svg+xml`:e.endsWith(`.json`)?`application/json`:`text/markdown`,R=n();function z({doc:e,own:t,needs:n,keep:r,open:i,exported:a}){let[o,s]=(0,C.useState)(v()?`here`:`coming`);(0,C.useEffect)(()=>{if(o!==`coming`)return;let e=!1;return x().then(()=>!e&&s(`here`),()=>!e&&s(`failed`)),()=>{e=!0}},[o]);let c=(0,C.useMemo)(()=>y(e),[e,o]),l=!t.some(e=>e.code===`E_SCHEMA`),u=(0,C.useMemo)(()=>l?P(e):void 0,[e,l]),d=(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>b(S(e),_(e),`application/json`),children:`Download graph (.grooph.json)`});return l?(0,R.jsxs)(`div`,{className:`inspector`,children:[(0,R.jsx)(B,{doc:e,open:i}),u?(0,R.jsx)(V,{doc:e,plan:u,graph:d}):null,r,(0,R.jsx)(H,{doc:e,attempt:c,compiler:o,own:t,needs:n,open:i,exported:a})]}):(0,R.jsxs)(`div`,{className:`inspector`,children:[(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsx)(`p`,{children:(0,R.jsx)(`strong`,{children:`This does not read as a graph yet: fix these first.`})}),(0,R.jsx)(`p`,{className:`field-hint`,children:`The document does not match the graph schema yet, so it has no plan and no package. The file itself can be kept.`}),(0,R.jsx)(`pre`,{className:`issue-lines`,children:t.map(p).join(`
|
|
5
|
+
`)}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>i(`issues`),children:`Show issues`})]}),(0,R.jsx)(`div`,{className:`export-actions`,children:d}),r]})}function B({doc:e,open:t}){let[n,i]=(0,C.useState)(null),a=()=>{try{return`${location.origin}${location.pathname}#/open?d=${o(r(e),e=>u(e,{level:9}))}`}catch(e){if(!(e instanceof s))throw e;i({errors:e.issues.filter(e=>e.severity===`error`).length});return}};return(0,R.jsxs)(`div`,{role:`group`,"aria-label":`A link`,children:[(0,R.jsx)(`h3`,{className:`files-title`,children:`A link`}),(0,R.jsx)(`p`,{className:`field-hint`,children:`Anyone with the link can open the graph in a browser, read-only. Nothing is uploaded: the graph is in the link itself.`}),(0,R.jsxs)(`div`,{className:`export-actions`,children:[(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:async()=>{let e=a();e&&i({url:e,copied:await g(e)})},children:`Copy link`}),typeof navigator.share==`function`?(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>{let t=a();t&&navigator.share({title:e.name,url:t}).catch(()=>void 0)},children:`Share…`}):null]}),n?.errors===void 0?n?.url?(0,R.jsxs)(`p`,{className:`field-hint`,role:`status`,children:[n.copied?`Copied.`:`This browser did not allow copying. Select the link and copy it:`,n.url.length>32e3?` It is ${n.url.length.toLocaleString(`en-US`)} characters long, and some messengers cut links that long: Download graph sends the file instead.`:``]}):null:(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsx)(`p`,{children:(0,R.jsxs)(`strong`,{children:[`A link needs a graph with no errors: this one has `,n.errors,`.`]})}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>t(`issues`),children:`Show issues`})]}),n?.url&&!n.copied?(0,R.jsx)(`input`,{className:`mono`,readOnly:!0,value:n.url,"aria-label":`The link`,onFocus:e=>e.currentTarget.select()}):null]})}function V({doc:e,plan:t,graph:n}){let r=t.toFix.filter(e=>e.severity===`error`).length,i=`${e.id}.svg`;return(0,R.jsxs)(`div`,{role:`group`,"aria-label":`The plan`,children:[(0,R.jsx)(`h3`,{className:`files-title`,children:`The plan`}),(0,R.jsx)(`p`,{className:`field-hint`,children:`A plan is this graph to read and follow: who does what, the picture and the file. It can always be kept, whatever the validator found.`}),(0,R.jsxs)(`div`,{className:`export-actions`,children:[(0,R.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>b(F(e),m(I(e,t)),`application/zip`),children:`The plan, all three (.zip)`}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>b(`PLAN.md`,t.files[`PLAN.md`],L(`PLAN.md`)),children:`Plan (PLAN.md)`}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>b(i,t.files[i],L(i)),children:`Its picture (.svg)`}),n]}),r>0?(0,R.jsxs)(`p`,{className:`field-hint`,children:[`PLAN.md lists what a harness would need before it could run this: `,r,` thing`,r===1?``:`s`,`. None of them stops the plan.`]}):null]})}function H({doc:e,attempt:t,compiler:n,own:r,needs:i,open:a,exported:o}){let[s,c]=(0,C.useState)(`idle`),[l,u]=(0,C.useState)(null),d=r.filter(e=>e.severity===`error`),_=(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`h3`,{className:`files-title`,children:[`A package for a harness `,(0,R.jsx)(`span`,{className:`badge badge-quiet`,children:`Experimental`})]}),(0,R.jsx)(`p`,{className:`field-hint`,children:`A package asks a coding session to follow this graph. It is a trial, and may change, or go, in a later version.`})]});if(d.length>0||i.length>0){let t=e.target?.harness?.trim();return(0,R.jsxs)(`div`,{className:`keep`,role:`group`,"aria-label":`A package for a harness`,children:[_,(0,R.jsx)(`p`,{className:`field-hint`,children:`The files a coding harness runs this graph from. To write them, grooph still needs:`}),(0,R.jsxs)(`ul`,{className:`files`,children:[i.map(e=>(0,R.jsx)(`li`,{className:`field-hint`,children:e},e)),d.length>0?(0,R.jsxs)(`li`,{className:`field-hint`,children:[`A graph with no errors: this one has `,d.length,`.`]}):null]}),i.length>0?(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>a(`graph`),children:`Open the graph's details`}):null,d.length>0?(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsx)(`p`,{children:(0,R.jsx)(`strong`,{children:t?`Cannot export for ${t}: fix these first.`:`Cannot write a package: fix these first.`})}),(0,R.jsx)(`p`,{className:`field-hint`,children:`${d.length} validation error${d.length===1?``:`s`} — ${d.map(e=>e.code).join(`, `)}`}),(0,R.jsx)(`pre`,{className:`issue-lines`,children:r.map(p).join(`
|
|
6
|
+
`)}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>a(`issues`),children:`Show issues`})]}):null]})}if(!t)return(0,R.jsxs)(`div`,{className:`keep`,role:`group`,"aria-label":`A package for a harness`,children:[_,n===`failed`?(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsxs)(`p`,{children:[(0,R.jsx)(`strong`,{children:`The exporter could not be fetched.`}),` It needs a connection the first time.`]}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>location.reload(),children:`Load the page again`})]}):(0,R.jsx)(`p`,{className:`field-hint`,role:`status`,children:`Preparing the package…`})]});if(!t.ok&&t.reason===`id`)return(0,R.jsxs)(`div`,{className:`keep`,role:`group`,"aria-label":`A package for a harness`,children:[_,(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsx)(`p`,{children:(0,R.jsxs)(`strong`,{children:[`Cannot export for `,t.target,`.`]})}),(0,R.jsxs)(`p`,{className:`field-hint`,children:[t.said,` Give the graph an id of its own, in the graph's panel, and export again.`]})]})]});if(!t.ok)return(0,R.jsxs)(`div`,{className:`keep`,role:`group`,"aria-label":`A package for a harness`,children:[_,(0,R.jsxs)(`div`,{className:`refusal`,role:`alert`,children:[(0,R.jsx)(`p`,{children:(0,R.jsxs)(`strong`,{children:[`Cannot export for `,t.target,`: fix these first.`]})}),(0,R.jsx)(`pre`,{className:`issue-lines`,children:t.issues.map(p).join(`
|
|
7
|
+
`)}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:()=>a(`issues`),children:`Show issues`})]})]});let{files:v,kickoff:y,warnings:x}=t.result,S=Object.keys(v);return(0,R.jsxs)(`div`,{className:`keep`,role:`group`,"aria-label":`A package for a harness`,children:[_,(0,R.jsxs)(`div`,{className:`export-actions`,children:[(0,R.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>{b(h(e,t.target),m(v),`application/zip`),o()},children:`Download package (.zip)`}),(0,R.jsx)(`button`,{type:`button`,className:`btn`,onClick:async()=>{c(await g(y)?`done`:`failed`),setTimeout(()=>c(`idle`),2500)},children:s===`done`?`Kickoff copied`:s===`failed`?`Copy failed`:`Copy kickoff prompt`})]}),(0,R.jsxs)(`p`,{className:`field-hint`,children:[`Unzip at the root of the project the run works in, then paste the kickoff prompt into a `,f(t.target),` session opened there.`]}),x.length>0?(0,R.jsxs)(`div`,{className:`warnings`,children:[(0,R.jsxs)(`p`,{children:[x.length,` warning`,x.length===1?``:`s`,`, carried into the lead brief:`]}),(0,R.jsx)(`pre`,{className:`issue-lines`,children:x.map(p).join(`
|
|
8
|
+
`)})]}):null,(0,R.jsxs)(`h3`,{className:`files-title`,children:[S.length,` files for `,t.target]}),(0,R.jsx)(`ul`,{className:`files`,children:S.map(e=>(0,R.jsxs)(`li`,{children:[(0,R.jsxs)(`button`,{type:`button`,className:`file`,"aria-expanded":l===e,onClick:()=>u(l===e?null:e),children:[(0,R.jsx)(`span`,{className:`mono file-path`,children:e}),(0,R.jsx)(`span`,{className:`file-size`,children:U(v[e])})]}),l===e?(0,R.jsx)(`pre`,{className:`file-body`,children:v[e]}):null]},e))}),(0,R.jsxs)(`details`,{className:`more`,children:[(0,R.jsx)(`summary`,{children:`Kickoff prompt`}),(0,R.jsx)(`pre`,{className:`file-body`,children:y})]})]})}var U=e=>{let t=new TextEncoder().encode(e).length;return t<1024?`${t} B`:`${(t/1024).toFixed(1)} KB`};export{z as ExportPanel};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,t as n}from"./index-B5ZkSF4L.js";import{$ as r,L as i,R as a,X as o,et as s,k as c,q as l}from"./share-DV3wsAsJ.js";import{m as u}from"./templates-DbAhWvP6.js";import{S as d,a as f,p,u as m}from"./runs-BA5HKtsx.js";import{n as h,t as g}from"./mermaid-read-cyqJ32v-.js";var _=t(e(),1),v=n(),y=e=>{try{return JSON.parse(e)}catch{return}},b=`It could not be read: it is nested far deeper than a grooph document ever is.`;async function x(e,t,n){try{return await S(e,t,n)}catch(e){if(!(e instanceof RangeError))throw e;return n.problem({name:t.name,issues:[],what:b}),!1}}async function S(e,t,n){let _=y(e);if(i(_)){let e=a(_);if(!e.bundle)return n.problem({name:t.name,issues:e.issues.map(e=>({code:`E_SCHEMA`,severity:`error`,message:e,at:[]})),what:`It is a run bundle grooph cannot read. Make it again with grooph runs bundle <run dir> --out <file>.`}),!1;let{record:r}=await f(e.bundle);return location.hash=d(r.key),!0}if(l(_)){let e=o(_);return e.map?(location.hash=n.route(e.map),!0):(n.problem({name:t.name,issues:e.issues,what:`It is an operation map grooph cannot read.`}),!1)}if(r(_)){let r=s(JSON.parse(e));try{if(!r.set)throw new c(`not a proposal set`,r.issues);return location.hash=n.route(r.set),!0}catch(e){if(!(e instanceof c))throw e;return n.problem({name:t.name,issues:e.issues,what:`It is a proposal set grooph cannot show. Make a self-contained copy with grooph share --out, which carries every graph.`}),!1}}if(_===void 0&&g(e)){let r=/\.(mmd|mermaid|md|markdown|txt)$/i.test(t.name)?t.name.replace(/\.[^.]+$/,``):void 0,i=h(e,r?{name:r}:{});return i.ok?(n.problem(null),n.review({name:t.name,doc:i.doc,guesses:i.guesses}),!0):(n.problem({name:t.name,issues:[],what:`It is Mermaid grooph cannot read: ${i.error}${i.line===void 0?``:` (line ${i.line})`}.`}),!1)}let v=p(e);if(!v.doc)return n.problem({name:t.name,issues:v.issues}),!1;if(n.problem(null),v.doc.template)return n.offer({name:t.name,doc:v.doc,refusal:u(v.doc)}),!0;let b=await m(v.doc);return n.open(b.key),!0}var C=e=>{try{let t=JSON.parse(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:void 0}catch{return}},w=e=>`grooph`in e||r(e)||l(e)||i(e);function T(e){if(w(e))return e;let t=e.graph;return typeof t==`object`&&t&&!Array.isArray(t)&&w(t)?t:void 0}function E(e,t){let n=0,r=!1;for(let i=t;i<e.length;i+=1){let a=e[i];if(r)a===`\\`?i+=1:a===`"`&&(r=!1);else if(a===`"`)r=!0;else if(a===`{`)n+=1;else if(a===`}`&&(--n,n===0))return e.slice(t,i+1)}}var D=1e6;function O(e){if(e.length>1e6)return{kind:`nothing`};let t=e.trim();if(t===``)return{kind:`nothing`};let n=[],r=C(t);if(r)n.push({text:t,json:r,inside:!1,at:0});else{for(let e of t.matchAll(/```[A-Za-z0-9-]*[ \t]*\r?\n([\s\S]*?)```/g)){let t=e[1].trim(),r=C(t);r&&n.push({text:t,json:r,inside:!1,at:e.index+e[0].indexOf(t)})}let e=0,r=t.indexOf(`{`);for(let i=0;r!==-1&&i<64&&e<8;i+=1){let i=E(t,r);if(i===void 0){e+=1,r=t.indexOf(`{`,r+1);continue}let a=C(i);a&&!n.some(e=>e.at===r)&&n.push({text:i,json:a,inside:e>0,at:r}),r=t.indexOf(`{`,r+i.length)}n.sort((e,t)=>e.at-t.at)}for(let e of n){let t=T(e.json);if(t)return{kind:`document`,text:t===e.json?e.text:JSON.stringify(t,null,2)}}let i=/#\/(?:open|embed)\?((?:[^#\s]*&)?d=([A-Za-z0-9_-]+)[^#\s]*)/.exec(t);if(i){let e=/(?:^|&)c=([A-Za-z0-9_%.~-]+)(?:&|$)/.exec(i[1])?.[1],t=/(?:^|&)theme=([a-z]+(?:-[a-z]+)?)(?:&|$)/.exec(i[1])?.[1],n=`${e?`&c=${e}`:``}${t?`&theme=${t}`:``}`;return{kind:`link`,payload:i[2],...n?{rest:n}:{}}}if(g(t))return{kind:`document`,text:t};let a=n.find(e=>!e.inside);return a?{kind:`document`,text:a.text}:{kind:`nothing`}}async function k(e,t,n=!1){let r=`what you pasted`;if(e.length>1e6)return n||t.problem({name:r,issues:[],what:`It is over a megabyte, and a grooph document is a few kilobytes. Paste the document alone.`}),!1;try{let i=O(e);if(i.kind===`link`)return t.close(),location.hash=`#/open?d=${i.payload}${i.rest??``}`,!0;if(i.kind===`nothing`)return n||t.problem({name:r,issues:[{code:`E_SCHEMA`,severity:`error`,message:`no JSON document and no grooph link was found in the text`,at:[]}],what:`Paste the whole document, from its first { to its last }, or a link that ends in #/open?d=…`}),!1;let a=await x(i.text,{name:r},t);return a&&t.close(),a}catch(e){if(!(e instanceof RangeError))throw e;return n||t.problem({name:r,issues:[],what:b}),!1}}var A=e=>e?.scrollIntoView({block:`nearest`});function j({host:e}){let[t,n]=(0,_.useState)(``);return(0,_.useEffect)(()=>e.problem(null),[]),(0,v.jsxs)(`form`,{className:`offer`,style:{flexDirection:`column`,alignItems:`stretch`},ref:A,onSubmit:n=>{n.preventDefault(),k(t,e)},children:[(0,v.jsx)(`label`,{className:`field-label`,htmlFor:`paste-document`,style:{marginBottom:0},children:`A graph's JSON, as a chat or an agent gave it to you, a grooph link, or Mermaid text`}),(0,v.jsx)(`textarea`,{id:`paste-document`,className:`input textarea mono`,rows:6,autoFocus:!0,spellCheck:!1,autoCapitalize:`off`,autoCorrect:`off`,placeholder:`{ "grooph": 0, "id": "…", "nodes": [ … ] }`,value:t,onChange:e=>n(e.target.value)}),(0,v.jsx)(`div`,{className:`field-hint`,style:{margin:0},children:`The code fence and the sentences around it can stay. Nothing leaves this device; the document is checked here, with the same rules as a file.`}),(0,v.jsxs)(`div`,{className:`offer-actions`,children:[(0,v.jsx)(`button`,{type:`submit`,className:`btn btn-primary`,disabled:t.trim()===``,children:`Open it`}),(0,v.jsx)(`button`,{type:`button`,className:`btn btn-quiet`,onClick:e.close,children:`Cancel`})]})]})}export{D as PASTE_LIMIT,j as PasteBox,x as importText,k as openPasted,O as readPasted};
|