grooph 0.0.0-stage → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -2
  3. package/THIRD-PARTY-NOTICES.md +450 -0
  4. package/dist/app/assets/App-48y1Ec5B.js +5 -0
  5. package/dist/app/assets/App-Cv1auMeh.css +1 -0
  6. package/dist/app/assets/EmbedApp-390Z2d4x.js +1 -0
  7. package/dist/app/assets/EmbedApp-CWb_HGpW.css +1 -0
  8. package/dist/app/assets/ExportPanel-Ca3wBB38.js +8 -0
  9. package/dist/app/assets/Import-ByMI7Uz9.js +1 -0
  10. package/dist/app/assets/base-CeJeskZg.css +1 -0
  11. package/dist/app/assets/brakes-BJPDrrJT.js +1 -0
  12. package/dist/app/assets/builtins-D_22fKRF.js +1 -0
  13. package/dist/app/assets/compile-BAvi-rd0.js +15 -0
  14. package/dist/app/assets/fonts/OFL-atkinson-hyperlegible-mono.txt +93 -0
  15. package/dist/app/assets/fonts/OFL-atkinson-hyperlegible-next.txt +93 -0
  16. package/dist/app/assets/fonts/atkinson-hyperlegible-mono.v1.woff2 +0 -0
  17. package/dist/app/assets/fonts/atkinson-hyperlegible-next-italic.v1.woff2 +0 -0
  18. package/dist/app/assets/fonts/atkinson-hyperlegible-next.v1.woff2 +0 -0
  19. package/dist/app/assets/front-BRl7rl-u.js +107 -0
  20. package/dist/app/assets/graph-more-393HTgQE.js +2 -0
  21. package/dist/app/assets/graph-stage-BkrQGQ-z.js +4 -0
  22. package/dist/app/assets/graph-views-COQF_QTQ.js +2 -0
  23. package/dist/app/assets/groups-CjEPiwfv.js +1 -0
  24. package/dist/app/assets/index-CeaKwKuU.js +10 -0
  25. package/dist/app/assets/offline-Byh4d-Wf.js +49 -0
  26. package/dist/app/assets/plan-templates-BQu8G2sX.js +1 -0
  27. package/dist/app/assets/runs-DcjRi7S6.js +1 -0
  28. package/dist/app/assets/screens-DWbqIw6l.css +1 -0
  29. package/dist/app/assets/screens-aIjMSzZg.js +16 -0
  30. package/dist/app/assets/share-BzIzB4EY.js +8 -0
  31. package/dist/app/assets/site-icons.v1.svg +9 -0
  32. package/dist/app/assets/space-DWgrlnfB.js +1 -0
  33. package/dist/app/assets/styles-BA1aVO9R.css +1 -0
  34. package/dist/app/assets/templates-CoNTzuuz.js +2 -0
  35. package/dist/app/assets/themes-DtOBrLk0.js +1 -0
  36. package/dist/app/assets/units-DOIUgOIU.js +1 -0
  37. package/dist/app/assets/views-CLwg5VNv.js +1 -0
  38. package/dist/app/demo/run.txt +1 -0
  39. package/dist/app/favicon.svg +1 -0
  40. package/dist/app/grooph-app.json +1 -0
  41. package/dist/app/icon-192.png +0 -0
  42. package/dist/app/icon-512.png +0 -0
  43. package/dist/app/icon-maskable-512.png +0 -0
  44. package/dist/app/index.html +42 -0
  45. package/dist/app/llms.txt +26 -0
  46. package/dist/app/manifest.webmanifest +16 -0
  47. package/dist/app/og.png +0 -0
  48. package/dist/app/routes.json +81 -0
  49. package/dist/app/sw.js +217 -0
  50. package/dist/bundle/grooph.js +18109 -0
  51. package/dist/patterns/contradiction-seeker.grooph.json +189 -0
  52. package/dist/patterns/debate-then-build.grooph.json +307 -0
  53. package/dist/patterns/dual-bar.grooph.json +200 -0
  54. package/dist/patterns/fresh-grind-rare-judge.grooph.json +252 -0
  55. package/dist/patterns/gauntlet-decomposed.grooph.json +427 -0
  56. package/dist/patterns/glyphs/contradiction-seeker.svg +13 -0
  57. package/dist/patterns/glyphs/debate-then-build.svg +28 -0
  58. package/dist/patterns/glyphs/dual-bar.svg +13 -0
  59. package/dist/patterns/glyphs/fresh-grind-rare-judge.svg +21 -0
  60. package/dist/patterns/glyphs/gauntlet-decomposed.svg +39 -0
  61. package/dist/patterns/glyphs/grind-loop.svg +13 -0
  62. package/dist/patterns/glyphs/heterogeneous-critic.svg +18 -0
  63. package/dist/patterns/glyphs/human-gated-irreversible.svg +11 -0
  64. package/dist/patterns/glyphs/merge-queue.svg +20 -0
  65. package/dist/patterns/glyphs/metric-sandwich.svg +18 -0
  66. package/dist/patterns/glyphs/ownership-not-swarm.svg +25 -0
  67. package/dist/patterns/glyphs/patrol-pulse.svg +21 -0
  68. package/dist/patterns/glyphs/ralph-loop.svg +18 -0
  69. package/dist/patterns/glyphs/red-team-loop.svg +13 -0
  70. package/dist/patterns/glyphs/retrospective-rewrite.svg +16 -0
  71. package/dist/patterns/glyphs/review-gate.svg +18 -0
  72. package/dist/patterns/glyphs/spec-then-loop.svg +19 -0
  73. package/dist/patterns/glyphs/specialist-critic-bank.svg +36 -0
  74. package/dist/patterns/glyphs/taste-polish.svg +18 -0
  75. package/dist/patterns/glyphs/tournament-then-judge.svg +22 -0
  76. package/dist/patterns/grind-loop.grooph.json +130 -0
  77. package/dist/patterns/heterogeneous-critic.grooph.json +218 -0
  78. package/dist/patterns/human-gated-irreversible.grooph.json +94 -0
  79. package/dist/patterns/index.json +580 -0
  80. package/dist/patterns/merge-queue.grooph.json +199 -0
  81. package/dist/patterns/metric-sandwich.grooph.json +214 -0
  82. package/dist/patterns/ownership-not-swarm.grooph.json +289 -0
  83. package/dist/patterns/patrol-pulse.grooph.json +213 -0
  84. package/dist/patterns/ralph-loop.grooph.json +188 -0
  85. package/dist/patterns/red-team-loop.grooph.json +193 -0
  86. package/dist/patterns/retrospective-rewrite.grooph.json +166 -0
  87. package/dist/patterns/review-gate.grooph.json +216 -0
  88. package/dist/patterns/spec-then-loop.grooph.json +235 -0
  89. package/dist/patterns/specialist-critic-bank.grooph.json +384 -0
  90. package/dist/patterns/taste-polish.grooph.json +232 -0
  91. package/dist/patterns/tournament-then-judge.grooph.json +247 -0
  92. package/dist/plans/literature-review.grooph.json +306 -0
  93. package/dist/plans/research-study.grooph.json +367 -0
  94. package/dist/plans/solo-project.grooph.json +210 -0
  95. package/dist/plans/team-handoffs.grooph.json +203 -0
  96. package/hooks/grooph-event.mjs +103 -0
  97. package/hooks/grooph-events-push.mjs +954 -0
  98. package/package.json +38 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan Joseph Kamp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,63 @@
