Skip to content

Commit ac45bad

Browse files
committed
WIP render embedded view of command result in AI panel
1 parent 2d55163 commit ac45bad

File tree

11 files changed

+227
-264
lines changed

11 files changed

+227
-264
lines changed

Diff for: packages/base/command-result.gts

+37-157
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
import GlimmerComponent from '@glimmer/component';
22
import { cached, tracked } from '@glimmer/tracking';
3-
import { array } from '@ember/helper';
43
import { on } from '@ember/modifier';
54
import { action } from '@ember/object';
5+
import { Button, FieldContainer } from '@cardstack/boxel-ui/components';
6+
import { eq } from '@cardstack/boxel-ui/helpers';
67
import {
7-
BoxelDropdown,
8-
Button,
9-
FieldContainer,
10-
Header,
11-
IconButton,
12-
Menu,
13-
} from '@cardstack/boxel-ui/components';
14-
import { eq, menuItem } from '@cardstack/boxel-ui/helpers';
15-
import {
16-
ArrowLeft,
178
IconMinusCircle,
189
IconPlus,
1910
IconSearchThick,
20-
ThreeDotsHorizontal,
2111
} from '@cardstack/boxel-ui/icons';
22-
import { getCard } from '@cardstack/runtime-common';
12+
import { getCard, primitive } from '@cardstack/runtime-common';
2313
import {
2414
BaseDef,
2515
CardDef,
@@ -30,8 +20,8 @@ import {
3020
field,
3121
type CardContext,
3222
type Format,
23+
FieldDef,
3324
} from './card-api';
34-
import { CommandObjectField } from './command';
3525

3626
type AttachedCardResource = {
3727
card: CardDef | undefined;
@@ -48,6 +38,10 @@ interface ResourceListSignature {
4838
context?: CardContext;
4939
}
5040

41+
export class JsonField extends FieldDef {
42+
static [primitive]: Record<string, any>;
43+
}
44+
5145
class ResourceList extends GlimmerComponent<ResourceListSignature> {
5246
<template>
5347
<ol class='result-list {{@format}}' data-test-result-list>
@@ -117,7 +111,9 @@ class ResourceList extends GlimmerComponent<ResourceListSignature> {
117111
</template>
118112
}
119113

120-
class CommandResultEmbeddedView extends Component<typeof CommandResult> {
114+
class SearchCardsResultEmbeddedView extends Component<
115+
typeof SearchCardsResult
116+
> {
121117
@tracked showAllResults = false;
122118

123119
@cached
@@ -181,74 +177,25 @@ class CommandResultEmbeddedView extends Component<typeof CommandResult> {
181177
}
182178

183179
<template>
184-
<div class='command-result' data-test-command-result>
185-
<Header
186-
@title='Search Results'
187-
@subtitle='{{this.numberOfCards}} {{if
188-
(eq this.numberOfCards 1)
189-
"Result"
190-
"Results"
191-
}}'
192-
@hasBottomBorder={{true}}
193-
class='header'
194-
data-test-command-result-header
195-
>
196-
<:icon>
197-
<div class='search-icon-container'>
198-
<IconSearchThick width='16' height='16' />
199-
</div>
200-
</:icon>
201-
<:actions>
202-
<BoxelDropdown>
203-
<:trigger as |bindings|>
204-
<IconButton
205-
@icon={{ThreeDotsHorizontal}}
206-
@width='20px'
207-
@height='20px'
208-
class='icon-button'
209-
aria-label='Options'
210-
data-test-more-options-button
211-
{{bindings}}
212-
/>
213-
</:trigger>
214-
<:content as |dd|>
215-
<Menu
216-
class='options-menu'
217-
@items={{array
218-
(menuItem
219-
'Copy to Workspace' this.copyToWorkspace icon=ArrowLeft
220-
)
221-
}}
222-
@closeMenu={{dd.close}}
223-
/>
224-
</:content>
225-
</BoxelDropdown>
226-
</:actions>
227-
</Header>
228-
<div class='body'>
229-
<ResourceList @resources={{this.attachedResources}} @format='atom' />
230-
<div class='footer'>
231-
{{#if this.numberOfCardsGreaterThanPaginateSize}}
232-
<Button
233-
@size='small'
234-
class='toggle-show'
235-
{{on 'click' this.toggleShow}}
236-
data-test-toggle-show-button
237-
>
238-
{{#if this.showAllResults}}
239-
<IconMinusCircle
240-
width='11px'
241-
height='11px'
242-
role='presentation'
243-
/>
244-
{{else}}
245-
<IconPlus width='11px' height='11px' role='presentation' />
246-
{{/if}}
180+
<div class='command-result'>
181+
<ResourceList @resources={{this.attachedResources}} @format='atom' />
182+
<div class='footer'>
183+
{{#if this.numberOfCardsGreaterThanPaginateSize}}
184+
<Button
185+
@size='small'
186+
class='toggle-show'
187+
{{on 'click' this.toggleShow}}
188+
data-test-toggle-show-button
189+
>
190+
{{#if this.showAllResults}}
191+
<IconMinusCircle width='11px' height='11px' role='presentation' />
192+
{{else}}
193+
<IconPlus width='11px' height='11px' role='presentation' />
194+
{{/if}}
247195

248-
{{this.toggleShowText}}
249-
</Button>
250-
{{/if}}
251-
</div>
196+
{{this.toggleShowText}}
197+
</Button>
198+
{{/if}}
252199
</div>
253200
</div>
254201
<style scoped>
@@ -257,36 +204,6 @@ class CommandResultEmbeddedView extends Component<typeof CommandResult> {
257204
background-color: var(--boxel-light);
258205
border-radius: var(--boxel-border-radius);
259206
--left-padding: var(--boxel-sp-xs);
260-
}
261-
.search-icon-container {
262-
background-color: var(--boxel-border-color);
263-
display: flex;
264-
padding: var(--boxel-sp-xxxs);
265-
border-radius: var(--boxel-border-radius-sm);
266-
}
267-
.header {
268-
--boxel-label-color: var(--boxel-450);
269-
--boxel-label-font: 600 var(--boxel-font-xs);
270-
--boxel-header-padding: var(--boxel-sp-xxxs) var(--boxel-sp-xxxs) 0
271-
var(--left-padding);
272-
}
273-
.header :deep(.content) {
274-
gap: 0;
275-
}
276-
.icon-button {
277-
--icon-color: var(--boxel-dark);
278-
}
279-
.icon-button:hover {
280-
--icon-color: var(--boxel-highlight);
281-
}
282-
.options-menu :deep(.boxel-menu__item__content) {
283-
padding-right: var(--boxel-sp-xxs);
284-
padding-left: var(--boxel-sp-xxs);
285-
}
286-
.options-menu :deep(.check-icon) {
287-
display: none;
288-
}
289-
.body {
290207
display: flex;
291208
flex-direction: column;
292209
font-weight: 600;
@@ -322,22 +239,9 @@ class CommandResultEmbeddedView extends Component<typeof CommandResult> {
322239
}
323240
</style>
324241
</template>
325-
326-
@action async copyToWorkspace() {
327-
let newCard = await this.args.context?.actions?.copyCard?.(
328-
this.args.model as CardDef,
329-
);
330-
if (!newCard) {
331-
console.error('Could not copy card to workspace.');
332-
return;
333-
}
334-
this.args.context?.actions?.viewCard(newCard, 'isolated', {
335-
openCardInRightMostStack: true,
336-
});
337-
}
338242
}
339243

340-
class CommandResultIsolated extends CommandResultEmbeddedView {
244+
class SearchCardsResultIsolatedView extends SearchCardsResultEmbeddedView {
341245
<template>
342246
<section class='command-result' data-test-command-result-isolated>
343247
<header>
@@ -351,9 +255,6 @@ class CommandResultIsolated extends CommandResultEmbeddedView {
351255
<FieldContainer @label='Description'>
352256
{{@model.description}}
353257
</FieldContainer>
354-
<FieldContainer @label='Filter'>
355-
<pre>{{this.filterString}}</pre>
356-
</FieldContainer>
357258
<FieldContainer @label='Results' class='results'>
358259
<ResourceList
359260
@resources={{this.attachedResources}}
@@ -393,38 +294,17 @@ class CommandResultIsolated extends CommandResultEmbeddedView {
393294
</template>
394295

395296
@tracked showAllResults = true;
396-
397-
get filterString() {
398-
if (!this.args.model.toolCallArgs?.attributes.filter) {
399-
return;
400-
}
401-
return JSON.stringify(
402-
this.args.model.toolCallArgs.attributes.filter,
403-
null,
404-
2,
405-
);
406-
}
407297
}
408298

409-
export class CommandResult extends CardDef {
410-
static displayName = 'Command Result';
411-
@field toolCallName = contains(StringField);
412-
@field toolCallId = contains(StringField);
413-
@field toolCallArgs = contains(CommandObjectField);
299+
export class SearchCardsResult extends CardDef {
300+
static displayName = 'Search Results';
301+
static icon = IconSearchThick;
414302
@field cardIds = containsMany(StringField);
303+
static embedded = SearchCardsResultEmbeddedView;
304+
static isolated = SearchCardsResultIsolatedView;
415305
@field title = contains(StringField, {
416-
computeVia: function (this: CommandResult) {
417-
return this.toolCallName === 'searchCard'
418-
? 'Search Results'
419-
: 'Command Result';
306+
computeVia: function (this: SearchCardsResult) {
307+
return 'Search Results';
420308
},
421309
});
422-
@field description = contains(StringField, {
423-
computeVia: function (this: CommandResult) {
424-
return this.toolCallArgs?.description;
425-
},
426-
});
427-
428-
static embedded = CommandResultEmbeddedView;
429-
static isolated = CommandResultIsolated;
430310
}

Diff for: packages/base/command.gts

+14-36
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,16 @@ import {
1414
import CodeRefField from './code-ref';
1515
import BooleanField from './boolean';
1616
import { SkillCard } from './skill-card';
17+
import { JsonField } from './command-result';
18+
import { SearchCardsResult } from './command-result';
1719

1820
export type CommandStatus = 'applied' | 'ready' | 'applying';
1921

20-
class CommandObjectFieldTemplate extends Component<typeof CommandObjectField> {
21-
<template>
22-
<pre>{{this.stringValue}}</pre>
23-
<style scoped>
24-
pre {
25-
margin: 0;
26-
white-space: pre-wrap;
27-
}
28-
</style>
29-
</template>
30-
31-
get stringValue() {
32-
return JSON.stringify(this.args.model, null, 2);
33-
}
34-
}
35-
36-
export class CommandObjectField extends FieldDef {
37-
static [primitive]: Record<string, any>;
38-
static [queryableValue](value: Record<string, any> | undefined) {
39-
return Boolean(value) && typeof value === 'object'
40-
? JSON.stringify(value)
41-
: undefined;
42-
}
43-
static edit = CommandObjectFieldTemplate;
44-
static embedded = CommandObjectFieldTemplate;
45-
}
46-
4722
export class SaveCardInput extends CardDef {
4823
@field realm = contains(StringField);
4924
@field card = linksTo(CardDef);
5025
}
5126

52-
class JsonField extends FieldDef {
53-
static [primitive]: Record<string, any>;
54-
}
55-
5627
export class PatchCardInput extends CardDef {
5728
@field cardId = contains(StringField);
5829
@field patch = contains(JsonField);
@@ -114,16 +85,21 @@ export class SendAiAssistantMessageResult extends CardDef {
11485
@field eventId = contains(StringField);
11586
}
11687

117-
<<<<<<< HEAD
11888
export class GetBoxelUIStateResult extends CardDef {
11989
@field submode = contains(StringField);
12090
//TODO expand this to include more of the UI state:
12191
// - open cards
12292
// - current room ID
123-
}
124-
125-
export class SearchCardsResult extends CardDef {
126-
@field cardDocs = containsMany(JsonField);
93+
static embedded = class Embedded extends Component<
94+
typeof GetBoxelUIStateResult
95+
> {
96+
<template>
97+
<div>
98+
<h2>Boxel UI State</h2>
99+
<div>Submode: {{@model.submode}}</div>
100+
</div>
101+
</template>
102+
};
127103
}
128104

129105
export class LegacyGenerateAppModuleResult extends CardDef {
@@ -134,3 +110,5 @@ export class LegacyGenerateAppModuleResult extends CardDef {
134110
export class OpenAiAssistantRoomInput extends CardDef {
135111
@field roomId = contains(StringField);
136112
}
113+
114+
export { SearchCardsResult };

0 commit comments

Comments
 (0)