forge-openclaw-plugin 0.2.60 → 0.2.61

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 (46) hide show
  1. package/README.md +93 -46
  2. package/dist/assets/{board-B1V3M__K.js → board-DThHV1D8.js} +1 -1
  3. package/dist/assets/index-7gvVCqnV.css +1 -0
  4. package/dist/assets/index-_Cn6Prym.js +90 -0
  5. package/dist/assets/{motion-CltSTItx.js → motion-BtTJtHCw.js} +1 -1
  6. package/dist/assets/{table-B-VrSFx8.js → table-Bnw6pcwN.js} +1 -1
  7. package/dist/assets/{ui-DUqM4jkt.js → ui-CnVxFkj0.js} +1 -1
  8. package/dist/assets/{vendor-C0otBhgu.js → vendor-BgZ3YrRd.js} +212 -207
  9. package/dist/gamification-previews/dark-fantasy-item-trophy-tasks-anvil-marathon.webp +0 -0
  10. package/dist/gamification-previews/dark-fantasy-item-trophy-xp-levels-the-first-heat.webp +0 -0
  11. package/dist/gamification-previews/dark-fantasy-item-unlock-streaks-molten-crown-fire.webp +0 -0
  12. package/dist/gamification-previews/dark-fantasy-mascot.webp +0 -0
  13. package/dist/gamification-previews/dramatic-smithie-item-trophy-tasks-anvil-marathon.webp +0 -0
  14. package/dist/gamification-previews/dramatic-smithie-item-trophy-xp-levels-the-first-heat.webp +0 -0
  15. package/dist/gamification-previews/dramatic-smithie-item-unlock-streaks-molten-crown-fire.webp +0 -0
  16. package/dist/gamification-previews/dramatic-smithie-mascot.webp +0 -0
  17. package/dist/gamification-previews/mind-locksmith-item-trophy-tasks-anvil-marathon.webp +0 -0
  18. package/dist/gamification-previews/mind-locksmith-item-trophy-xp-levels-the-first-heat.webp +0 -0
  19. package/dist/gamification-previews/mind-locksmith-item-unlock-streaks-molten-crown-fire.webp +0 -0
  20. package/dist/gamification-previews/mind-locksmith-mascot.webp +0 -0
  21. package/dist/index.html +7 -7
  22. package/dist/openclaw/parity.js +27 -0
  23. package/dist/openclaw/plugin-entry-shared.js +2 -2
  24. package/dist/openclaw/plugin-sdk-types.d.ts +2 -1
  25. package/dist/openclaw/routes.d.ts +4 -0
  26. package/dist/openclaw/routes.js +112 -3
  27. package/dist/openclaw/tools.js +32 -4
  28. package/dist/server/server/migrations/059_data_backup_retention.sql +2 -0
  29. package/dist/server/server/src/app.js +125 -43
  30. package/dist/server/server/src/data-management-types.js +2 -0
  31. package/dist/server/server/src/health.js +40 -0
  32. package/dist/server/server/src/openapi.js +398 -7
  33. package/dist/server/server/src/repositories/rewards.js +60 -0
  34. package/dist/server/server/src/services/data-management.js +32 -2
  35. package/dist/server/server/src/services/doctor.js +762 -0
  36. package/dist/server/server/src/services/gamification.js +75 -3
  37. package/dist/server/src/lib/api.js +9 -0
  38. package/dist/server/src/lib/gamification-catalog.js +1 -1
  39. package/openclaw.plugin.json +85 -3
  40. package/package.json +8 -4
  41. package/server/migrations/059_data_backup_retention.sql +2 -0
  42. package/skills/forge-openclaw/SKILL.md +38 -19
  43. package/skills/forge-openclaw/entity_conversation_playbooks.md +66 -8
  44. package/skills/forge-openclaw/psyche_entity_playbooks.md +23 -0
  45. package/dist/assets/index-BwKAPo98.css +0 -1
  46. package/dist/assets/index-Dy7c-dRY.js +0 -90
package/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # Forge OpenClaw Plugin
2
2
 
