Skip to content

Commit 38975cc

Browse files
committed
Show auto attached file in the attachment picker
1 parent f450589 commit 38975cc

File tree

2 files changed

+94
-13
lines changed

2 files changed

+94
-13
lines changed

Diff for: packages/host/app/components/ai-assistant/attachment-picker/index.gts

+34-9
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,21 @@ import {
1818

1919
import CardPill from '@cardstack/host/components/card-pill';
2020

21+
import FilePill from '@cardstack/host/components/file-pill';
22+
2123
import { type CardDef } from 'https://cardstack.com/base/card-api';
2224
import { type FileDef } from 'https://cardstack.com/base/file-api';
2325

2426
interface Signature {
2527
Element: HTMLDivElement;
2628
Args: {
2729
autoAttachedCards?: TrackedSet<CardDef>;
28-
autoAttachedFiles?: FileDef[];
2930
cardsToAttach: CardDef[] | undefined;
31+
autoAttachedFile?: FileDef;
3032
filesToAttach: FileDef[] | undefined;
3133
chooseCard: (card: CardDef) => void;
3234
removeCard: (card: CardDef) => void;
35+
removeFile: (file: FileDef) => void;
3336
maxNumberOfItemsToAttach?: number;
3437
};
3538
}
@@ -64,6 +67,27 @@ export default class AiAssistantAttachmentPicker extends Component<Signature> {
6467
@removeCard={{@removeCard}}
6568
/>
6669
{{/if}}
70+
{{else}}
71+
{{#if (this.isAutoAttachedFile item)}}
72+
<Tooltip @placement='top'>
73+
<:trigger>
74+
<FilePill
75+
@file={{item}}
76+
@isAutoAttachedFile={{true}}
77+
@removeFile={{@removeFile}}
78+
/>
79+
</:trigger>
80+
<:content>
81+
Currently opened file is shared automatically
82+
</:content>
83+
</Tooltip>
84+
{{else}}
85+
<FilePill
86+
@file={{item}}
87+
@isAutoAttachedFile={{false}}
88+
@removeFile={{@removeFile}}
89+
/>
90+
{{/if}}
6791
{{/if}}
6892
{{/each}}
6993
{{#if
@@ -141,23 +165,24 @@ export default class AiAssistantAttachmentPicker extends Component<Signature> {
141165
};
142166

143167
isAutoAttachedCard = (card: CardDef) => {
144-
if (this.args.autoAttachedCards === undefined) {
145-
return false;
146-
}
147-
return this.args.autoAttachedCards.has(card);
168+
return this.args.autoAttachedCards?.has(card);
169+
};
170+
171+
isAutoAttachedFile = (file: FileDef) => {
172+
return this.args.autoAttachedFile?.sourceUrl === file.sourceUrl;
148173
};
149174

150175
private get items() {
151176
let cards = this.args.cardsToAttach ?? [];
152-
let files = this.args.filesToAttach ?? [];
177+
153178
if (this.args.autoAttachedCards) {
154179
cards = [...new Set([...this.args.autoAttachedCards, ...cards])];
155180
}
156181

157182
cards = cards.filter((card) => card.id); // Dont show new unsaved cards
158-
159-
if (this.args.autoAttachedFiles) {
160-
files = [...new Set([...this.args.autoAttachedFiles, ...files])];
183+
let files: FileDef[] = [];
184+
if (this.args.autoAttachedFile) {
185+
files = [...new Set([this.args.autoAttachedFile])];
161186
}
162187
return [...cards, ...files];
163188
}

Diff for: packages/host/app/components/matrix/room.gts

+60-4
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ import { getRoom } from '@cardstack/host/resources/room';
4242

4343
import type CardService from '@cardstack/host/services/card-service';
4444
import type CommandService from '@cardstack/host/services/command-service';
45+
import LoaderService from '@cardstack/host/services/loader-service';
4546
import type MatrixService from '@cardstack/host/services/matrix-service';
4647
import { type MonacoSDK } from '@cardstack/host/services/monaco-service';
4748
import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';
4849

4950
import { type CardDef } from 'https://cardstack.com/base/card-api';
51+
import * as FileAPI from 'https://cardstack.com/base/file-api';
5052
import type { SkillCard } from 'https://cardstack.com/base/skill-card';
5153

5254
import AiAssistantAttachmentPicker from '../ai-assistant/attachment-picker';
@@ -139,8 +141,9 @@ export default class Room extends Component<Signature> {
139141
@cardsToAttach={{this.cardsToAttach}}
140142
@chooseCard={{this.chooseCard}}
141143
@removeCard={{this.removeCard}}
142-
@autoAttachedFiles={{this.autoAttachedFiles}}
144+
@autoAttachedFile={{this.autoAttachedFile}}
143145
@filesToAttach={{this.filesToAttach}}
146+
@removeFile={{this.excludeFile}}
144147
/>
145148
<LLMSelect
146149
@selected={{this.roomResource.activeLLM}}
@@ -209,6 +212,9 @@ export default class Room extends Component<Signature> {
209212
@service private declare commandService: CommandService;
210213
@service private declare matrixService: MatrixService;
211214
@service private declare operatorModeStateService: OperatorModeStateService;
215+
@service private declare loaderService: LoaderService;
216+
@tracked private fileAPI: typeof FileAPI | undefined;
217+
@tracked private filesToExclude: FileAPI.FileDef[] = [];
212218

213219
private roomResource = getRoom(
214220
this,
@@ -238,6 +244,7 @@ export default class Room extends Component<Signature> {
238244
constructor(owner: Owner, args: Signature['Args']) {
239245
super(owner, args);
240246
this.doMatrixEventFlush.perform();
247+
this.doLoadFileAPI.perform();
241248
registerDestructor(this, () => {
242249
this.scrollState().messageVisibilityObserver.disconnect();
243250
});
@@ -274,12 +281,46 @@ export default class Room extends Component<Signature> {
274281
return state;
275282
}
276283

277-
private get autoAttachedFiles() {
278-
return []; // TODO: implement
284+
private get autoAttachedFileUrl() {
285+
if (!this.fileAPI) {
286+
return undefined;
287+
}
288+
return this.operatorModeStateService.state.codePath?.href;
289+
}
290+
291+
private get autoAttachedFile() {
292+
if (!this.autoAttachedFileUrl) {
293+
return undefined;
294+
}
295+
296+
if (!this.fileAPI) {
297+
return undefined;
298+
}
299+
300+
if (
301+
this.filesToExclude.some(
302+
(file) => file.sourceUrl === this.autoAttachedFileUrl,
303+
)
304+
) {
305+
return undefined;
306+
}
307+
308+
return new this.fileAPI.FileDef({
309+
sourceUrl: this.autoAttachedFileUrl,
310+
name: this.autoAttachedFileUrl.split('/').pop(),
311+
});
279312
}
280313

281314
private get filesToAttach() {
282-
return []; // TODO: implement
315+
if (!this.fileAPI) {
316+
return [];
317+
}
318+
// User will be eventually able to attach files manually (from the realm file system, or uploaded from the device),
319+
// but for now we only support auto-attaching files from the operator mode code path
320+
if (!this.autoAttachedFile) {
321+
return [];
322+
}
323+
return [this.autoAttachedFile];
283324
}
284325

285326
private get isScrolledToBottom() {
@@ -478,6 +519,10 @@ export default class Room extends Component<Signature> {
478519
this.roomResource.toggleViewCode(message);
479520
};
480521

522+
private excludeFile = (file: FileAPI.FileDef) => {
523+
this.filesToExclude = [...this.filesToExclude, file];
524+
};
525+
481526
private doMatrixEventFlush = restartableTask(async () => {
482527
await this.matrixService.flushMembership;
483528
await this.matrixService.flushTimeline;
@@ -630,10 +675,13 @@ export default class Room extends Component<Signature> {
630675
.map((stackItem) => stackItem.card.id),
631676
};
632677
try {
678+
let files = await this.matrixService.uploadFiles(this.filesToAttach);
679+
633680
await this.matrixService.sendMessage(
634681
this.args.roomId,
635682
message,
636683
cards,
684+
files,
637685
clientGeneratedId,
638686
context,
639687
);
@@ -698,6 +746,14 @@ export default class Room extends Component<Signature> {
698746
skills: [card],
699747
});
700748
});
749+
750+
private doLoadFileAPI = restartableTask(async () => {
751+
let fileAPI = await this.loaderService.loader.import<typeof FileAPI>(
752+
'https://cardstack.com/base/file-api',
753+
);
754+
755+
this.fileAPI = fileAPI;
756+
});
701757
}
702758

703759
declare module '@glint/environment-ember-loose/registry' {

0 commit comments

Comments
 (0)