@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.
Files changed (58) hide show
  1. package/MIGRATION.md +45 -0
  2. package/README.md +70 -1
  3. package/dist/crowdy-studio/agent-dom-shell.d.ts +29 -1
  4. package/dist/crowdy-studio/agent-dom-shell.d.ts.map +1 -1
  5. package/dist/crowdy-studio/agent-dom-shell.js +108 -44
  6. package/dist/crowdy-studio/dom-shell.d.ts +46 -3
  7. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  8. package/dist/crowdy-studio/dom-shell.js +551 -125
  9. package/dist/crowdy-studio/embed/dock.d.ts +56 -0
  10. package/dist/crowdy-studio/embed/dock.d.ts.map +1 -0
  11. package/dist/crowdy-studio/embed/dock.js +231 -0
  12. package/dist/crowdy-studio/embed/embed-styles.d.ts +12 -0
  13. package/dist/crowdy-studio/embed/embed-styles.d.ts.map +1 -0
  14. package/dist/crowdy-studio/embed/embed-styles.js +549 -0
  15. package/dist/crowdy-studio/embed/hud-layer.d.ts +40 -0
  16. package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -0
  17. package/dist/crowdy-studio/embed/hud-layer.js +100 -0
  18. package/dist/crowdy-studio/embed/index.d.ts +5 -0
  19. package/dist/crowdy-studio/embed/index.d.ts.map +1 -0
  20. package/dist/crowdy-studio/embed/index.js +4 -0
  21. package/dist/crowdy-studio/embed/panel.d.ts +162 -0
  22. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -0
  23. package/dist/crowdy-studio/embed/panel.js +642 -0
  24. package/dist/crowdy-studio/index.d.ts +3 -0
  25. package/dist/crowdy-studio/index.d.ts.map +1 -1
  26. package/dist/crowdy-studio/index.js +3 -0
  27. package/dist/crowdy-studio/layout.d.ts +50 -0
  28. package/dist/crowdy-studio/layout.d.ts.map +1 -0
  29. package/dist/crowdy-studio/layout.js +155 -0
  30. package/dist/crowdy-studio/mount.d.ts.map +1 -1
  31. package/dist/crowdy-studio/mount.js +3 -1
  32. package/dist/crowdy-studio/splitter.d.ts +33 -0
  33. package/dist/crowdy-studio/splitter.d.ts.map +1 -0
  34. package/dist/crowdy-studio/splitter.js +133 -0
  35. package/dist/crowdy-studio/styles.d.ts +1 -1
  36. package/dist/crowdy-studio/styles.d.ts.map +1 -1
  37. package/dist/crowdy-studio/styles.js +128 -13
  38. package/dist/domains/gameModel.d.ts +68 -1
  39. package/dist/domains/gameModel.d.ts.map +1 -1
  40. package/dist/domains/gameModel.js +88 -1
  41. package/dist/generated/graphql.d.ts +132 -15
  42. package/dist/generated/graphql.d.ts.map +1 -1
  43. package/dist/generated/graphql.js +12 -0
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/kit/npcs.d.ts.map +1 -1
  48. package/dist/kit/social.d.ts.map +1 -1
  49. package/dist/player-host/control-banner.d.ts +40 -0
  50. package/dist/player-host/control-banner.d.ts.map +1 -0
  51. package/dist/player-host/control-banner.js +252 -0
  52. package/dist/player-host/control-gate.d.ts +77 -0
  53. package/dist/player-host/control-gate.d.ts.map +1 -0
  54. package/dist/player-host/control-gate.js +197 -0
  55. package/dist/player-host/index.d.ts +2 -0
  56. package/dist/player-host/index.d.ts.map +1 -1
  57. package/dist/player-host/index.js +2 -0
  58. 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
- /** Accessible, text-only rendering for the integrated durable agent dock. */
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;AAavC,MAAM,WAAW,gCAAgC;IAC/C,mBAAmB,CAAC,EAAE,MAAM;QAC1B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,sBAAsB,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;CACV;AAED,6EAA6E;AAC7E,qBAAa,yBAAyB;IAqBlC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IArB1B,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,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,QAAQ,CAAS;gBAGvB,IAAI,EAAE,WAAW,EACA,UAAU,EAAE,2BAA2B,EACvC,OAAO,GAAE,gCAAqC;IAkKjE,MAAM,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IAyB7C,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,cAAc;IA4DtB,OAAO,CAAC,eAAe;IA+CvB,OAAO,CAAC,iBAAiB;YAkCX,GAAG;CAQlB"}
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
- /** Accessible, text-only rendering for the integrated durable agent dock. */
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
- this.budget = document.createElement('div');
54
- this.budget.className = 'ck-crowdy-studio-agent-budget';
55
- this.budget.setAttribute('aria-label', 'Agent budget');
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
- const conversationTitle = heading('Conversation');
96
- this.messages = document.createElement('div');
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.approvals = document.createElement('section');
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
- const form = document.createElement('form');
116
- form.className = 'ck-crowdy-studio-agent-composer';
117
- this.composer = document.createElement('textarea');
118
- this.composer.maxLength = 32768;
119
- this.composer.rows = 3;
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
- /** Modular DOM shell; editor implementations mount into `editorHost`. */
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
- private readonly projectSelect;
10
- private readonly saveBadge;
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,EAE3B,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,sBAAsB,CAAC;AAY9B,yEAAyE;AACzE,qBAAa,oBAAoB;IAqC7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IApC7B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmB;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2C;IACxE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,QAAQ,CAAS;gBAGvB,IAAI,EAAE,WAAW,EACA,UAAU,EAAE,sBAAsB,EACnD,eAAe,CAAC,EAAE,2BAA2B,EAC7C,YAAY,GAAE,gCAAqC;IA2MrD,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IA+BtC,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,cAAc;IAkDtB,OAAO,CAAC,gBAAgB;IAuCxB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,iBAAiB;YAgBX,GAAG;CAQlB"}
1
+ {"version":3,"file":"dom-shell.d.ts","sourceRoot":"","sources":["../../src/crowdy-studio/dom-shell.ts"],"names":[],"mappings":"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"}