3
- `forge-openclaw-plugin` is the publishable OpenClaw package for Forge.
4
- When the plugin targets `localhost` or `127.0.0.1`, it auto-starts the bundled Forge runtime so the local install path stays one-step.
3
+ `forge-openclaw-plugin` is the OpenClaw bridge for Forge.
5
4
 
6
- OpenClaw install note:
5
+ It gives OpenClaw a clear way to:
7
6
 
8
- - `openclaw plugins enable forge-openclaw-plugin` is not always enough by itself.
9
- - If `forge-openclaw-plugin` is missing from `plugins.allow`, OpenClaw can still refuse to load it.
10
- - The install section below includes the `node -e ...` step that repairs `plugins.allow` safely.
11
- - I re-verified on April 21, 2026 that OpenClaw `2026.4.15` still blocks both the published package install and the repo-local install because Forge launches a local runtime and gets flagged by the installer scanner. The bypass sections below are still current.
7
+ - start or reach the local Forge runtime
8
+ - open the Forge web app
9
+ - read the current operator overview
10
+ - create and update goals, projects, issues, tasks, notes, wiki pages, health records, preferences, and Psyche records
11
+ - run Forge Doctor when the local setup looks wrong
12
+
13
+ When the plugin targets `localhost` or `127.0.0.1`, it can auto-start the bundled Forge runtime. That is why current OpenClaw installers may ask for explicit approval during install.
12
14
 
13
15
  ## Open the UI
14
16
 
@@ -35,10 +37,13 @@ If you want Forge to use a specific local data folder, set `dataRoot` in the plu
35
37
 
36
38
  Default data path:
37
39
 
38
- - local installs now default to the shared Forge home at `~/.forge/forge.sqlite`
39
- - set `dataRoot` only when you intentionally want a different shared database
40
+ - local installs default to `~/.forge/forge.sqlite`
41
+ - the user can set `dataRoot` to use another folder
42
+ - when `dataRoot` is set, Forge stores `forge.sqlite` directly inside that folder
43
+
44
+ If you want the data to live somewhere specific for persistence or backup reasons, set `dataRoot` explicitly in the plugin config and restart the gateway. When debugging storage, check the config and the live runtime path before moving or merging database files.
40
45
 
41
- If you want the data to live somewhere else for persistence or backup reasons, set `dataRoot` explicitly in the plugin config and restart the gateway.
46
+ The Forge web app also exposes these controls in `Settings -> Data`. Use it to see the live data folder, move current data, adopt an existing Forge data folder, create a manual backup, enable automatic backups, and choose how many days of automatic backups Forge should keep. Automatic retention only cleans automatic backups; manual and safety backups stay in place.
42
47
 
43
48
  ## What Forge looks like
44
49
 
@@ -116,8 +121,8 @@ Recommended shared setup:
116
121
 
117
122
  1. Run one shared Forge runtime.
118
123
  2. Point OpenClaw, Hermes, and the browser UI at that same runtime.
119
- 3. Let the default shared Forge home `~/.forge` stand unless you intentionally
120
- want a different shared database.
124
+ 3. Use one explicit shared `dataRoot` when several adapters must share the same
125
+ database.
121
126
  4. Create the human and bot users in `Settings -> Users`.
122
127
  5. Use `userId` on writes and `userIds` on reads.
123
128
 
@@ -130,9 +135,9 @@ Current sharing behavior is intentionally clear:
130
135
  tasks, notes, or strategy nodes
131
136
 
132
137
  If OpenClaw and Hermes are supposed to collaborate inside one Forge system, the
133
- important thing is not only matching `origin` and `port`. Local installs now
134
- converge on the same `~/.forge` data root automatically. Only override
135
- `dataRoot` when you deliberately want a different shared database.
138
+ important thing is not only matching `origin` and `port`. Either leave every
139
+ adapter on the default `~/.forge` root or set one explicit `dataRoot`, then
140
+ verify that each live runtime has opened that database.
136
141
 
137
142
  ## Strategies And Alignment Metrics
138
143
 
@@ -215,45 +220,91 @@ The sports UI route is `/forge/sports`, while the backend overview route remains
215
220
 
216
221
  ## Install
217
222
 
218
- Current OpenClaw builds should use package discovery:
223
+ ### Published Package
224
+
225
+ Use this path when you want the released Forge plugin from npm:
219
226
 
