Skip to content

Commit af3907d

Browse files
committed
Merge branch 'main' into host/ai-message-attached-card-realm-icon-cs-6551
2 parents 12d7481 + 2b28723 commit af3907d

File tree

25 files changed

+514
-297
lines changed

25 files changed

+514
-297
lines changed

packages/ai-bot/helpers.ts

+16-21
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,6 @@ export function constructHistory(history: IRoomEvent[]) {
6969
serializedCardFromFragments(id, fragments),
7070
);
7171
}
72-
if (
73-
event.content.data.context.openCardsEventIds &&
74-
event.content.data.context.openCardsEventIds.length > 0
75-
) {
76-
event.content.data.context.openCards =
77-
event.content.data.context.openCardsEventIds.map((id) =>
78-
serializedCardFromFragments(id, fragments),
79-
);
80-
}
8172
}
8273

8374
if (event.content['m.relates_to']?.rel_type === 'm.replace') {
@@ -112,19 +103,23 @@ function serializedCardFromFragments(
112103
`No card fragment found in fragments cache for event id ${eventId}`,
113104
);
114105
}
115-
if (fragment.data.totalParts === 1) {
116-
return JSON.parse(fragment.data.cardFragment) as LooseSingleCardDocument;
117-
}
106+
let cardFragments: CardFragmentContent[] = [];
107+
let currentFragment: string | undefined = eventId;
108+
do {
109+
let fragment = fragments.get(currentFragment);
110+
if (!fragment) {
111+
throw new Error(
112+
`No card fragment found in cache for event id ${eventId}`,
113+
);
114+
}
115+
cardFragments.push(fragment);
116+
currentFragment = fragment.data.nextFragment;
117+
} while (currentFragment);
118118

