Skip to content

Commit 268e1b9

Browse files
kriscendobotclaude
andcommitted
feat(chat): inventory + create menu and new-agent wizard
Implements the chat-inventory-create-menu design (PR #404, merged onto llm): a `+` create affordance at the TOP of the Chat inventory whose pop-over menu lists the whole-cloth item types Chat can mint. Live this phase: - The `+` header-row button + keyboard-navigable pop-over menu. - Filesystem mount — provideMount(hostPath, petName), end to end. - Scratch space — provideScratchMount(petName), end to end. - New-agent wizard: harness pane (Lal/Fae/Genie, discovered + annotated), inference-source pane (Anthropic/OpenAI/Ollama/Ollama-Remote/OpenRouter, provider-by-name with hidden URLs, auth by authShape, model picker), and an endowment pane. Submit routes to the chosen harness's outstanding manager form via listMessages/reverseLocate/submit — no new daemon API. Documented placeholders (per the design's phasing): - Passable value and Structured value surface the architectural direction without a half-implemented control. - Pane 3's endowment roster is a documentation-only checklist this phase. No daemon-side changes: composes existing provideMount / provideScratchMount / listMessages / submit primitives only. New component test (test/component/create-menu.test.js) exercises the rendered DOM: menu opens with five items, mount calls provideMount, scratch calls provideScratchMount, special-name rejection, the placeholder, and the wizard's three panes + form-submit routing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5e3232c commit 268e1b9

8 files changed

Lines changed: 2138 additions & 0 deletions

File tree

packages/chat/chat.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { createShareModal } from '@endo/space-channel/share-modal.js';
3333
import { outlinerComponent } from './outliner-component.js';
3434
import { inboxComponent } from './inbox-component.js';
3535
import { inventoryComponent } from './inventory-component.js';
36+
import { createInventoryCreateMenu } from './create-menu.js';
3637
import { channelListComponent } from './channel-list.js';
3738
import { createSpacesGutter } from './spaces-gutter.js';
3839
import { inventoryGraphComponent } from './inventory-graph-component.js';
@@ -58,6 +59,14 @@ const template = `
5859
aria-pressed="false" title="Show special names">@</button>
5960
</div>
6061
</div>
62+
<div class="inventory-create-row">
63+
<button type="button" id="inventory-create-button" class="inventory-create-button"
64+
aria-haspopup="menu" aria-expanded="false" title="Create inventory item">
65+
<span class="inventory-create-glyph">+</span>
66+
<span class="inventory-create-text">Create&hellip;</span>
67+
</button>
68+
<div id="inventory-create-menu"></div>
69+
</div>
6170
<div class="pet-list"></div>
6271
<div id="profile-bar"></div>
6372
</div>
@@ -102,6 +111,8 @@ const template = `
102111
<div id="chat-modeline"></div>
103112
</div>
104113
114+
<div id="inventory-create-modal-container"></div>
115+
105116
<div id="eval-form-backdrop"></div>
106117
<div id="eval-form-container"></div>
107118
@@ -247,6 +258,9 @@ const bodyComponent = (
247258
/** @type {{ dispose: () => void } | null} */
248259
let valueRef = null;
249260

261+
/** @type {{ dispose: () => void } | null} */
262+
let createMenuRef = null;
263+
250264
$parent.innerHTML = template;
251265

252266
const $messages = /** @type {HTMLElement} */ (
@@ -425,6 +439,28 @@ const bodyComponent = (
425439
);
426440
valueRef = { dispose: disposeValue };
427441

442+
// Wire the inventory `+` create menu (design: chat-inventory-create-menu).
443+
// The button lives in the static template at the top of the inventory
444+
// panel; the controller owns the pop-over menu and the per-type create
445+
// modals, dispatching against the active profile's powers.
446+
const $createButton = /** @type {HTMLButtonElement | null} */ (
447+
$parent.querySelector('#inventory-create-button')
448+
);
449+
const $createMenu = /** @type {HTMLElement | null} */ (
450+
$parent.querySelector('#inventory-create-menu')
451+
);
452+
const $createModalContainer = /** @type {HTMLElement | null} */ (
453+
$parent.querySelector('#inventory-create-modal-container')
454+
);
455+
if ($createButton && $createMenu && $createModalContainer) {
456+
createMenuRef = createInventoryCreateMenu({
457+
$button: $createButton,
458+
$menuContainer: $createMenu,
459+
$modalContainer: $createModalContainer,
460+
getPowers: () => /** @type {ERef<EndoHost>} */ (resolvedPowers),
461+
});
462+
}
463+
428464
const getConversationPetName = () => {
429465
if (!activeConversation) return null;
430466
const { petName } = activeConversation;
@@ -1647,6 +1683,10 @@ const bodyComponent = (
16471683
valueRef.dispose();
16481684
valueRef = null;
16491685
}
1686+
if (createMenuRef) {
1687+
createMenuRef.dispose();
1688+
createMenuRef = null;
1689+
}
16501690
};
16511691
};
16521692

0 commit comments

Comments
 (0)