220
227
  ```bash
221
- openclaw plugins install forge-openclaw-plugin
228
+ openclaw plugins install --dangerously-force-unsafe-install forge-openclaw-plugin
222
229
  openclaw plugins enable forge-openclaw-plugin
223
- node -e 'const fs=require("fs"); const p=process.env.HOME+"/.openclaw/openclaw.json"; const j=JSON.parse(fs.readFileSync(p,"utf8")); j.plugins ??= {}; j.plugins.allow = Array.from(new Set([...(j.plugins.allow || []), "forge-openclaw-plugin"])); fs.writeFileSync(p, JSON.stringify(j, null, 2)+"\n");'
224
230
  openclaw gateway restart
225
231
  openclaw forge health
232
+ openclaw forge ui
226
233
  ```
227
234
 
228
- If the install path is blocked on your OpenClaw build, use this temporary npm bypass instead. This is still required on OpenClaw `2026.4.15` as of April 21, 2026:
235
+ Tested on OpenClaw `2026.5.4`: plain `openclaw plugins install forge-openclaw-plugin` is blocked by the plugin scanner because Forge contains local runtime startup helpers. The documented command uses OpenClaw's explicit approval flag and installs without the old config-edit workaround.
236
+
237
+ Check what OpenClaw loaded:
229
238
 
230
239
  ```bash
231
- npm install -g forge-openclaw-plugin
232
- node -e 'const cp=require("child_process"); const fs=require("fs"); const path=require("path"); const p=process.env.HOME+"/.openclaw/openclaw.json"; const j=JSON.parse(fs.readFileSync(p,"utf8")); const pluginPath=path.join(cp.execSync("npm root -g",{encoding:"utf8"}).trim(),"forge-openclaw-plugin"); j.plugins ??= {}; j.plugins.allow = Array.from(new Set([...(j.plugins.allow || []), "forge-openclaw-plugin"])); j.plugins.load ??= {}; j.plugins.load.paths = Array.from(new Set([...(j.plugins.load.paths || []), pluginPath])); j.plugins.entries ??= {}; j.plugins.entries["forge-openclaw-plugin"] = { enabled: true, config: { origin: "http://127.0.0.1", port: 4317, actorLabel: "", timeoutMs: 15000 } }; fs.writeFileSync(p, JSON.stringify(j, null, 2)+"\n"); console.log("Configured", pluginPath);'
233
- openclaw gateway restart
234
- openclaw plugins info forge-openclaw-plugin
240
+ openclaw plugins inspect forge-openclaw-plugin --runtime
235
241
  openclaw forge health
236
242
  ```
237
243
 
238
- That bypass still uses the published npm package. It just tells OpenClaw to load the npm-installed folder directly from `plugins.load.paths`, which avoids the installer block that still happens on OpenClaw `2026.4.15`.
239
-
240
- `openclaw plugins enable forge-openclaw-plugin` marks the plugin enabled, but it does not guarantee that `plugins.allow` was repaired. The `node -e ...` command above preserves the current allow list and appends `"forge-openclaw-plugin"` if it is missing.
244
+ ### Local Development From This Repo
241
245
 
242
- For release-parity local development from this repo:
246
+ Use this path when you are editing Forge and want OpenClaw to load this checkout directly:
243
247
 
244
248
  ```bash
245
- openclaw plugins install ./projects/forge/openclaw-plugin
249
+ openclaw plugins install --link --dangerously-force-unsafe-install ./openclaw-plugin
246
250
  openclaw plugins enable forge-openclaw-plugin
247
- node -e 'const fs=require("fs"); const p=process.env.HOME+"/.openclaw/openclaw.json"; const j=JSON.parse(fs.readFileSync(p,"utf8")); j.plugins ??= {}; j.plugins.allow = Array.from(new Set([...(j.plugins.allow || []), "forge-openclaw-plugin"])); fs.writeFileSync(p, JSON.stringify(j, null, 2)+"\n");'
248
251
  openclaw gateway restart
252
+ openclaw plugins inspect forge-openclaw-plugin --runtime
249
253
  openclaw forge health
250
254
  ```
