@crowdedkingdoms/crowdyjs 12.0.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +45 -0
- package/README.md +70 -1
- package/dist/crowdy-studio/agent-dom-shell.d.ts +29 -1
- package/dist/crowdy-studio/agent-dom-shell.d.ts.map +1 -1
- package/dist/crowdy-studio/agent-dom-shell.js +108 -44
- package/dist/crowdy-studio/dom-shell.d.ts +46 -3
- package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
- package/dist/crowdy-studio/dom-shell.js +551 -125
- package/dist/crowdy-studio/embed/dock.d.ts +56 -0
- package/dist/crowdy-studio/embed/dock.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/dock.js +231 -0
- package/dist/crowdy-studio/embed/embed-styles.d.ts +12 -0
- package/dist/crowdy-studio/embed/embed-styles.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/embed-styles.js +549 -0
- package/dist/crowdy-studio/embed/hud-layer.d.ts +40 -0
- package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/hud-layer.js +100 -0
- package/dist/crowdy-studio/embed/index.d.ts +5 -0
- package/dist/crowdy-studio/embed/index.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/index.js +4 -0
- package/dist/crowdy-studio/embed/panel.d.ts +162 -0
- package/dist/crowdy-studio/embed/panel.d.ts.map +1 -0
- package/dist/crowdy-studio/embed/panel.js +642 -0
- package/dist/crowdy-studio/index.d.ts +3 -0
- package/dist/crowdy-studio/index.d.ts.map +1 -1
- package/dist/crowdy-studio/index.js +3 -0
- package/dist/crowdy-studio/layout.d.ts +50 -0
- package/dist/crowdy-studio/layout.d.ts.map +1 -0
- package/dist/crowdy-studio/layout.js +155 -0
- package/dist/crowdy-studio/mount.d.ts.map +1 -1
- package/dist/crowdy-studio/mount.js +3 -1
- package/dist/crowdy-studio/splitter.d.ts +33 -0
- package/dist/crowdy-studio/splitter.d.ts.map +1 -0
- package/dist/crowdy-studio/splitter.js +133 -0
- package/dist/crowdy-studio/styles.d.ts +1 -1
- package/dist/crowdy-studio/styles.d.ts.map +1 -1
- package/dist/crowdy-studio/styles.js +128 -13
- package/dist/domains/gameModel.d.ts +68 -1
- package/dist/domains/gameModel.d.ts.map +1 -1
- package/dist/domains/gameModel.js +88 -1
- package/dist/generated/graphql.d.ts +132 -15
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +12 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/kit/npcs.d.ts.map +1 -1
- package/dist/kit/social.d.ts.map +1 -1
- package/dist/player-host/control-banner.d.ts +40 -0
- package/dist/player-host/control-banner.d.ts.map +1 -0
- package/dist/player-host/control-banner.js +252 -0
- package/dist/player-host/control-gate.d.ts +77 -0
- package/dist/player-host/control-gate.d.ts.map +1 -0
- package/dist/player-host/control-gate.js +197 -0
- package/dist/player-host/index.d.ts +2 -0
- package/dist/player-host/index.d.ts.map +1 -1
- package/dist/player-host/index.js +2 -0
- package/package.json +6 -1
package/MIGRATION.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
# CrowdyJS v12.1 — Crowdy Studio embed kit (additive)
|
|
2
|
+
|
|
3
|
+
Version 12.1 ships the reusable game-embed chrome that previously lived only
|
|
4
|
+
in Blocks with Friends. Nothing breaks; games that already hand-roll a shell
|
|
5
|
+
can adopt incrementally.
|
|
6
|
+
|
|
7
|
+
New from `@crowdedkingdoms/crowdyjs/crowdy-studio`:
|
|
8
|
+
|
|
9
|
+
- `createCrowdyStudioEmbed(options)` / `CrowdyStudioEmbed` — responsive
|
|
10
|
+
dock/fullscreen panel with focus trap, Escape/close-key semantics, compact
|
|
11
|
+
header, on-demand Context drawer (grid bounds, permission cards, optional
|
|
12
|
+
HUD preview), loading/error/retry chrome, and assembly of the full
|
|
13
|
+
`mountCrowdyStudio` call (agent block included when the client exposes
|
|
14
|
+
`crowdyStudioAgent` and the game passes `playerHost`).
|
|
15
|
+
- `CrowdyStudioEmbedDock` — accessible game/studio splitter with persisted
|
|
16
|
+
width under `ck:crowdy-studio:embed:dock-width:v1`.
|
|
17
|
+
- `CrowdyStudioTextHud` — text-only presentation sink for CLIENT-mod
|
|
18
|
+
`hud_set` payloads plus the drawer preview mount.
|
|
19
|
+
- `CROWDY_STUDIO_EMBED_STYLES` / `ensureCrowdyStudioEmbedStyles()` — injected
|
|
20
|
+
`ck-crowdy-studio-embed-*` styling; the docked panel sets
|
|
21
|
+
`--ck-game-right-inset` on `document.body` for game HUD insets.
|
|
22
|
+
|
|
23
|
+
New from `@crowdedkingdoms/crowdyjs/player-host`:
|
|
24
|
+
|
|
25
|
+
- `PlayerControlGate` — the synchronous human-takeover seam (capture-phase
|
|
26
|
+
keyboard/pointer preemption, offline Stop, page-hide/visibility handling),
|
|
27
|
+
parameterized on a `clearAgentIntent` hook.
|
|
28
|
+
- `AgentControlBanner` — the always-visible-on-control Pause/Stop safety
|
|
29
|
+
region with self-injected `ck-agent-control-*` styles.
|
|
30
|
+
|
|
31
|
+
New package subpath:
|
|
32
|
+
|
|
33
|
+
- `@crowdedkingdoms/crowdyjs/player-glue-worker` — the self-starting tokenless
|
|
34
|
+
CLIENT-mod glue worker entry. Bundle it as a same-origin module worker (for
|
|
35
|
+
example Vite's `?worker&url`) instead of copying a worker wrapper into the
|
|
36
|
+
game.
|
|
37
|
+
|
|
38
|
+
Migrating from the Blocks with Friends copies: `CrowdyStudioPanel` →
|
|
39
|
+
`CrowdyStudioEmbed`, `CrowdyStudioDock` → `CrowdyStudioEmbedDock`,
|
|
40
|
+
`ModHudLayer` → `CrowdyStudioTextHud`, `bwf-crowdy-studio-*` CSS →
|
|
41
|
+
`ck-crowdy-studio-embed-*`, `bwf-agent-control-*` → `ck-agent-control-*`,
|
|
42
|
+
`--bwf-game-right-inset` → `--ck-game-right-inset`. The persisted dock width
|
|
43
|
+
key changes from `bwf:crowdy-studio:dock-width:v1` to the `ck:` key above
|
|
44
|
+
(previous widths reset once).
|
|
45
|
+
|
|
1
46
|
# CrowdyJS v12 — Agentic Crowdy Studio contract (BREAKING)
|
|
2
47
|
|
|
3
48
|
Version 12 establishes the greenfield public contracts
|
package/README.md
CHANGED
|
@@ -39,6 +39,16 @@ CrowdyJS v4 targets browsers by default and uses native `fetch`, `WebSocket`, `c
|
|
|
39
39
|
> `marketplace.claimGridChunk` / `marketplace.releaseClaimedGrid` require the
|
|
40
40
|
> corresponding 2026-07-22 Game API claim schema. Older servers reject only
|
|
41
41
|
> those operations during GraphQL validation.
|
|
42
|
+
>
|
|
43
|
+
> **App-scoped player-count compatibility:** `gameModel.activePlayerCount(appId)`
|
|
44
|
+
> and `gameModel.activePlayerCountChanged({ appId }, handlers)` require the
|
|
45
|
+
> matching 2026-07-24 Game API schema and an app token scoped to that exact app;
|
|
46
|
+
> the subscription also requires `wsUrl`. They count active gameplay sessions,
|
|
47
|
+
> not distinct users or actors, and an abandoned session can remain counted for
|
|
48
|
+
> approximately 120 seconds. Only `FRESH` snapshots are complete:
|
|
49
|
+
> `PARTIAL`/`UNAVAILABLE` are not authoritative zeroes. The subscription is
|
|
50
|
+
> best-effort and has no initial event; establish it, query the snapshot,
|
|
51
|
+
> deduplicate by `revision`, and requery after reconnects or revision gaps.
|
|
42
52
|
|
|
43
53
|
> **v8.21.1 Node realtime routing hotfix:** app-routing responses provide a
|
|
44
54
|
> WebSocket base URL. CrowdyJS now normalizes that base to `/graphql` before
|
|
@@ -155,7 +165,7 @@ If `managementUrl` is omitted, the SDK falls back to `httpUrl` for backwards-com
|
|
|
155
165
|
| `client.host` | Game-host election (`get`, `amIHost`) + actor liveness `heartbeat`. `amIHost` is UI convenience only — authoritative host gating uses `gameModelInvoke`'s `is_host` policy. |
|
|
156
166
|
| `client.teleport` | Teleport requests. |
|
|
157
167
|
| `client.channels`, `client.teams` | Messaging channels and app-scoped player teams (membership + roles). |
|
|
158
|
-
| `client.gameModel` | Abstract game model: containers, properties, functions (incl. model-driven `notify_*` effects), sessions, and **automations / NPCs** (`upsertAutomation`, `runAutomation`, `automationRuns`, `automationStats`, …). |
|
|
168
|
+
| `client.gameModel` | Abstract game model: containers, properties, functions (incl. model-driven `notify_*` effects), sessions, **app-scoped active-session counts** (`activePlayerCount`, `activePlayerCountChanged`), and **automations / NPCs** (`upsertAutomation`, `runAutomation`, `automationRuns`, `automationStats`, …). |
|
|
159
169
|
| `client.compute` | **Compute Modules** — server-side Rust/WASM logic: author + deploy source (`upsertModule`, `deployVersion`, `waitForCompile`), triggers + policy, synchronous `invoke`, and monitoring (`moduleRuns`, `moduleStats`, `moduleLogs`, `appDiagnostics`). Modules run server-only; see [Compute Modules docs](https://docs.crowdedkingdoms.com/game-api/compute-modules). |
|
|
160
170
|
| `client.playerCompute` | Player-authored SERVER/CLIENT Rust/WASM bound to player-owned grids: deploy source, activate/deactivate, list modules/versions, and delete self-authored modules. |
|
|
161
171
|
| `client.crowdyStudio` | Cloud project, personal-library, and common-file APIs for Crowdy Studio: target-scoped files, metadata/module names, pairing preference, optimistic revisions, copy-by-value imports, and atomic metadata/file saves. Generated operations are pinned to the committed merged SDL. |
|
|
@@ -244,6 +254,65 @@ embedded index remains the byte-identical game export (`schemaVersion: 2`, Rust
|
|
|
244
254
|
1.97.1, SDK 0.1.5, ABI 0, 725 symbols, content hash
|
|
245
255
|
`3f5f39d4…18ffb`).
|
|
246
256
|
|
|
257
|
+
### Embed Crowdy Studio in your game (v12.1)
|
|
258
|
+
|
|
259
|
+
Most games should not hand-roll the window chrome around `mountCrowdyStudio`.
|
|
260
|
+
The embed kit ships the proven Blocks with Friends shell as reusable, game
|
|
261
|
+
-agnostic components (exported from `@crowdedkingdoms/crowdyjs/crowdy-studio`):
|
|
262
|
+
|
|
263
|
+
- **`createCrowdyStudioEmbed(options)` / `CrowdyStudioEmbed`** — a responsive
|
|
264
|
+
panel that renders a resizable right dock on desktop and a focus-trapped
|
|
265
|
+
fullscreen modal below 1000 px. It owns Escape/close-key semantics, the
|
|
266
|
+
compact header (title, grid pill, on-demand Context drawer, Close), loading
|
|
267
|
+
and retry chrome, and assembles the `mountCrowdyStudio` call — including the
|
|
268
|
+
agent block when the client exposes `crowdyStudioAgent` and the game passes a
|
|
269
|
+
`playerHost`.
|
|
270
|
+
- **`CrowdyStudioEmbedDock`** — the accessible game/studio splitter with
|
|
271
|
+
persisted width (`ck:crowdy-studio:embed:dock-width:v1`), arrow-key/Home/End
|
|
272
|
+
resize, and ARIA value text.
|
|
273
|
+
- **`CrowdyStudioTextHud`** — the text-only presentation sink for CLIENT-mod
|
|
274
|
+
`hud_set` payloads plus the drawer HUD preview. Untrusted payloads render as
|
|
275
|
+
text, never HTML.
|
|
276
|
+
- **`ensureCrowdyStudioEmbedStyles()`** — injected `ck-crowdy-studio-embed-*`
|
|
277
|
+
styles; games restyle by overriding classes. While docked, the panel sets
|
|
278
|
+
`--ck-game-right-inset` on `document.body` so game HUDs can keep clear of
|
|
279
|
+
the dock.
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { createCrowdyStudioEmbed } from '@crowdedkingdoms/crowdyjs/crowdy-studio';
|
|
283
|
+
// Self-starting glue worker for CLIENT mods (Vite shown; any bundler that
|
|
284
|
+
// packages module workers works):
|
|
285
|
+
import workerUrl from '@crowdedkingdoms/crowdyjs/player-glue-worker?worker&url';
|
|
286
|
+
|
|
287
|
+
const studio = createCrowdyStudioEmbed({
|
|
288
|
+
client: game, // CrowdyClient: crowdyStudio, playerCompute, playerWallet, crowdyStudioAgent
|
|
289
|
+
appId,
|
|
290
|
+
gameName: 'My Game',
|
|
291
|
+
suppressGameplayInput: () => pauseInput(),
|
|
292
|
+
onLayoutChange: () => resizeCanvas(),
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Per open (for example after the player claims a grid):
|
|
296
|
+
studio.toggle({
|
|
297
|
+
gridId,
|
|
298
|
+
targetPermissions: {
|
|
299
|
+
SERVER: { canWrite: true, canRun: true },
|
|
300
|
+
CLIENT: { canWrite: false, canRun: false }, // SERVER-only embeds omit workerUrl
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
CLIENT-mod embeds additionally pass `grid`, `workerUrl`, `onHostCall`, an
|
|
306
|
+
optional `hud` sink, and a `playerHost` adapter. Note that CLIENT mods outside
|
|
307
|
+
Blocks with Friends remain gated by program decision D13; new games should
|
|
308
|
+
start SERVER-only exactly as above.
|
|
309
|
+
|
|
310
|
+
The visible agent safety chrome is exported from
|
|
311
|
+
`@crowdedkingdoms/crowdyjs/player-host`: **`PlayerControlGate`** (capture-phase
|
|
312
|
+
human preemption seam with offline Stop) and **`AgentControlBanner`** (the
|
|
313
|
+
always-visible-on-control Pause/Stop region, self-injected
|
|
314
|
+
`ck-agent-control-*` styles).
|
|
315
|
+
|
|
247
316
|
### Game API project contract
|
|
248
317
|
|
|
249
318
|
`CrowdyStudioAPI` implements the transport-neutral
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { type CrowdyStudioAgentController, type CrowdyStudioAgentStateV1 } from '../crowdy-agent/controller.js';
|
|
2
|
+
import type { StudioLayoutController } from './layout.js';
|
|
2
3
|
export interface CrowdyStudioAgentDomShellOptions {
|
|
3
4
|
getPlayLeaseContext?: () => {
|
|
4
5
|
controlledEntityId: string;
|
|
5
6
|
hostCapabilityRevision: string;
|
|
6
7
|
} | null;
|
|
8
|
+
/**
|
|
9
|
+
* Optional studio layout controller. When present the dock participates in
|
|
10
|
+
* the shared pane layout and auto-reveals itself for safety-critical
|
|
11
|
+
* moments (pending exact approvals, an activating Play lease).
|
|
12
|
+
*/
|
|
13
|
+
layout?: StudioLayoutController;
|
|
7
14
|
}
|
|
8
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Accessible, text-only rendering for the integrated durable agent dock.
|
|
17
|
+
*
|
|
18
|
+
* The conversation is the primary surface. Play-control, activity, and
|
|
19
|
+
* change-tracking sections are collapsible disclosures so the dock stays
|
|
20
|
+
* minimal, with one deliberate exception: pending exact approvals and the
|
|
21
|
+
* active Play lease countdown always render prominently.
|
|
22
|
+
*/
|
|
9
23
|
export declare class CrowdyStudioAgentDomShell {
|
|
10
24
|
private readonly controller;
|
|
11
25
|
private readonly options;
|
|
@@ -20,15 +34,29 @@ export declare class CrowdyStudioAgentDomShell {
|
|
|
20
34
|
private readonly checkpoints;
|
|
21
35
|
private readonly composer;
|
|
22
36
|
private readonly send;
|
|
37
|
+
private readonly playDisclosure;
|
|
38
|
+
private readonly playSummary;
|
|
39
|
+
private readonly activityDisclosure;
|
|
40
|
+
private readonly activitySummary;
|
|
41
|
+
private readonly changesDisclosure;
|
|
42
|
+
private readonly changesSummary;
|
|
23
43
|
private readonly modeButtons;
|
|
24
44
|
private readonly scopeInputs;
|
|
25
45
|
private readonly unsubscribe;
|
|
26
46
|
private readonly countdownTimer;
|
|
27
47
|
private lastState;
|
|
48
|
+
private lastPendingApprovals;
|
|
49
|
+
private lastPlayLeaseActive;
|
|
28
50
|
private disposed;
|
|
29
51
|
constructor(host: HTMLElement, controller: CrowdyStudioAgentController, options?: CrowdyStudioAgentDomShellOptions);
|
|
30
52
|
render(state: CrowdyStudioAgentStateV1): void;
|
|
31
53
|
dispose(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Safety-critical auto-reveal: a newly pending exact approval or a Play
|
|
56
|
+
* lease activation opens the dock and the Play disclosure so the human is
|
|
57
|
+
* always shown what needs their decision or is acting on their behalf.
|
|
58
|
+
*/
|
|
59
|
+
private autoReveal;
|
|
32
60
|
private renderBudget;
|
|
33
61
|
private renderLease;
|
|
34
62
|
private renderMessages;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-dom-shell.d.ts","sourceRoot":"","sources":["../../src/crowdy-studio/agent-dom-shell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-dom-shell.d.ts","sourceRoot":"","sources":["../../src/crowdy-studio/agent-dom-shell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAY1D,MAAM,WAAW,gCAAgC;IAC/C,mBAAmB,CAAC,EAAE,MAAM;QAC1B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,sBAAsB,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT;;;;OAIG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,qBAAa,yBAAyB;IA6BlC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IA7B1B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiD;IAC7E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;IACnE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAS;gBAGvB,IAAI,EAAE,WAAW,EACA,UAAU,EAAE,2BAA2B,EACvC,OAAO,GAAE,gCAAqC;IAmLjE,MAAM,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IA0B7C,OAAO,IAAI,IAAI;IAQf;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAqBlB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,cAAc;IA8DtB,OAAO,CAAC,eAAe;IAgDvB,OAAO,CAAC,iBAAiB;YA0CX,GAAG;CAQlB"}
|
|
@@ -7,13 +7,22 @@ const PLAY_SCOPES = [
|
|
|
7
7
|
'communicate',
|
|
8
8
|
'travel',
|
|
9
9
|
];
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Accessible, text-only rendering for the integrated durable agent dock.
|
|
12
|
+
*
|
|
13
|
+
* The conversation is the primary surface. Play-control, activity, and
|
|
14
|
+
* change-tracking sections are collapsible disclosures so the dock stays
|
|
15
|
+
* minimal, with one deliberate exception: pending exact approvals and the
|
|
16
|
+
* active Play lease countdown always render prominently.
|
|
17
|
+
*/
|
|
11
18
|
export class CrowdyStudioAgentDomShell {
|
|
12
19
|
constructor(host, controller, options = {}) {
|
|
13
20
|
this.controller = controller;
|
|
14
21
|
this.options = options;
|
|
15
22
|
this.modeButtons = new Map();
|
|
16
23
|
this.scopeInputs = new Map();
|
|
24
|
+
this.lastPendingApprovals = 0;
|
|
25
|
+
this.lastPlayLeaseActive = false;
|
|
17
26
|
this.disposed = false;
|
|
18
27
|
this.lastState = controller.getState();
|
|
19
28
|
this.root = document.createElement('aside');
|
|
@@ -27,6 +36,8 @@ export class CrowdyStudioAgentDomShell {
|
|
|
27
36
|
this.status.setAttribute('role', 'status');
|
|
28
37
|
this.status.setAttribute('aria-live', 'polite');
|
|
29
38
|
header.append(title, this.status);
|
|
39
|
+
const controlsRow = document.createElement('div');
|
|
40
|
+
controlsRow.className = 'ck-crowdy-studio-agent-controls-row';
|
|
30
41
|
const modeGroup = document.createElement('div');
|
|
31
42
|
modeGroup.className = 'ck-crowdy-studio-agent-modes';
|
|
32
43
|
modeGroup.setAttribute('role', 'group');
|
|
@@ -50,13 +61,52 @@ export class CrowdyStudioAgentDomShell {
|
|
|
50
61
|
stop.className = 'ck-crowdy-studio-agent-stop';
|
|
51
62
|
stop.addEventListener('click', () => void this.run(() => this.controller.stop()));
|
|
52
63
|
controls.append(pause, resume, stop);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
controlsRow.append(modeGroup, controls);
|
|
65
|
+
// Safety surfaces: approvals and the active Play lease stay prominent and
|
|
66
|
+
// are never hidden behind a disclosure.
|
|
67
|
+
this.approvals = document.createElement('section');
|
|
68
|
+
this.approvals.className = 'ck-crowdy-studio-agent-approvals';
|
|
69
|
+
this.approvals.setAttribute('aria-label', 'Pending exact approvals');
|
|
56
70
|
this.lease = document.createElement('div');
|
|
57
71
|
this.lease.className = 'ck-crowdy-studio-agent-lease';
|
|
58
72
|
this.lease.setAttribute('role', 'status');
|
|
59
73
|
this.lease.setAttribute('aria-label', 'Agent control lease');
|
|
74
|
+
this.messages = document.createElement('div');
|
|
75
|
+
this.messages.className = 'ck-crowdy-studio-agent-messages';
|
|
76
|
+
this.messages.setAttribute('role', 'log');
|
|
77
|
+
this.messages.setAttribute('aria-live', 'polite');
|
|
78
|
+
this.messages.setAttribute('aria-relevant', 'additions text');
|
|
79
|
+
this.stream = document.createElement('div');
|
|
80
|
+
this.stream.className = 'ck-crowdy-studio-agent-stream';
|
|
81
|
+
this.stream.setAttribute('aria-label', 'Streaming assistant response');
|
|
82
|
+
this.messages.append(this.stream);
|
|
83
|
+
const form = document.createElement('form');
|
|
84
|
+
form.className = 'ck-crowdy-studio-agent-composer';
|
|
85
|
+
this.composer = document.createElement('textarea');
|
|
86
|
+
this.composer.maxLength = 32768;
|
|
87
|
+
this.composer.rows = 3;
|
|
88
|
+
this.composer.placeholder = 'Ask, build, or play…';
|
|
89
|
+
this.composer.setAttribute('aria-label', 'Message Crowdy Agent');
|
|
90
|
+
this.send = button('Send', 'submit');
|
|
91
|
+
form.append(this.composer, this.send);
|
|
92
|
+
form.addEventListener('submit', (event) => {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
const content = this.composer.value;
|
|
95
|
+
if (!content.trim())
|
|
96
|
+
return;
|
|
97
|
+
void this.run(async () => {
|
|
98
|
+
await this.controller.sendMessage(content);
|
|
99
|
+
this.composer.value = '';
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
this.composer.addEventListener('keydown', (event) => {
|
|
103
|
+
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
form.requestSubmit();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// Collapsible secondary sections.
|
|
109
|
+
const playBody = document.createElement('div');
|
|
60
110
|
const leaseControls = document.createElement('fieldset');
|
|
61
111
|
leaseControls.className = 'ck-crowdy-studio-agent-lease-controls';
|
|
62
112
|
const leaseLegend = document.createElement('legend');
|
|
@@ -92,56 +142,25 @@ export class CrowdyStudioAgentDomShell {
|
|
|
92
142
|
}));
|
|
93
143
|
});
|
|
94
144
|
leaseControls.append(grant);
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
this.messages.className = 'ck-crowdy-studio-agent-messages';
|
|
98
|
-
this.messages.setAttribute('role', 'log');
|
|
99
|
-
this.messages.setAttribute('aria-live', 'polite');
|
|
100
|
-
this.messages.setAttribute('aria-relevant', 'additions text');
|
|
101
|
-
this.stream = document.createElement('div');
|
|
102
|
-
this.stream.className = 'ck-crowdy-studio-agent-stream';
|
|
103
|
-
this.stream.setAttribute('aria-label', 'Streaming assistant response');
|
|
104
|
-
this.messages.append(this.stream);
|
|
105
|
-
const activityTitle = heading('Plan and tools');
|
|
145
|
+
playBody.append(leaseControls);
|
|
146
|
+
[this.playDisclosure, this.playSummary] = disclosure('Play control', playBody);
|
|
106
147
|
this.timeline = document.createElement('ol');
|
|
107
148
|
this.timeline.className = 'ck-crowdy-studio-agent-timeline';
|
|
108
149
|
this.timeline.setAttribute('aria-label', 'Agent plan and tool timeline');
|
|
109
|
-
this.
|
|
110
|
-
this.approvals.className = 'ck-crowdy-studio-agent-approvals';
|
|
111
|
-
this.approvals.setAttribute('aria-label', 'Pending exact approvals');
|
|
150
|
+
[this.activityDisclosure, this.activitySummary] = disclosure('Activity', this.timeline);
|
|
112
151
|
this.checkpoints = document.createElement('section');
|
|
113
152
|
this.checkpoints.className = 'ck-crowdy-studio-agent-checkpoints';
|
|
114
153
|
this.checkpoints.setAttribute('aria-label', 'Agent diffs and checkpoints');
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
119
|
-
this.
|
|
120
|
-
this.composer.placeholder = 'Ask, build, or play…';
|
|
121
|
-
this.composer.setAttribute('aria-label', 'Message Crowdy Agent');
|
|
122
|
-
this.send = button('Send', 'submit');
|
|
123
|
-
form.append(this.composer, this.send);
|
|
124
|
-
form.addEventListener('submit', (event) => {
|
|
125
|
-
event.preventDefault();
|
|
126
|
-
const content = this.composer.value;
|
|
127
|
-
if (!content.trim())
|
|
128
|
-
return;
|
|
129
|
-
void this.run(async () => {
|
|
130
|
-
await this.controller.sendMessage(content);
|
|
131
|
-
this.composer.value = '';
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
this.composer.addEventListener('keydown', (event) => {
|
|
135
|
-
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
|
|
136
|
-
event.preventDefault();
|
|
137
|
-
form.requestSubmit();
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
this.root.append(header, modeGroup, controls, this.budget, this.lease, leaseControls, conversationTitle, this.messages, activityTitle, this.timeline, this.approvals, this.checkpoints, form);
|
|
154
|
+
[this.changesDisclosure, this.changesSummary] = disclosure('Changes', this.checkpoints);
|
|
155
|
+
this.budget = document.createElement('div');
|
|
156
|
+
this.budget.className = 'ck-crowdy-studio-agent-budget';
|
|
157
|
+
this.budget.setAttribute('aria-label', 'Agent budget');
|
|
158
|
+
this.root.append(header, controlsRow, this.approvals, this.lease, this.messages, form, this.playDisclosure, this.activityDisclosure, this.changesDisclosure, this.budget);
|
|
141
159
|
host.dataset.agent = 'true';
|
|
142
160
|
host.append(this.root);
|
|
143
161
|
this.unsubscribe = controller.subscribe((state) => this.render(state));
|
|
144
162
|
this.countdownTimer = setInterval(() => this.renderLease(this.lastState), 1000);
|
|
163
|
+
this.render(this.lastState);
|
|
145
164
|
}
|
|
146
165
|
render(state) {
|
|
147
166
|
if (this.disposed)
|
|
@@ -167,6 +186,7 @@ export class CrowdyStudioAgentDomShell {
|
|
|
167
186
|
this.renderTimeline(state);
|
|
168
187
|
this.renderApprovals(state);
|
|
169
188
|
this.renderCheckpoints(state);
|
|
189
|
+
this.autoReveal(state);
|
|
170
190
|
}
|
|
171
191
|
dispose() {
|
|
172
192
|
if (this.disposed)
|
|
@@ -176,6 +196,27 @@ export class CrowdyStudioAgentDomShell {
|
|
|
176
196
|
this.unsubscribe();
|
|
177
197
|
this.root.remove();
|
|
178
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Safety-critical auto-reveal: a newly pending exact approval or a Play
|
|
201
|
+
* lease activation opens the dock and the Play disclosure so the human is
|
|
202
|
+
* always shown what needs their decision or is acting on their behalf.
|
|
203
|
+
*/
|
|
204
|
+
autoReveal(state) {
|
|
205
|
+
const pending = state.approvals.filter((approval) => approval.status === 'PENDING').length;
|
|
206
|
+
const playLeaseActive = state.leases.some((lease) => lease.kind === 'PLAY' && lease.status === 'ACTIVE');
|
|
207
|
+
if (pending > this.lastPendingApprovals) {
|
|
208
|
+
this.options.layout?.setVisible('agent', true);
|
|
209
|
+
}
|
|
210
|
+
if (playLeaseActive && !this.lastPlayLeaseActive) {
|
|
211
|
+
this.options.layout?.setVisible('agent', true);
|
|
212
|
+
this.playDisclosure.open = true;
|
|
213
|
+
}
|
|
214
|
+
if (state.session?.mode === 'PLAY' && !playLeaseActive) {
|
|
215
|
+
this.playDisclosure.open = true;
|
|
216
|
+
}
|
|
217
|
+
this.lastPendingApprovals = pending;
|
|
218
|
+
this.lastPlayLeaseActive = playLeaseActive;
|
|
219
|
+
}
|
|
179
220
|
renderBudget(state) {
|
|
180
221
|
if (!state.budget) {
|
|
181
222
|
this.budget.textContent = 'Budget unavailable';
|
|
@@ -191,15 +232,19 @@ export class CrowdyStudioAgentDomShell {
|
|
|
191
232
|
const lease = state.leases.find((entry) => entry.kind === 'PLAY' && entry.status === 'ACTIVE');
|
|
192
233
|
if (!lease) {
|
|
193
234
|
this.lease.textContent = 'No active Play lease';
|
|
235
|
+
this.lease.dataset.active = 'false';
|
|
236
|
+
this.playSummary.textContent = 'Play control';
|
|
194
237
|
return;
|
|
195
238
|
}
|
|
196
239
|
const remaining = Math.max(0, Math.ceil((Date.parse(lease.expiresAt) - Date.now()) / 1000));
|
|
240
|
+
this.lease.dataset.active = 'true';
|
|
197
241
|
this.lease.textContent = [
|
|
198
242
|
`Play lease: ${remaining}s`,
|
|
199
243
|
`holder ${lease.holder}`,
|
|
200
244
|
`entity ${lease.controlledEntityId ?? 'none'}`,
|
|
201
245
|
`scopes ${lease.scopes.join(', ')}`,
|
|
202
246
|
].join(' · ');
|
|
247
|
+
this.playSummary.textContent = `Play control · lease ${remaining}s`;
|
|
203
248
|
}
|
|
204
249
|
renderMessages(state) {
|
|
205
250
|
const rows = state.messages.map((message) => {
|
|
@@ -266,6 +311,8 @@ export class CrowdyStudioAgentDomShell {
|
|
|
266
311
|
});
|
|
267
312
|
const items = [...runItems, ...toolItems];
|
|
268
313
|
this.timeline.replaceChildren(...items);
|
|
314
|
+
this.activitySummary.textContent =
|
|
315
|
+
items.length > 0 ? `Activity (${items.length})` : 'Activity';
|
|
269
316
|
if (items.length === 0) {
|
|
270
317
|
const empty = document.createElement('li');
|
|
271
318
|
empty.textContent = 'No tool activity.';
|
|
@@ -301,6 +348,7 @@ export class CrowdyStudioAgentDomShell {
|
|
|
301
348
|
return card;
|
|
302
349
|
});
|
|
303
350
|
this.approvals.replaceChildren(...cards);
|
|
351
|
+
this.approvals.hidden = cards.length === 0;
|
|
304
352
|
}
|
|
305
353
|
renderCheckpoints(state) {
|
|
306
354
|
const diffTools = state.tools.filter((tool) => tool.name === 'workspace.file.patch' ||
|
|
@@ -327,6 +375,14 @@ export class CrowdyStudioAgentDomShell {
|
|
|
327
375
|
cards.push(card);
|
|
328
376
|
}
|
|
329
377
|
this.checkpoints.replaceChildren(...cards);
|
|
378
|
+
this.changesSummary.textContent =
|
|
379
|
+
cards.length > 0 ? `Changes (${cards.length})` : 'Changes';
|
|
380
|
+
if (cards.length === 0) {
|
|
381
|
+
const none = document.createElement('p');
|
|
382
|
+
none.className = 'ck-crowdy-studio-empty';
|
|
383
|
+
none.textContent = 'No agent edits or checkpoints yet.';
|
|
384
|
+
this.checkpoints.append(none);
|
|
385
|
+
}
|
|
330
386
|
}
|
|
331
387
|
async run(operation) {
|
|
332
388
|
try {
|
|
@@ -338,6 +394,14 @@ export class CrowdyStudioAgentDomShell {
|
|
|
338
394
|
}
|
|
339
395
|
}
|
|
340
396
|
}
|
|
397
|
+
function disclosure(label, body) {
|
|
398
|
+
const details = document.createElement('details');
|
|
399
|
+
details.className = 'ck-crowdy-studio-agent-disclosure';
|
|
400
|
+
const summary = document.createElement('summary');
|
|
401
|
+
summary.textContent = label;
|
|
402
|
+
details.append(summary, body);
|
|
403
|
+
return [details, summary];
|
|
404
|
+
}
|
|
341
405
|
function button(label, type = 'button') {
|
|
342
406
|
const control = document.createElement('button');
|
|
343
407
|
control.type = type;
|
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import { type CrowdyStudioController, type CrowdyStudioState } from './controller.js';
|
|
2
2
|
import type { CrowdyStudioAgentController } from '../crowdy-agent/controller.js';
|
|
3
3
|
import { type CrowdyStudioAgentDomShellOptions } from './agent-dom-shell.js';
|
|
4
|
-
|
|
4
|
+
import { StudioLayoutController } from './layout.js';
|
|
5
|
+
/**
|
|
6
|
+
* Modular DOM shell; editor implementations mount into `editorHost`.
|
|
7
|
+
*
|
|
8
|
+
* Layout is user directed: the editor is always visible while the explorer,
|
|
9
|
+
* project settings, bottom panel, and agent dock are toggled from the
|
|
10
|
+
* activity rail, resized with accessible splitters, and persisted through
|
|
11
|
+
* {@link StudioLayoutController}.
|
|
12
|
+
*/
|
|
5
13
|
export declare class CrowdyStudioDomShell {
|
|
6
14
|
private readonly controller;
|
|
7
15
|
readonly root: HTMLElement;
|
|
8
16
|
readonly editorHost: HTMLElement;
|
|
9
|
-
|
|
10
|
-
private readonly
|
|
17
|
+
readonly layout: StudioLayoutController;
|
|
18
|
+
private readonly workspace;
|
|
19
|
+
private readonly editorColumn;
|
|
20
|
+
private readonly projectMenu;
|
|
21
|
+
private readonly projectMenuList;
|
|
22
|
+
private readonly saveMenu;
|
|
23
|
+
private readonly saveActions;
|
|
24
|
+
private readonly saveMessageText;
|
|
11
25
|
private readonly saveAction;
|
|
12
26
|
private readonly remoteAction;
|
|
13
27
|
private readonly testAction;
|
|
28
|
+
private readonly runMenu;
|
|
14
29
|
private readonly deployAction;
|
|
30
|
+
private readonly stopAction;
|
|
15
31
|
private readonly runtimeStatus;
|
|
16
32
|
private readonly budgetStatus;
|
|
33
|
+
private readonly saveStatus;
|
|
17
34
|
private readonly newForm;
|
|
18
35
|
private readonly newName;
|
|
19
36
|
private readonly newKind;
|
|
20
37
|
private readonly explorer;
|
|
38
|
+
private readonly settings;
|
|
39
|
+
private readonly bottom;
|
|
21
40
|
private readonly tabs;
|
|
22
41
|
private readonly settingsName;
|
|
23
42
|
private readonly settingsDescription;
|
|
@@ -34,20 +53,44 @@ export declare class CrowdyStudioDomShell {
|
|
|
34
53
|
private readonly invokeResult;
|
|
35
54
|
private readonly panelButtons;
|
|
36
55
|
private readonly panels;
|
|
56
|
+
private readonly railButtons;
|
|
57
|
+
private readonly splitters;
|
|
37
58
|
private readonly agentShell;
|
|
59
|
+
private readonly unsubscribeLayout;
|
|
38
60
|
private activePanel;
|
|
61
|
+
private explorerForm;
|
|
62
|
+
private lastState;
|
|
63
|
+
private lastPhase;
|
|
39
64
|
private disposed;
|
|
40
65
|
constructor(host: HTMLElement, controller: CrowdyStudioController, agentController?: CrowdyStudioAgentController, agentOptions?: CrowdyStudioAgentDomShellOptions);
|
|
41
66
|
render(state: CrowdyStudioState): void;
|
|
42
67
|
dispose(): void;
|
|
68
|
+
private railButton;
|
|
69
|
+
/**
|
|
70
|
+
* Narrow widths turn side panes into overlays (styles.ts container
|
|
71
|
+
* queries), where several open panes would stack invisibly on top of each
|
|
72
|
+
* other. Enforce the "single pane" rule there: opening one side pane
|
|
73
|
+
* closes the others that overlay at the current width.
|
|
74
|
+
*/
|
|
75
|
+
private collapseOverlappingPanes;
|
|
76
|
+
private paneSplitter;
|
|
77
|
+
private applyLayout;
|
|
78
|
+
private revealPanel;
|
|
79
|
+
private autoRevealFailures;
|
|
80
|
+
private createMenu;
|
|
43
81
|
private createPanel;
|
|
44
82
|
private activatePanel;
|
|
83
|
+
/** Poll logs/runs only while their panel is both selected and shown. */
|
|
84
|
+
private syncPanelPolling;
|
|
45
85
|
private renderProjects;
|
|
46
86
|
private renderSaveState;
|
|
47
87
|
private renderExplorer;
|
|
48
88
|
private projectSection;
|
|
49
89
|
private projectFileRow;
|
|
90
|
+
private projectFileFormRow;
|
|
50
91
|
private referenceSection;
|
|
92
|
+
private inlineForm;
|
|
93
|
+
private rerenderExplorer;
|
|
51
94
|
private renderTabs;
|
|
52
95
|
private renderSettings;
|
|
53
96
|
private renderProblems;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-shell.d.ts","sourceRoot":"","sources":["../../src/crowdy-studio/dom-shell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,
|
|
1
|
+
{"version":3,"file":"dom-shell.d.ts","sourceRoot":"","sources":["../../src/crowdy-studio/dom-shell.ts"],"names":[],"mappings":"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"}
|