1
- # Temporary Holding Version
1
+ # grooph
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Loop graphs for coding agents.** You, or an agent working with you, say who builds, who checks, where a person decides and when the work stops, in one small JSON document. grooph checks that every loop names a stop and every bar names what a critic can inspect, then compiles the graph into a prompt package for Claude Code or for Codex. Your harness runs the package. grooph never runs an agent and never calls a model.
4
+
5
+ A graph can also be a plan for people: `grooph plan` writes a plan people can follow, for any graph that reads.
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.
8
+
9
+ ```bash
10
+ npx grooph --help
11
+ ```
12
+
13
+ ## A first graph
14
+
15
+ Node 22 or later. This makes a graph from a template, checks it, draws it, and writes the package Claude Code runs:
16
+
17
+ ```bash
18
+ 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
19
+ npx grooph validate --for-export flaky.grooph.json
20
+ npx grooph image flaky.grooph.json --out flaky.svg
21
+ npx grooph export flaky.grooph.json --target claude-code --into .
22
+ ```
23
+
24
+ A package is for Claude Code or for Codex: `--target codex` writes one for a graph that names Codex. `grooph template list` shows every template with when to use it. `grooph explain flaky.grooph.json` says in plain words what the graph's brakes are. `grooph share flaky.grooph.json` prints a link that opens the graph in the [grooph app](https://ryanjosephkamp.github.io/grooph/) on any device; the graph travels after the `#`, which a browser sends to no server.
25
+
26
+ Install it once to drop the `npx`:
27
+
28
+ ```bash
29
+ npm install --global grooph
30
+ ```
31
+
32
+ A PNG (`--out flaky.png`) needs the optional renderer `@resvg/resvg-js`, which npm installs with grooph where a build for the platform exists. The SVG is the same drawing and needs nothing.
33
+
34
+ ## For an agent
35
+
36
+ `grooph mcp` is an MCP server over standard input and output. An agent with nothing but tool calls can pick a template, fill it, change it, check it, draw it and hand back a link. Documents go in and come back as JSON, so no file has to exist.
37
+
38
+ ```bash
39
+ claude mcp add grooph -- npx -y grooph mcp # Claude Code
40
+ ```
41
+
42
+ ```toml
43
+ # Codex, in ~/.codex/config.toml
44
+ [mcp_servers.grooph]
45
+ command = "npx"
46
+ args = ["-y", "grooph", "mcp", "--harness", "codex"]
47
+ ```
48
+
49
+ ```json
50
+ { "mcpServers": { "grooph": { "command": "npx", "args": ["-y", "grooph", "mcp", "--chat"] } } }
51
+ ```
52
+
53
+ The last one is for a chat in Claude's desktop app (`claude_desktop_config.json`): `--chat` offers the authoring tools only, and reads and writes no file of yours.
54
+
55
+ - [The page for agents](https://ryanjosephkamp.github.io/grooph/docs/agents/): the shortest path, every operation by example, what to do about each rule, and when no graph is the right answer.
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
+
59
+ ## What it does not do
60
+
61
+ It 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. `grooph export` places files and prints a kickoff prompt; pasting that prompt into your harness is your decision. `grooph hooks install` adds an event hook that records which sessions and subagents ran, for `grooph watch` to show; the hook appends one line per event, its script returns no decision to the harness, and an agent can read what it wrote through grooph's own tool.
62
+
63
+ Source, issues and the templates' recorded runs: [github.com/ryanjosephkamp/grooph](https://github.com/ryanjosephkamp/grooph). MIT. The app this package serves holds other people's code; their names and licenses are in `THIRD-PARTY-NOTICES.md`, beside this file.
@@ -0,0 +1,450 @@
1
+ # Third-party notices
2
+
3
+ grooph 0.4.1 is under the MIT license (`LICENSE`). Two of its files are built files that 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
+
5
+ ## In the app (`dist/app/`), which `grooph watch` serves
6
+
7
+ 18 packages, each with the license text its own package carries:
8
+
9
+ ### @xyflow/react 12.11.6
10
+
11
+ License: MIT · https://reactflow.dev
12
+
13
+ ```text
14
+ MIT License
15
+
16
+ Copyright (c) 2019-2025 webkid GmbH
17
+
18
+ Permission is hereby granted, free of charge, to any person obtaining a copy
19
+ of this software and associated documentation files (the "Software"), to deal
20
+ in the Software without restriction, including without limitation the rights
21
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22
+ copies of the Software, and to permit persons to whom the Software is
23
+ furnished to do so, subject to the following conditions:
24
+
25
+ The above copyright notice and this permission notice shall be included in all
26
+ copies or substantial portions of the Software.
27
+
28
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34
+ SOFTWARE.
35
+ ```
36
+
37
+ ### @xyflow/system 0.0.82
38
+
39
+ License: MIT · https://github.com/xyflow/xyflow
40
+
41
+ ```text
42
+ MIT License
43
+
44
+ Copyright (c) 2019-2025 webkid GmbH
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining a copy
47
+ of this software and associated documentation files (the "Software"), to deal
48
+ in the Software without restriction, including without limitation the rights
49
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
50
+ copies of the Software, and to permit persons to whom the Software is
51
+ furnished to do so, subject to the following conditions:
52
+
53
+ The above copyright notice and this permission notice shall be included in all
54
+ copies or substantial portions of the Software.
55
+
56
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
58
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
59
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
60
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
61
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
62
+ SOFTWARE.
63
+ ```
64
+
65
+ ### classcat 5.0.5
66
+
67
+ License: MIT · jorgebucaran/classcat
68
+
69
+ ```text
70
+ Copyright © Jorge Bucaran <<https://jorgebucaran.com>>
71
+
72
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
73
+
74
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
77
+ ```
78
+
79
+ ### d3-color 3.1.0
80
+
81
+ License: ISC · https://d3js.org/d3-color/
82
+
83
+ ```text
84
+ Copyright 2010-2022 Mike Bostock
85
+
86
+ Permission to use, copy, modify, and/or distribute this software for any purpose
87
+ with or without fee is hereby granted, provided that the above copyright notice
88
+ and this permission notice appear in all copies.
89
+
90
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
91
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
92
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
93
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
94
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
95
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
96
+ THIS SOFTWARE.
97
+ ```
98
+
99
+ ### d3-dispatch 3.0.1
100
+
101
+ License: ISC · https://d3js.org/d3-dispatch/
102
+
103
+ ```text
104
+ Copyright 2010-2021 Mike Bostock
105
+
106
+ Permission to use, copy, modify, and/or distribute this software for any purpose
107
+ with or without fee is hereby granted, provided that the above copyright notice
108
+ and this permission notice appear in all copies.
109
+
110
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
111
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
112
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
113
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
114
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
115
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
116
+ THIS SOFTWARE.
117
+ ```
118
+
119
+ ### d3-drag 3.0.0
120
+
121
+ License: ISC · https://d3js.org/d3-drag/
122
+
123
+ ```text
124
+ Copyright 2010-2021 Mike Bostock
125
+
126
+ Permission to use, copy, modify, and/or distribute this software for any purpose
127
+ with or without fee is hereby granted, provided that the above copyright notice
128
+ and this permission notice appear in all copies.
129
+
130
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
131
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
132
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
133
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
134
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
135
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
136
+ THIS SOFTWARE.
137
+ ```
138
+
139
+ ### d3-ease 3.0.1
140
+
141
+ License: BSD-3-Clause · https://d3js.org/d3-ease/
142
+
143
+ ```text
144
+ Copyright 2010-2021 Mike Bostock
145
+ Copyright 2001 Robert Penner
146
+ All rights reserved.
147
+
148
+ Redistribution and use in source and binary forms, with or without modification,
149
+ are permitted provided that the following conditions are met:
150
+
151
+ * Redistributions of source code must retain the above copyright notice, this
152
+ list of conditions and the following disclaimer.
153
+
154
+ * Redistributions in binary form must reproduce the above copyright notice,
155
+ this list of conditions and the following disclaimer in the documentation
156
+ and/or other materials provided with the distribution.
157
+
158
+ * Neither the name of the author nor the names of contributors may be used to
159
+ endorse or promote products derived from this software without specific prior
160
+ written permission.
161
+
162
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
163
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
164
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
165
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
166
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
167
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
168
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
169
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
170
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
171
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
172
+ ```
173
+
174
+ ### d3-interpolate 3.0.1
175
+
176
+ License: ISC · https://d3js.org/d3-interpolate/
177
+
178
+ ```text
179
+ Copyright 2010-2021 Mike Bostock
180
+
181
+ Permission to use, copy, modify, and/or distribute this software for any purpose
182
+ with or without fee is hereby granted, provided that the above copyright notice
183
+ and this permission notice appear in all copies.
184
+
185
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
186
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
187
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
188
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
189
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
190
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
191
+ THIS SOFTWARE.
192
+ ```
193
+
194
+ ### d3-selection 3.0.0
195
+
196
+ License: ISC · https://d3js.org/d3-selection/
197
+
198
+ ```text
199
+ Copyright 2010-2021 Mike Bostock
200
+
201
+ Permission to use, copy, modify, and/or distribute this software for any purpose
202
+ with or without fee is hereby granted, provided that the above copyright notice
203
+ and this permission notice appear in all copies.
204
+
205
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
206
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
207
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
208
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
209
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
210
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
211
+ THIS SOFTWARE.
212
+ ```
213
+
214
+ ### d3-timer 3.0.1
215
+
216
+ License: ISC · https://d3js.org/d3-timer/
217
+
218
+ ```text
219
+ Copyright 2010-2021 Mike Bostock
220
+
221
+ Permission to use, copy, modify, and/or distribute this software for any purpose
222
+ with or without fee is hereby granted, provided that the above copyright notice
223
+ and this permission notice appear in all copies.
224
+
225
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
226
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
227
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
228
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
229
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
230
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
231
+ THIS SOFTWARE.
232
+ ```
233
+
234
+ ### d3-transition 3.0.1
235
+
236
+ License: ISC · https://d3js.org/d3-transition/
237
+
238
+ ```text
239
+ Copyright 2010-2021 Mike Bostock
240
+
241
+ Permission to use, copy, modify, and/or distribute this software for any purpose
242
+ with or without fee is hereby granted, provided that the above copyright notice
243
+ and this permission notice appear in all copies.
244
+
245
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
246
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
247
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
248
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
249
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
250
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
251
+ THIS SOFTWARE.
252
+ ```
253
+
254
+ ### d3-zoom 3.0.0
255
+
256
+ License: ISC · https://d3js.org/d3-zoom/
257
+
258
+ ```text
259
+ Copyright 2010-2021 Mike Bostock
260
+
261
+ Permission to use, copy, modify, and/or distribute this software for any purpose
262
+ with or without fee is hereby granted, provided that the above copyright notice
263
+ and this permission notice appear in all copies.
264
+
265
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
266
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
267
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
268
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
269
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
270
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
271
+ THIS SOFTWARE.
272
+ ```
273
+
274
+ ### fflate 0.8.3
275
+
276
+ License: MIT · https://101arrowz.github.io/fflate
277
+
278
+ ```text
279
+ MIT License
280
+
281
+ Copyright (c) 2026 Arjun Barrett
282
+
283
+ Permission is hereby granted, free of charge, to any person obtaining a copy
284
+ of this software and associated documentation files (the "Software"), to deal
285
+ in the Software without restriction, including without limitation the rights
286
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
287
+ copies of the Software, and to permit persons to whom the Software is
288
+ furnished to do so, subject to the following conditions:
289
+
290
+ The above copyright notice and this permission notice shall be included in all
291
+ copies or substantial portions of the Software.
292
+
293
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
294
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
295
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
296
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
297
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
298
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
299
+ SOFTWARE.
300
+ ```
301
+
302
+ ### react 19.3.0
303
+
304
+ License: MIT · https://react.dev/
305
+
306
+ ```text
307
+ MIT License
308
+
309
+ Copyright (c) Meta Platforms, Inc. and affiliates.
310
+
311
+ Permission is hereby granted, free of charge, to any person obtaining a copy
312
+ of this software and associated documentation files (the "Software"), to deal
313
+ in the Software without restriction, including without limitation the rights
314
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
315
+ copies of the Software, and to permit persons to whom the Software is
316
+ furnished to do so, subject to the following conditions:
317
+
318
+ The above copyright notice and this permission notice shall be included in all
319
+ copies or substantial portions of the Software.
320
+
321
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
322
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
323
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
324
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
325
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
326
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
327
+ SOFTWARE.
328
+ ```
329
+
330
+ ### react-dom 19.3.0
331
+
332
+ License: MIT · https://react.dev/
333
+
334
+ ```text
335
+ MIT License
336
+
337
+ Copyright (c) Meta Platforms, Inc. and affiliates.
338
+
339
+ Permission is hereby granted, free of charge, to any person obtaining a copy
340
+ of this software and associated documentation files (the "Software"), to deal
341
+ in the Software without restriction, including without limitation the rights
342
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
343
+ copies of the Software, and to permit persons to whom the Software is
344
+ furnished to do so, subject to the following conditions:
345
+
346
+ The above copyright notice and this permission notice shall be included in all
347
+ copies or substantial portions of the Software.
348
+
349
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
350
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
351
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
352
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
353
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
354
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
355
+ SOFTWARE.
356
+ ```
357
+
358
+ ### scheduler 0.28.0
359
+
360
+ License: MIT · https://react.dev/
361
+
362
+ ```text
363
+ MIT License
364
+
365
+ Copyright (c) Meta Platforms, Inc. and affiliates.
366
+
367
+ Permission is hereby granted, free of charge, to any person obtaining a copy
368
+ of this software and associated documentation files (the "Software"), to deal
369
+ in the Software without restriction, including without limitation the rights
370
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
371
+ copies of the Software, and to permit persons to whom the Software is
372
+ furnished to do so, subject to the following conditions:
373
+
374
+ The above copyright notice and this permission notice shall be included in all
375
+ copies or substantial portions of the Software.
376
+
377
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
378
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
379
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
380
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
381
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
382
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
383
+ SOFTWARE.
384
+ ```
385
+
386
+ ### use-sync-external-store 1.7.0
387
+
388
+ License: MIT · https://github.com/react/react
389
+
390
+ ```text
391
+ MIT License
392
+
393
+ Copyright (c) Meta Platforms, Inc. and affiliates.
394
+
395
+ Permission is hereby granted, free of charge, to any person obtaining a copy
396
+ of this software and associated documentation files (the "Software"), to deal
397
+ in the Software without restriction, including without limitation the rights
398
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
399
+ copies of the Software, and to permit persons to whom the Software is
400
+ furnished to do so, subject to the following conditions:
401
+
402
+ The above copyright notice and this permission notice shall be included in all
403
+ copies or substantial portions of the Software.
404
+
405
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
406
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
407
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
408
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
409
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
410
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
411
+ SOFTWARE.
412
+ ```
413
+
414
+ ### zustand 4.5.7
415
+
416
+ License: MIT · https://github.com/pmndrs/zustand
417
+
418
+ ```text
419
+ MIT License
420
+
421
+ Copyright (c) 2019 Paul Henschel
422
+
423
+ Permission is hereby granted, free of charge, to any person obtaining a copy
424
+ of this software and associated documentation files (the "Software"), to deal
425
+ in the Software without restriction, including without limitation the rights
426
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
427
+ copies of the Software, and to permit persons to whom the Software is
428
+ furnished to do so, subject to the following conditions:
429
+
430
+ The above copyright notice and this permission notice shall be included in all
431
+ copies or substantial portions of the Software.
432
+
433
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
434
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
435
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
436
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
437
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
438
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
439
+ SOFTWARE.
440
+ ```
441
+
442
+ The app's fonts, Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono, are under the SIL Open Font License 1.1. Their license texts are beside them, in `dist/app/assets/fonts/`.
443
+
444
+ ## In the command (`dist/bundle/grooph.js`)
445
+
446
+ grooph's own code and no other package's.
447
+
448
+ ## Installed beside it, and not in this package
449
+
450
+ - `@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,5 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/compile-BAvi-rd0.js","assets/share-BzIzB4EY.js","assets/groups-CjEPiwfv.js","assets/Import-ByMI7Uz9.js","assets/index-CeaKwKuU.js","assets/templates-CoNTzuuz.js","assets/runs-DcjRi7S6.js","assets/EmbedApp-390Z2d4x.js","assets/EmbedApp-CWb_HGpW.css","assets/screens-aIjMSzZg.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-CeaKwKuU.js";import{D as o,F as s,I as c,Rt as l,Tt as u,b as d,cn as f,fn as p,i as m,n as h,r as g,rn as _,sn as v,tt as y}from"./share-BzIzB4EY.js";import{d as b,i as x,l as S,m as C,n as w,o as T,p as E,r as D,t as ee,u as te}from"./templates-CoNTzuuz.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-DcjRi7S6.js";var k=t(e(),1),de=String.raw`^(?!#\/(?:run\?live$|live$|run\/[^?]+$|templates$|embed(?:\?|$)|templates\/(?:built-in|yours|plan)\/[^/?]+(?:\/use)?$|g\/[^/?]+(?:\?new)?$|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-BAvi-rd0.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&&_.includes(t)?t:`claude-code`,r=f(e);if(!r.doc)return{ok:!1,target:t??`(none)`,reason:`schema`,issues:r.issues};let i=y(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]=g(r);return new Uint8Array(m(t,{level:6}))}var Ce=e=>v(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=d(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=>w[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 E((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(C(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=>v(e),V=(e,t,n=!1)=>`#/templates/${e}/${encodeURIComponent(t)}${n?`/use`:``}`,Xe=[];function Ze(){let[e,t]=(0,k.useState)(T);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 Qe(){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=Ze(),a=i??Xe,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`),te().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<$e||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.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)(H,{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)(H,{label:e[0].toUpperCase()+e.slice(1),options:D[e],text:e=>e[0].toUpperCase()+e.slice(1),title:t=>x[e][t],level:t=>w[e][t],on:n[e],onToggle:t=>S(e,z(n[e],t))},e)),s.length>0?(0,F.jsx)(H,{label:`Tags`,options:y,text:e=>e,on:n.tags,onToggle:e=>S(`tags`,z(n.tags,e)),more:s.length>$e?(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)(U,{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)(U,{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:ee}),(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)(U,{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 $e=8;function H({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 U({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:V(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)(et,{profile:n.profile})]})]})},t.id)})})}function et({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":w[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))})}var W=`https://github.com/ryanjosephkamp/grooph`,G=`/grooph/docs/`,K=[{id:`grooph`,label:`Grooph`,dot:`oklch(0.85 0.14 165)`},{id:`meteor`,label:`Meteor`,dot:`oklch(0.9 0.2 124)`}],tt=`groophTheme`,nt=()=>K.find(e=>e.id===document.documentElement.dataset.theme)?.id??K[0].id;function rt(){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 it=()=>(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 at(){let[e,t]=(0,k.useState)(!1),[n,r]=(0,k.useState)(nt),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)(rt,{}),(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:G,children:`Docs`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${G}field-guide/`,children:`Field guide`})}),(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{href:W,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)(ot,{theme:n,onTheme:r})]})})}function ot({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=K.findIndex(t=>t.id===e);(0,k.useEffect)(()=>{if(!n)return;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===K[0].id?delete document.documentElement.dataset.theme:document.documentElement.dataset.theme=e;try{localStorage.setItem(tt,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: ${K[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.jsxs)(`ul`,{className:`site-theme-list`,id:`site-theme-list`,role:`menu`,"aria-label":`Theme`,hidden:!n,onKeyDown:e=>{let t=o.current.indexOf(document.activeElement),n=e.key===`ArrowDown`?(t+1)%q:e.key===`ArrowUp`?(t-1+q)%q:e.key===`Home`?0:e.key===`End`?q-1:-1;n>=0?(e.preventDefault(),o.current[n]?.focus()):e.key===`Escape`?(e.stopPropagation(),c()):e.key===`Tab`&&r(!1)},children:[K.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)(it,{})]})},t.id)),(0,F.jsx)(`li`,{role:`none`,children:(0,F.jsxs)(`button`,{type:`button`,role:`menuitem`,"aria-haspopup":`menu`,tabIndex:-1,"data-pictures":``,ref:e=>void(o.current[K.length]=e),onClick:e=>{e.currentTarget.dataset.pressed=``,r(!1)},children:[(0,F.jsx)(`span`,{className:`site-theme-dot`,style:{"--dot":`#0000`},"aria-hidden":`true`}),`Picture theme`]})})]})]})}var q=K.length+1,st=`/grooph/assets/site-icons.v1.svg`,ct=[{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 lt(){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)(rt,{}),(0,F.jsx)(`span`,{children:`grooph`})]}),(0,F.jsx)(`p`,{children:`Loop graphs for coding agents. 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:`${G}quickstart/`,children:`Quickstart`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${G}field-guide/`,children:`Field guide`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:G,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:`${W}/issues`,rel:`noopener`,children:`Report a bug or suggest a feature`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:`${G}community/`,children:`Community loops`})}),(0,F.jsx)(`li`,{children:(0,F.jsx)(`a`,{href:W,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:ct.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:`${st}#${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:`${st}#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:`${W}#license-and-author`,rel:`noopener`,children:`Credits`})]})]})})}function ut({children:e}){let[t,n]=(0,k.useState)(`idle`),[r,i]=(0,k.useState)(null),[a,o]=(0,k.useState)(680),s=(0,k.useRef)(null);return(0,k.useEffect)(()=>{if(r===null)return;let e=e=>{let t=e.data;e.origin===location.origin&&e.source===s.current?.contentWindow&&t?.grooph===`embed-height`&&Number(t.height)>0&&o(Math.min(Number(t.height),4e3))};return addEventListener(`message`,e),()=>removeEventListener(`message`,e)},[r]),r===null?(0,F.jsxs)(F.Fragment,{children:[e,(0,F.jsxs)(`p`,{className:`land-play`,children:[(0,F.jsxs)(`button`,{type:`button`,className:`btn`,disabled:t===`loading`,onClick:async()=>{n(`loading`);try{let e=await fetch(`/grooph/demo/run.txt`);if(!e.ok)throw Error(String(e.status));i((await e.text()).trim()),n(`idle`)}catch{n(`failed`)}},children:[(0,F.jsx)(`span`,{"aria-hidden":`true`,children:`▶`}),` Watch a recorded run`]}),(0,F.jsx)(`span`,{className:`muted`,role:`status`,children:t===`loading`?`Fetching the run…`:t===`failed`?`The run could not be fetched. It needs a network the first time.`:``})]})]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`iframe`,{ref:s,className:`land-run-frame`,title:`A recorded run of the heterogeneous critic template, played step by step`,src:`/grooph/#/embed?d=${r}&play=1`,style:{height:a}}),(0,F.jsxs)(`figcaption`,{className:`muted`,children:[`A run Claude Code made on 20 September, replayed from its own notes. The critic fails the first round, the builder goes again, and the run halts at the human gate.`,` `,(0,F.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>i(null),children:`Back to the picture`})]})]})}var J=`review-gate`,dt=`/grooph-design a builder and a critic that loop until the checkout tests pass, and ask me before merging`,ft,Y=()=>n(`front`,()=>r(()=>import(`./front-BRl7rl-u.js`),[])).then(e=>ft=e),pt=600;function mt(){let[e,t]=(0,k.useState)(ft),[n,r]=(0,k.useState)(!1);return(0,k.useEffect)(()=>{if(e)return;let n=!0,i=setTimeout(()=>n&&r(!0),pt);return Y().then(e=>n&&t(e),()=>n&&r(!0)),()=>{n=!1,clearTimeout(i)}},[]),e??(n?null:void 0)}var ht=[`No account`,`Built for a phone's screen`,`Opens offline after a first visit`,`Graphs stay on your device`],X=`${G}field-guide/poster.svg`;function gt({device:e}){let[t,n]=(0,k.useState)(!0),r=mt();return r===void 0?(0,F.jsx)(`div`,{className:`loading`,children:`Opening…`}):(0,F.jsxs)(`div`,{className:`land`,children:[(0,F.jsx)(at,{}),(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:[`Loop graphs `,(0,F.jsx)(`span`,{children:`for coding agents.`})]})]}),(0,F.jsxs)(`div`,{className:`land-hero-text`,children:[(0,F.jsx)(`p`,{className:`land-lede`,children:`Draw who builds, who checks, where a person decides and when the work stops. grooph checks that every loop names a stop, then compiles the graph into a package a Claude Code session runs. Your harness runs it; grooph never does.`}),(0,F.jsxs)(`div`,{className:`land-cta`,children:[(0,F.jsx)(`a`,{className:`btn btn-primary`,href:V(`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:ht.map(e=>(0,F.jsxs)(`li`,{children:[(0,F.jsx)(it,{}),e]},e))})]}),r?(0,F.jsx)(`figure`,{className:`land-figure`,children:(0,F.jsxs)(ut,{children:[(0,F.jsx)(`div`,{className:`land-picture`,role:`img`,"aria-label":`The review gate template as a graph: 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:V(`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:`Every loop names its stop.`}),` The validator refuses a loop without one, and warns when a loop has no budget and either no cap or a cap above five rounds. Where a graph asks for it, it refuses a critic that shares the builder’s context, and it refuses a step marked irreversible with no human gate before it.`]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`strong`,{children:`The graph is the contract.`}),` The package tells the session to run it as drawn: named subagents, stops in order, a halt at every human gate. grooph does not enforce it while it runs; eighteen of the twenty runs counted pass the checks of it, and two say why they do not.`]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`strong`,{children:`Every run is asked for a record.`}),` Notes, rounds and why it stopped, in a folder a monitor reads. To go on, a halted run is resumed in the same session with the person’s answer and its run id.`]})]})})}),(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:`${G}quickstart/`,children:`installed`}),`, describe the work. The session proposes one to three graphs, sends a link to compare them on your phone, and places the package you pick.`]}),(0,F.jsx)(vt,{text:dt})]}),(0,F.jsxs)(`div`,{className:`land-way`,children:[(0,F.jsx)(`h3`,{children:`Open a template`}),(0,F.jsx)(`p`,{children:`Twenty templates, each with a recorded run of its own. Open one, fill in its blanks, and export the package or the graph.`}),(0,F.jsxs)(`div`,{className:`land-way-actions`,children:[(0,F.jsx)(`a`,{className:`btn btn-primary`,href:V(`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)(_t,{})})]})}),(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:`Loop 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:V(`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:X,"aria-label":`Open the poster of the twenty loop shapes`,children:(0,F.jsx)(`img`,{src:X,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 what each one’s recorded run showed: `,(0,F.jsx)(`a`,{href:`${G}field-guide/`,children:`the field guide`}),`, and`,` `,(0,F.jsx)(`a`,{href:X,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 drawing`}),(0,F.jsxs)(`ul`,{className:`land-more-list`,children:[(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`h3`,{children:`Put a graph on any page`}),(0,F.jsxs)(`p`,{children:[`One line of HTML shows a live graph a reader can pan, zoom and tap, or a recorded run that plays. `,(0,F.jsx)(`a`,{href:`${G}exports/`,children:`How to embed`}),`.`]})]}),(0,F.jsxs)(`li`,{children:[(0,F.jsx)(`h3`,{children:`See what is running`}),(0,F.jsxs)(`p`,{children:[`A hook records when each session and subagent starts and stops, and nothing they say. A screen shows it live.`,` `,(0,F.jsx)(`a`,{href:`${G}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:`${G}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:[`What is shown, `,(0,F.jsx)(`mark`,{children:`and what is not`})]}),(0,F.jsxs)(`p`,{children:[`The latest kept run of each of the twenty templates is counted: in those a session stopped where its graph said, at a passed bar or a human gate, and left a record; eighteen of the twenty pass the project’s checks. No round cap or budget is on record as firing. In a paired comparison on four small tasks the package showed no quality advantage over a prompt derived from it. grooph runs no agent, calls no model and needs no hosted service.`,` `,(0,F.jsx)(`a`,{href:`${G}claims/`,children:`Every claim and its evidence`}),`.`]})]})})]}),(0,F.jsx)(lt,{})]})}function _t(){return(0,F.jsx)(`p`,{className:`land-device-line`,children:(0,F.jsx)(`a`,{className:`btn`,href:`#/`,children:`Your graphs`})})}function vt({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 yt({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 bt({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 xt=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()},St=()=>n(`Import`,()=>r(()=>import(`./Import-ByMI7Uz9.js`),__vite__mapDeps([3,4,1,5,6]))),Ct=e=>e?.scrollIntoView({block:`nearest`});function wt({runs:e}){return(0,F.jsx)(`ul`,{className:`run-rows`,"aria-label":`Runs`,children:e.map(e=>{let t=c(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)})})}function Tt({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),[m,g]=(0,k.useState)(null),[_,v]=(0,k.useState)(null),y=(0,k.useRef)(null),[b,x]=(0,k.useState)(null),S=(0,k.useCallback)(async()=>{n(await ae()),i(await ce()),o((await O()).persistent)},[]);(0,k.useEffect)(()=>void S(),[S]);let C={open:e,route:h,problem:c,offer:v,close:()=>x(null)},w=(e,t)=>void St().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.`})),T=e=>{e&&w(e.name,async t=>t.importText(await e.text(),e,C))};(0,k.useEffect)(()=>{let e=e=>{let t=e.clipboardData?.getData(`text/plain`);t&&e.target instanceof Element&&!e.target.closest(`input, textarea, select`)&&St().then(e=>e.openPasted(t,C,!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){v(e=>e&&{...e,exists:n.exists});return}v(null),location.hash=V(`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 .grooph.json`,(0,F.jsx)(`input`,{ref:y,type:`file`,accept:`.json,application/json`,onChange:e=>{T(e.target.files?.[0]),e.target.value=``}})]}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-large`,onClick:()=>w(`what you paste`,e=>x(t=>t?null:e.PasteBox)),children:`Paste a document`})]}),b?(0,F.jsx)(b,{host:C}):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)(yt,{}),_?(0,F.jsxs)(`div`,{className:`offer`,role:`alert`,ref:Ct,children:[(0,F.jsxs)(`p`,{children:[(0,F.jsxs)(`strong`,{children:[_.name,` is a template:`]}),` `,_.doc.template.title,_.doc.template.kind===`fragment`?` (a fragment)`:``,`.`,` `,_.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.`]}),_.refusal?(0,F.jsx)(`pre`,{className:`issue-lines`,children:_.refusal.issues.map(p).join(`
4
+ `)}):null,_.exists?(0,F.jsxs)(`p`,{className:`field-hint`,children:[`Yours already has a template with the id `,(0,F.jsx)(`span`,{className:`mono`,children:_.doc.id}),` (version `,_.exists.version,`). Replacing it makes version `,Math.max(_.exists.version,_.doc.version)+1,`.`]}):null,(0,F.jsxs)(`div`,{className:`offer-actions`,children:[_.refusal?null:_.exists?(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>void E(_.doc,!0),children:`Replace yours`}):(0,F.jsx)(`button`,{type:`button`,className:`btn btn-primary`,onClick:()=>void E(_.doc,!1),children:`Add to Yours`}),(0,F.jsx)(`button`,{type:`button`,className:`btn`,onClick:async()=>{let t=await ue(_.doc);v(null),e(t.key)},children:`Open it as a graph`}),(0,F.jsx)(`button`,{type:`button`,className:`btn btn-quiet`,onClick:()=>v(null),children:`Cancel`})]})]}):null,s?(0,F.jsxs)(`div`,{className:`refusal`,role:`alert`,ref:Ct,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(p).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)(gt,{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 or a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-map.json`}),` operation map.`]})]}),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:`Loop graphs for coding agents. Draw, validate, export.`})]}),(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 or a `,(0,F.jsx)(`span`,{className:`mono`,children:`.grooph-map.json`}),` operation map.`]})]}):(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)(Et,{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 S()}}):(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`,` · `,xt(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),g(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)(wt,{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 S()},children:`Duplicate`}),m===t.key?(0,F.jsx)(`button`,{type:`button`,className:`btn btn-danger`,onClick:async()=>{await ie(t.key),u(null),g(null),await S()},children:`Delete for good`}):(0,F.jsx)(`button`,{type:`button`,className:`btn btn-danger-text`,onClick:()=>g(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)(wt,{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 Et(e){let{record:t}=e,n=u(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)(Dt,{exportedAs:r,nextId:n,onKeep:()=>e.onSave(r)}):null]})}function Dt({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 Ot=i({App:()=>Lt,loadScreens:()=>Q,ready:()=>Pt}),kt=(0,k.lazy)(()=>n(`EmbedApp`,()=>r(()=>import(`./EmbedApp-390Z2d4x.js`),__vite__mapDeps([7,4,1,8]))).then(e=>({default:e.EmbedApp}))),Z;function Q(){return n(`screens`,()=>r(()=>import(`./screens-aIjMSzZg.js`),__vite__mapDeps([9,4,1,5,6,10]))).then(e=>Z=e)}function At(e){return!jt.has(Ft(e).name)}var jt=new Set([`library`,`about`,`templates`,`embed`]),Mt=e=>e.name===`template`&&e.source===`built-in`,Nt=300;function Pt(e){let t=[];return At(e)&&t.push(Q()),new RegExp(fe).test(e)&&t.push(S()),new RegExp(de).test(e)&&t.push(Promise.race([Y(),new Promise(e=>setTimeout(e,Nt))])),Promise.all(t.map(e=>e.catch(()=>void 0)))}function $(e){try{return decodeURIComponent(e)}catch{return e}}function Ft(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:$(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:$(n[2]),use:n[3]!==void 0};let r=/^#\/g\/([^/?]+)(\?new)?$/.exec(e);if(r)return{name:`graph`,key:$(r[1]),fresh:r[2]!==void 0};if(e.startsWith(`#/open?`)){let t=/[?&]c=([^&]*)/.exec(e)?.[1];return{name:`open`,payload:o(e)??``,...t?{candidate:$(t)}:{}}}return{name:`library`}}function It(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 Lt(){let[e,t]=(0,k.useState)(()=>Ft(location.hash));(0,k.useEffect)(()=>{let e=()=>{t(Ft(location.hash)),window.scrollTo(0,0)};return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]);let n=It(()=>Z!==void 0,Q,e),r=It(()=>T()!==void 0,S,e);(0,k.useEffect)(()=>void Y().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)(gt,{});if(Mt(e)&&r!==`yes`)return i(r);if(e.name===`templates`)return(0,F.jsx)(Qe,{});if(e.name===`library`)return(0,F.jsx)(Tt,{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)(kt,{})});if(!Z)return i(n);let{EditorScreen:a,LiveRun:o,LiveSessions:s,OpenScreen:c,StoredRun:l,TemplateView:u,UseTemplate:d}=Z;if(e.name===`graph`)return(0,F.jsx)(a,{graphKey:e.key,fresh:e.fresh},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{Lt as App,Se as S,Ee as _,V as a,ye as b,Ge as c,Je as d,Ye as f,De as g,be as h,bt as i,We as l,Q as loadScreens,xe as m,Dt as n,Ze as o,I as p,yt as r,Pt as ready,qe as s,Ot as t,Ke as u,we as v,Te as x,Ce as y};