251
255
 
252
- OpenClaw `2026.4.15` still blocks that repo-local install on this machine, so keep
253
- the repo folder on `plugins.load.paths`, make sure
254
- `openclaw plugins info forge-openclaw-plugin` still points at the local Forge source
255
- path, then restart the gateway and verify health. That fallback still keeps OpenClaw
256
- on the local code folder instead of switching to the published package.
256
+ Use `--link` for active local development. Omit `--link` when you want to test the copied package layout that a normal install receives.
257
+
258
+ If you are running the command from the monorepo root instead of the Forge repo root, use:
259
+
260
+ ```bash
261
+ openclaw plugins install --link --dangerously-force-unsafe-install ./projects/forge/openclaw-plugin
262
+ ```
263
+
264
+ ### Manual Fallback For Older OpenClaw Builds
265
+
266
+ Use this only if your OpenClaw installer does not support `--dangerously-force-unsafe-install` or still refuses to install Forge. It installs the npm package, adds the package folder to `plugins.load.paths`, enables the plugin, and keeps the default local Forge runtime on `127.0.0.1:4317`.
267
+
268
+ ```bash
269
+ npm install -g forge-openclaw-plugin
270
+ node <<'NODE'
271
+ const cp = require("node:child_process");
272
+ const fs = require("node:fs");
273
+ const path = require("node:path");
274
+
275
+ const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
276
+ const pluginPath = path.join(
277
+ cp.execSync("npm root -g", { encoding: "utf8" }).trim(),
278
+ "forge-openclaw-plugin"
279
+ );
280
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
281
+
282
+ config.plugins ??= {};
283
+ config.plugins.allow = Array.from(
284
+ new Set([...(config.plugins.allow ?? []), "forge-openclaw-plugin"])
285
+ );
286
+ config.plugins.load ??= {};
287
+ config.plugins.load.paths = Array.from(
288
+ new Set([...(config.plugins.load.paths ?? []), pluginPath])
289
+ );
290
+ config.plugins.entries ??= {};
291
+ config.plugins.entries["forge-openclaw-plugin"] = {
292
+ enabled: true,
293
+ config: {
294
+ origin: "http://127.0.0.1",
295
+ port: 4317,
296
+ actorLabel: "",
297
+ timeoutMs: 15000
298
+ }
299
+ };
300
+
301
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
302
+ console.log(`Configured ${pluginPath}`);
303
+ NODE
304
+ openclaw gateway restart
305
+ openclaw plugins inspect forge-openclaw-plugin --runtime
306
+ openclaw forge health
307
+ ```
257
308
 
258
309
  Equivalent config:
259
310
 