119-
let cardFragments = [
120-
fragment,
121-
...[...fragments.values()]
122-
.filter((f) => f.data.firstFragment && f.data.firstFragment === eventId)
123-
.sort((a, b) => (a.data.index = b.data.index)),
124-
];
125-
if (cardFragments.length !== fragment.data.totalParts) {
119+
cardFragments.sort((a, b) => (a.data.index = b.data.index));
120+
if (cardFragments.length !== cardFragments[0].data.totalParts) {
126121
throw new Error(
127-
`Expected to find ${fragment.data.totalParts} fragments for fragment of event id ${eventId} but found ${cardFragments.length} fragments`,
122+
`Expected to find ${cardFragments[0].data.totalParts} fragments for fragment of event id ${eventId} but found ${cardFragments.length} fragments`,
128123
);
129124
}
130125
return JSON.parse(
@@ -241,7 +236,7 @@ export function getStartOfConversation(
241236
) {
242237
/**
243238
* Get just the start of the conversation
244-
* useful for summarising while limiting the context
239+
* useful for summarizing while limiting the context
245240
*/
246241
let messages: OpenAIPromptMessage[] = [];
247242
let totalLength = 0;

packages/ai-bot/tests/history-construction-test.ts

+27-33
Original file line numberDiff line numberDiff line change
@@ -377,15 +377,15 @@ module('constructHistory', () => {
377377
{
378378
type: 'm.room.message',
379379
event_id: '1',
380-
origin_server_ts: 1234567890,
380+
origin_server_ts: 1234567900,
381381
content: {
382382
msgtype: 'org.boxel.cardFragment',
383383
format: 'org.boxel.card',
384384
formatted_body: '',
385385
body: '',
386386
data: JSON.stringify({
387-
cardFragment: `{"data":{"type":"card","id":"http://localhost:4201/drafts/Author/1","attributes":{"firstName":"Ter`,
388-
index: 0,
387+
cardFragment: `ry","lastName":"Pratchett"},"meta":{"adoptsFrom":{"module":"../author","name":"Author"}}}}`,
388+
index: 1,
389389
totalParts: 2,
390390
}),
391391
},
@@ -399,20 +399,16 @@ module('constructHistory', () => {
399399
{
400400
type: 'm.room.message',
401401
event_id: '2',
402-
origin_server_ts: 1234567900,
402+
origin_server_ts: 1234567890,
403403
content: {
404-
'm.relates_to': {
405-
rel_type: 'append',
406-
event_id: '1',
407-
},
408404
msgtype: 'org.boxel.cardFragment',
409405
format: 'org.boxel.card',
410406
formatted_body: '',
411407
body: '',
412408
data: JSON.stringify({
413-
firstFragment: '1',
414-
cardFragment: `ry","lastName":"Pratchett"},"meta":{"adoptsFrom":{"module":"../author","name":"Author"}}}}`,
415-
index: 1,
409+
cardFragment: `{"data":{"type":"card","id":"http://localhost:4201/drafts/Author/1","attributes":{"firstName":"Ter`,
410+
index: 0,
411+
nextFragment: '1',
416412
totalParts: 2,
417413
}),
418414
},
@@ -457,9 +453,9 @@ module('constructHistory', () => {
457453
data: JSON.stringify({
458454
context: {
459455
functions: [],
460-
openCardsEventIds: ['3'],
456+
openCardIds: ['http://localhost:4201/drafts/Author/1'],
461457
},
462-
attachedCardsEventIds: ['1'],
458+
attachedCardsEventIds: ['2', '3'],
463459
}),
464460
},
465461
sender: '@user:localhost',
@@ -485,27 +481,9 @@ module('constructHistory', () => {
485481
data: {
486482
context: {
487483
functions: [],
488-
openCardsEventIds: ['3'],
489-
openCards: [
490-
{
491-
data: {
492-
type: 'card',
493-
id: 'http://localhost:4201/drafts/Author/1',
494-
attributes: {
495-
firstName: 'Mango',
496-
lastName: 'Abdel-Rahman',
497-
},
498-
meta: {
499-
adoptsFrom: {
500-
module: '../author',
501-
name: 'Author',
502-
},
503-
},
504-
},
505-
},
506-
],
484+
openCardIds: ['http://localhost:4201/drafts/Author/1'],
507485
},
508-
attachedCardsEventIds: ['1'],
486+
attachedCardsEventIds: ['2', '3'],
509487
attachedCards: [
510488
{
511489
data: {
@@ -523,6 +501,22 @@ module('constructHistory', () => {
523501
},
524502
},
525503
},
504+
{
505+
data: {
506+
type: 'card',
507+
id: 'http://localhost:4201/drafts/Author/1',
508+
attributes: {
509+
firstName: 'Mango',
510+
lastName: 'Abdel-Rahman',
511+
},
512+
meta: {
513+
adoptsFrom: {
514+
module: '../author',
515+
name: 'Author',
516+
},
517+
},
518+
},
519+
},
526520
],
527521
},
528522
},

packages/base/base64-image.gts

+36-69
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import {
77
Component,
88
primitive,
99
useIndexBasedKey,
10+
BaseDefConstructor,
11+
BaseInstanceType,
12+
deserialize,
1013
} from './card-api';
1114
import { tracked } from '@glimmer/tracking';
12-
import Modifier from 'ember-modifier';
1315
import { on } from '@ember/modifier';
1416
import { fn } from '@ember/helper';
1517
import { hash } from '@ember/helper';
16-
import { pick, eq } from '@cardstack/boxel-ui/helpers';
1718
import { FieldContainer, BoxelInput } from '@cardstack/boxel-ui/components';
1819
import { FailureBordered } from '@cardstack/boxel-ui/icons';
1920
import { htmlSafe } from '@ember/template';
21+
import { RadioInput } from '@cardstack/boxel-ui/components';
2022

2123
const atomImgHeight = 200;
2224

@@ -196,6 +198,16 @@ class ImageSizeField extends FieldDef {
196198
static displayName = 'Image Size';
197199
static [primitive]: 'actual' | 'contain' | 'cover';
198200
static [useIndexBasedKey]: never;
201+
202+
static async [deserialize]<T extends BaseDefConstructor>(
203+
this: T,
204+
val: any,
205+
): Promise<BaseInstanceType<T>> {
206+
if (val === undefined || val === null) {
207+
return 'actual' as BaseInstanceType<T>;
208+
}
209+
return val as BaseInstanceType<T>;
210+
}
199211
static embedded = class Embedded extends Component<typeof this> {
200212
<template>
201213
{{@model}}
@@ -204,58 +216,32 @@ class ImageSizeField extends FieldDef {
204216
static edit = class Edit extends Component<typeof this> {
205217
<template>
206218
<div class='radio-group' data-test-radio-group={{@fieldName}}>
207-
<label for='{{this.radioGroup}}_actual'>
208-
Actual
209-
<input
210-
data-test-actual-size-input
211-
type='radio'
212-
{{RadioInitializer @model 'actual'}}
213-
id='{{this.radioGroup}}_actual'
214-
name='{{this.radioGroup}}'
215-
checked={{eq @model 'actual'}}
216-
{{on 'change' (pick 'target.value' (fn @set 'actual'))}}
217-
/>
218-
</label>
219-
<label for='{{this.radioGroup}}_contain'>
220-
Contain
221-
<input
222-
data-test-contain-size-input
223-
type='radio'
224-
{{RadioInitializer @model 'contain'}}
225-
id='{{this.radioGroup}}_contain'
226-
name='{{this.radioGroup}}'
227-
checked={{eq @model 'contain'}}
228-
{{on 'change' (pick 'target.value' (fn @set 'contain'))}}
229-
/>
230-
</label>
231-
<label for='{{this.radioGroup}}_cover'>
232-
Cover
233-
<input
234-
data-test-cover-size-input
235-
type='radio'
236-
{{RadioInitializer @model 'cover'}}
237-
id='{{this.radioGroup}}_cover'
238-
name='{{this.radioGroup}}'
239-
checked={{eq @model 'cover'}}
240-
{{on 'change' (pick 'target.value' (fn @set 'cover'))}}
241-
/>
242-
</label>
219+
<RadioInput
220+
@items={{this.items}}
221+
@groupDescription='Image Size Field'
222+
name='{{this.radioGroup}}'
223+
@checkedId={{this.checkedId}}
224+
@hideBorder={{true}}
225+
as |item|
226+
>
227+
<item.component @onChange={{fn @set item.data.id}}>
228+
{{item.data.text}}
229+
</item.component>
230+
</RadioInput>
243231
</div>
244-
<style>
245-
.radio-group {
246-
display: flex;
247-
justify-content: space-between;
248-
}
249-
</style>
232+
<style></style>
250233
</template>
251234

252235
private radioGroup = `__cardstack_img_size${groupNumber++}__`;
253-
constructor(owner: unknown, args: any) {
254-
super(owner, args);
255-
// initializes to 'actual'
256-
if (!this.args.model) {
257-
this.args.set('actual');
258-
}
236+
237+
private items = [
238+
{ id: 'actual', text: 'Actual' },
239+
{ id: 'contain', text: 'Contain' },
240+
{ id: 'cover', text: 'Cover' },
241+
];
242+
243+
get checkedId() {
244+
return this.args.model;
259245
}
260246
};
261247
static atom = class Atom extends Component<typeof this> {
@@ -265,25 +251,6 @@ class ImageSizeField extends FieldDef {
265251
};
266252
}
267253

268-
interface RadioSignature {
269-
element: HTMLInputElement;
270-
Args: {
271-
Positional: [
272-
model: 'actual' | 'cover' | 'contain' | null,
273-
inputType: 'actual' | 'cover' | 'contain',
274-
];
275-
};
276-
}
277-
278-
class RadioInitializer extends Modifier<RadioSignature> {
279-
modify(
280-
element: HTMLInputElement,
281-
[model, inputType]: RadioSignature['Args']['Positional'],
282-
) {
283-
element.checked = model === inputType;
284-
}
285-
}
286-
287254
function getConstrainedImageSize(maxHeight: number) {
288255
return class ConstrainedSize extends Component<typeof Base64ImageField> {
289256
get height() {

0 commit comments

Comments
 (0)