@@ -265,7 +316,7 @@ Equivalent config:
265
316
  "forge-openclaw-plugin": {
266
317
  enabled: true,
267
318
  config: {
268
- dataRoot: "~/.forge",
319
+ dataRoot: "/absolute/path/to/forge-data",
269
320
  actorLabel: "",
270
321
  apiToken: ""
271
322
  }
@@ -295,7 +346,8 @@ Recommended local behavior:
295
346
 
296
347
  - leave `actorLabel` blank so Forge can inherit the trusted local operator label automatically
297
348
  - leave `apiToken` blank for localhost and trusted Tailscale setups
298
- - leave `dataRoot` alone unless you intentionally want a different shared local Forge home
349
+ - keep `dataRoot` aligned across OpenClaw, Hermes, Codex, and the browser runtime when they should share data
350
+ - before changing or merging data roots, back up every candidate Forge database and verify which database the live runtime is using
299
351
 
300
352
  ## Doctor And Runtime Config
301
353
 
@@ -318,10 +370,12 @@ Diagnostic entrypoints:
318
370
 
319
371
  ```bash
320
372
  openclaw forge doctor
373
+ openclaw forge doctor --json
374
+ openclaw forge doctor --fix
321
375
  npm run doctor --prefix ./projects/forge
322
376
  ```
323
377
 
324
- The doctor output now includes `settingsFile` details such as the resolved path, validity, sync state, parse errors, and which override keys were applied from `forge.json`.
378
+ The doctor output includes explicit runtime, settings, SQLite storage, entity-link, hierarchy, reward, and gamification checks. It also returns concrete issues and proposed fixes. Fixes are never applied by a normal read; `openclaw forge doctor --fix` applies only Doctor-marked safe fixes.
325
379
 
326
380
  Then restart the gateway:
327
381
 
@@ -329,14 +383,7 @@ Then restart the gateway:
329
383
  openclaw gateway restart
330
384
  ```
331
385
 
332
- Older OpenClaw builds can keep using the repo-root fallback entry during the transition:
333
-
334
- ```bash
335
- openclaw plugins install ./projects/forge
336
- openclaw gateway restart
337
- ```
338
-
339
- That repo-local path is the fallback only. The published package stays on the SDK `definePluginEntry` entrypoint. The OpenClaw plugin id is `forge-openclaw-plugin`, while the product name stays `Forge` and the CLI namespace stays `forge`.
386
+ The OpenClaw plugin id is `forge-openclaw-plugin`, the product name is `Forge`, and the CLI namespace is `forge`.
340
387
 
341
388
  ## Recommended usage
342
389
 
@@ -465,7 +512,7 @@ The startup error now points at that log file when the child process exits befor
465
512
  The reliable publication path for the Forge plugin is:
466
513
 
467
514
  1. publish `forge-openclaw-plugin` to npm
468
- 2. verify `openclaw plugins install forge-openclaw-plugin`
515
+ 2. verify `openclaw plugins install --dangerously-force-unsafe-install forge-openclaw-plugin`
469
516
  3. add Forge to the OpenClaw community plugin listing with the npm package and GitHub repo
470
517
 
471
518
  The repo now supports a tag-driven GitHub Actions release path for step 1. The normal
@@ -1,4 +1,4 @@
1
- import{r as c,R as I,a as Ne}from"./vendor-C0otBhgu.js";function Mn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return c.useMemo(()=>r=>{t.forEach(o=>o(r))},t)}const nt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function we(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function mt(e){return"nodeType"in e}function z(e){var t,n;return e?we(e)?e:mt(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function yt(e){const{Document:t}=z(e);return e instanceof t}function Be(e){return we(e)?!1:e instanceof z(e).HTMLElement}function Vt(e){return e instanceof z(e).SVGElement}function xe(e){return e?we(e)?e.document:mt(e)?yt(e)?e:Be(e)||Vt(e)?e.ownerDocument:document:document:document}const V=nt?c.useLayoutEffect:c.useEffect;function rt(e){const t=c.useRef(e);return V(()=>{t.current=e}),c.useCallback(function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return t.current==null?void 0:t.current(...r)},[])}function On(){const e=c.useRef(null),t=c.useCallback((r,o)=>{e.current=setInterval(r,o)},[]),n=c.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function Pe(e,t){t===void 0&&(t=[e]);const n=c.useRef(e);return V(()=>{n.current!==e&&(n.current=e)},t),n}function Fe(e,t){const n=c.useRef();return c.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Je(e){const t=rt(e),n=c.useRef(null),r=c.useCallback(o=>{o!==n.current&&(t==null||t(o,n.current)),n.current=o},[]);return[n,r]}function _e(e){const t=c.useRef();return c.useEffect(()=>{t.current=e},[e]),t.current}let ut={};function $e(e,t){return c.useMemo(()=>{if(t)return t;const n=ut[e]==null?0:ut[e]+1;return ut[e]=n,e+"-"+n},[e,t])}function qt(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((i,s)=>{const a=Object.entries(s);for(const[l,u]of a){const d=i[l];d!=null&&(i[l]=d+e*u)}return i},{...t})}}const ye=qt(1),Qe=qt(-1);function In(e){return"clientX"in e&&"clientY"in e}function ot(e){if(!e)return!1;const{KeyboardEvent:t}=z(e.target);return t&&e instanceof t}function Nn(e){if(!e)return!1;const{TouchEvent:t}=z(e.target);return t&&e instanceof t}function Ze(e){if(Nn(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return In(e)?{x:e.clientX,y:e.clientY}:null}const fe=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[fe.Translate.toString(e),fe.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),kt="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Tn(e){return e.matches(kt)?e:e.querySelector(kt)}const Ln={display:"none"};function kn(e){let{id:t,value:n}=e;return I.createElement("div",{id:t,style:Ln},n)}function Pn(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const o={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return I.createElement("div",{id:t,style:o,role:"status","aria-live":r,"aria-atomic":!0},n)}function zn(){const[e,t]=c.useState("");return{announce:c.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const Gt=c.createContext(null);function Bn(e){const t=c.useContext(Gt);c.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of <DndContext>");return t(e)},[e,t])}function Fn(){const[e]=c.useState(()=>new Set),t=c.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[c.useCallback(r=>{let{type:o,event:i}=r;e.forEach(s=>{var a;return(a=s[o])==null?void 0:a.call(s,i)})},[e]),t]}const $n={draggable:`
1
+ import{r as c,R as I,a as Ne}from"./vendor-BgZ3YrRd.js";function Mn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return c.useMemo(()=>r=>{t.forEach(o=>o(r))},t)}const nt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function we(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function mt(e){return"nodeType"in e}function z(e){var t,n;return e?we(e)?e:mt(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function yt(e){const{Document:t}=z(e);return e instanceof t}function Be(e){return we(e)?!1:e instanceof z(e).HTMLElement}function Vt(e){return e instanceof z(e).SVGElement}function xe(e){return e?we(e)?e.document:mt(e)?yt(e)?e:Be(e)||Vt(e)?e.ownerDocument:document:document:document}const V=nt?c.useLayoutEffect:c.useEffect;function rt(e){const t=c.useRef(e);return V(()=>{t.current=e}),c.useCallback(function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return t.current==null?void 0:t.current(...r)},[])}function On(){const e=c.useRef(null),t=c.useCallback((r,o)=>{e.current=setInterval(r,o)},[]),n=c.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function Pe(e,t){t===void 0&&(t=[e]);const n=c.useRef(e);return V(()=>{n.current!==e&&(n.current=e)},t),n}function Fe(e,t){const n=c.useRef();return c.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Je(e){const t=rt(e),n=c.useRef(null),r=c.useCallback(o=>{o!==n.current&&(t==null||t(o,n.current)),n.current=o},[]);return[n,r]}function _e(e){const t=c.useRef();return c.useEffect(()=>{t.current=e},[e]),t.current}let ut={};function $e(e,t){return c.useMemo(()=>{if(t)return t;const n=ut[e]==null?0:ut[e]+1;return ut[e]=n,e+"-"+n},[e,t])}function qt(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((i,s)=>{const a=Object.entries(s);for(const[l,u]of a){const d=i[l];d!=null&&(i[l]=d+e*u)}return i},{...t})}}const ye=qt(1),Qe=qt(-1);function In(e){return"clientX"in e&&"clientY"in e}function ot(e){if(!e)return!1;const{KeyboardEvent:t}=z(e.target);return t&&e instanceof t}function Nn(e){if(!e)return!1;const{TouchEvent:t}=z(e.target);return t&&e instanceof t}function Ze(e){if(Nn(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return In(e)?{x:e.clientX,y:e.clientY}:null}const fe=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[fe.Translate.toString(e),fe.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),kt="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Tn(e){return e.matches(kt)?e:e.querySelector(kt)}const Ln={display:"none"};function kn(e){let{id:t,value:n}=e;return I.createElement("div",{id:t,style:Ln},n)}function Pn(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const o={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return I.createElement("div",{id:t,style:o,role:"status","aria-live":r,"aria-atomic":!0},n)}function zn(){const[e,t]=c.useState("");return{announce:c.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const Gt=c.createContext(null);function Bn(e){const t=c.useContext(Gt);c.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of <DndContext>");return t(e)},[e,t])}function Fn(){const[e]=c.useState(()=>new Set),t=c.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[c.useCallback(r=>{let{type:o,event:i}=r;e.forEach(s=>{var a;return(a=s[o])==null?void 0:a.call(s,i)})},[e]),t]}const $n={draggable:`
2
2
  To pick up a draggable item, press the space bar.
3
3
  While dragging, use the arrow keys to move the item.
4
4
  Press space again to drop the item in its new position, or press escape to cancel.