Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File Picker for Attaching Files #2126

Merged
merged 35 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bd7af13
Refactor directory component
FadhlanR Feb 7, 2025
f117f1e
Fix host test
FadhlanR Feb 7, 2025
b9cf6f6
Attach file modal
FadhlanR Feb 8, 2025
abf7eb1
Update button text
FadhlanR Feb 8, 2025
0e490f7
WIP
FadhlanR Feb 11, 2025
2253db2
Merge branch 'main' into cs-7948-file-picker
FadhlanR Feb 11, 2025
bc51b97
Attach files to the input section
FadhlanR Feb 11, 2025
8eca7d1
lint fix
FadhlanR Feb 11, 2025
7259e90
Ability to upload files and attach to the send message payload
jurgenwerk Feb 11, 2025
f450589
Introduce file pill
jurgenwerk Feb 11, 2025
38975cc
Show auto attached file in the attachment picker
jurgenwerk Feb 11, 2025
ed59b98
fix host test
FadhlanR Feb 11, 2025
3db6ae3
Fix matrix tests
FadhlanR Feb 11, 2025
53c0dbc
Merge branch 'cs-7853-add-ability-to-attach-code-you-are-looking-at-i…
FadhlanR Feb 11, 2025
e27c868
Remove CodeFile from boxel-ui
FadhlanR Feb 11, 2025
7bf4aad
rename BoxelPill to Pill
FadhlanR Feb 11, 2025
93b8029
Update icon
FadhlanR Feb 11, 2025
e96f9b3
type -> contentType
jurgenwerk Feb 11, 2025
938a40c
Update usage file
jurgenwerk Feb 11, 2025
ac7ec2f
Add a test
jurgenwerk Feb 11, 2025
8bd57b9
Fix matrix tests
FadhlanR Feb 11, 2025
d27d648
Dont auto attach a card in code mode
jurgenwerk Feb 11, 2025
97c3106
Mock uploading
jurgenwerk Feb 11, 2025
013cdf6
lint fix
FadhlanR Feb 12, 2025
5bd5381
Merge branch 'cs-7853-add-ability-to-attach-code-you-are-looking-at-i…
FadhlanR Feb 12, 2025
9eea54e
Add matrix tests
FadhlanR Feb 12, 2025
c6c4c94
Update choose and remove file
FadhlanR Feb 12, 2025
fb4b2cd
lint fix
FadhlanR Feb 12, 2025
ecc6b85
Fix host test
FadhlanR Feb 12, 2025
9212e84
Handle auto attached file removal
FadhlanR Feb 12, 2025
f16efc4
Address feedbacks
FadhlanR Feb 13, 2025
d448d79
Remove submode condition
FadhlanR Feb 13, 2025
d73449b
Not used anymore
jurgenwerk Feb 13, 2025
afbfd8a
Rename for clarity
jurgenwerk Feb 13, 2025
f45e1fa
Merge branch 'main' into cs-7948-file-picker
jurgenwerk Feb 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/pr-boxel-host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
AWS_REGION: us-east-1
AWS_CLOUDFRONT_DISTRIBUTION: EU4RGLH4EOCHJ
ENABLE_PLAYGROUND: true
ENABLE_ATTACHING_FILES: true
with:
package: boxel-host
environment: staging
Expand Down Expand Up @@ -96,6 +97,7 @@ jobs:
AWS_REGION: us-east-1
AWS_CLOUDFRONT_DISTRIBUTION: E2PZR9CIAW093B
ENABLE_PLAYGROUND: true
ENABLE_ATTACHING_FILES: true
with:
package: boxel-host
environment: production
12 changes: 12 additions & 0 deletions packages/base/file-api.gts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,15 @@ export class FileDef extends BaseDef {
};
}
}

export function createFileDef({
url,
sourceUrl,
name,
}: {
url: string;
sourceUrl: string;
name: string;
}) {
return new FileDef({ url, sourceUrl, name });
}
9 changes: 9 additions & 0 deletions packages/boxel-ui/addon/raw-icons/code-file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/boxel-ui/addon/src/icons.gts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Card from './icons/card.gts';
import CardbotLg from './icons/cardbot-lg.gts';
import CaretDown from './icons/caret-down.gts';
import CheckMark from './icons/check-mark.gts';
import CodeFile from './icons/code-file.gts';
import Copy from './icons/copy.gts';
import DiagonalArrowLeftUp from './icons/diagonal-arrow-left-up.gts';
import Download from './icons/download.gts';
Expand Down Expand Up @@ -71,6 +72,7 @@ export const ALL_ICON_COMPONENTS = [
CardbotLg,
CaretDown,
CheckMark,
CodeFile,
Copy,
DiagonalArrowLeftUp,
Download,
Expand Down Expand Up @@ -132,6 +134,7 @@ export {
CardbotLg,
CaretDown,
CheckMark,
CodeFile,
Copy,
DiagonalArrowLeftUp,
Download,
Expand Down
23 changes: 23 additions & 0 deletions packages/boxel-ui/addon/src/icons/code-file.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This file is auto-generated by 'pnpm rebuild:icons'
import type { TemplateOnlyComponent } from '@ember/component/template-only';

import type { Signature } from './types.ts';

const IconComponent: TemplateOnlyComponent<Signature> = <template>
<svg
xmlns='http://www.w3.org/2000/svg'
fill='none'
transform='scale(-1 1)'
viewBox='0 0 24 24'
...attributes
><path
fill='var(--icon-color, #000000)'
fill-rule='evenodd'
d='M9.293 1.293A1 1 0 0 1 10 1h8a3 3 0 0 1 3 3v5a1 1 0 1 1-2 0V4a1 1 0 0 0-1-1h-7v5a1 1 0 0 1-1 1H5v11a1 1 0 0 0 1 1h3a1 1 0 1 1 0 2H6a3 3 0 0 1-3-3V8a1 1 0 0 1 .293-.707l6-6ZM6.414 7H9V4.414L6.414 7Zm12.293 5.293 4 4a1 1 0 0 1 0 1.414l-4 4a1 1 0 0 1-1.414-1.414L20.586 17l-3.293-3.293a1 1 0 0 1 1.414-1.414Zm-4 1.414a1 1 0 0 0-1.414-1.414l-4 4a1 1 0 0 0 0 1.414l4 4a1 1 0 0 0 1.414-1.414L11.414 17l3.293-3.293Z'
clip-rule='evenodd'
/></svg>
</template>;

// @ts-expect-error this is the only way to set a name on a Template Only Component currently
IconComponent.name = 'CodeFile';
export default IconComponent;
152 changes: 108 additions & 44 deletions packages/host/app/components/ai-assistant/attachment-picker/index.gts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,29 @@ import { restartableTask } from 'ember-concurrency';

import { TrackedSet } from 'tracked-built-ins';

import { AddButton, Tooltip, Pill } from '@cardstack/boxel-ui/components';
import { and, cn, gt, not } from '@cardstack/boxel-ui/helpers';
import {
AddButton,
Tooltip,
Pill as BoxelPill,
} from '@cardstack/boxel-ui/components';
import { and, cn, eq, gt, not } from '@cardstack/boxel-ui/helpers';

import {
chooseCard,
baseCardRef,
isCardInstance,
chooseFile,
} from '@cardstack/runtime-common';

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

import ENV from '@cardstack/host/config/environment';

import { type CardDef } from 'https://cardstack.com/base/card-api';
import { type FileDef } from 'https://cardstack.com/base/file-api';

import { Submode } from '../../submode-switcher';

interface Signature {
Element: HTMLDivElement;
Args: {
Expand All @@ -30,40 +39,44 @@ interface Signature {
filesToAttach: FileDef[] | undefined;
chooseCard: (card: CardDef) => void;
removeCard: (card: CardDef) => void;
chooseFile: (file: FileDef) => void;
removeFile: (file: FileDef) => void;
submode: Submode;
maxNumberOfItemsToAttach?: number;
};
}

const isAttachingFilesEnabled = ENV.featureFlags?.ENABLE_ATTACHING_FILES;
const MAX_ITEMS_TO_DISPLAY = 4;

export default class AiAssistantAttachmentPicker extends Component<Signature> {
<template>
<div class='item-picker'>
{{#each this.itemsToDisplay as |item|}}
{{#if (this.isCard item)}}
{{#if (this.isAutoAttachedCard item)}}
<Tooltip @placement='top'>
<:trigger>
<CardPill
@card={{item}}
@isAutoAttachedCard={{true}}
@removeCard={{@removeCard}}
/>
</:trigger>

<:content>
{{#if (this.isAutoAttachedCard item)}}
Topmost card is shared automatically
{{/if}}
</:content>
</Tooltip>
{{else}}
<CardPill
@card={{item}}
@isAutoAttachedCard={{false}}
@removeCard={{@removeCard}}
/>
{{/if}}
{{#if (this.isAutoAttached item)}}
<Tooltip @placement='top'>
<:trigger>
<Pill
@item={{item}}
@isAutoAttached={{true}}
@remove={{this.removeItem}}
/>
</:trigger>

<:content>
{{#if (this.isAutoAttached item)}}
Topmost
{{if (this.isCard item) 'card' 'file'}}
is shared automatically
{{/if}}
</:content>
</Tooltip>
{{else}}
<Pill
@item={{item}}
@isAutoAttached={{false}}
@remove={{this.removeItem}}
/>
{{/if}}
{{/each}}
{{#if
Expand All @@ -72,28 +85,44 @@ export default class AiAssistantAttachmentPicker extends Component<Signature> {
(not this.areAllItemsDisplayed)
)
}}
<Pill
<BoxelPill
@kind='button'
{{on 'click' this.toggleViewAllAttachedCards}}
data-test-view-all
>
View All ({{this.items.length}})
</Pill>
</BoxelPill>
{{/if}}
{{#if this.canDisplayAddButton}}
<AddButton
class={{cn 'attach-button' icon-only=this.itemsToDisplay.length}}
@variant='pill'
@iconWidth='14'
@iconHeight='14'
{{on 'click' this.chooseCard}}
@disabled={{this.doChooseCard.isRunning}}
data-test-choose-card-btn
>
<span class={{if this.itemsToDisplay.length 'boxel-sr-only'}}>
Add Card
</span>
</AddButton>
{{#if (and (eq @submode 'code') isAttachingFilesEnabled)}}
<AddButton
class={{cn 'attach-button' icon-only=this.itemsToDisplay.length}}
@variant='pill'
@iconWidth='14'
@iconHeight='14'
{{on 'click' this.chooseFile}}
@disabled={{this.doChooseFile.isRunning}}
data-test-choose-file-btn
>
<span class={{if this.itemsToDisplay.length 'boxel-sr-only'}}>
Attach File
</span>
</AddButton>
{{else}}
<AddButton
class={{cn 'attach-button' icon-only=this.itemsToDisplay.length}}
@variant='pill'
@iconWidth='14'
@iconHeight='14'
{{on 'click' this.chooseCard}}
@disabled={{this.doChooseCard.isRunning}}
data-test-choose-card-btn
>
<span class={{if this.itemsToDisplay.length 'boxel-sr-only'}}>
Add Card
</span>
</AddButton>
{{/if}}
{{/if}}
</div>
<style scoped>
Expand Down Expand Up @@ -136,17 +165,30 @@ export default class AiAssistantAttachmentPicker extends Component<Signature> {
this.areAllItemsDisplayed = !this.areAllItemsDisplayed;
}

isCard = (item: CardDef | FileDef): item is CardDef => {
private isCard = (item: CardDef | FileDef): item is CardDef => {
return isCardInstance(item);
};

isAutoAttachedCard = (card: CardDef) => {
private isAutoAttachedCard = (card: CardDef) => {
if (this.args.autoAttachedCards === undefined) {
return false;
}
return this.args.autoAttachedCards.has(card);
};

private isAutoAttachedFile = (file: FileDef) => {
if (this.args.autoAttachedFiles === undefined) {
return false;
}
return this.args.autoAttachedFiles.includes(file);
};

private isAutoAttached = (item: CardDef | FileDef) => {
return this.isCard(item)
? this.isAutoAttachedCard(item)
: this.isAutoAttachedFile(item);
};

private get items() {
let cards = this.args.cardsToAttach ?? [];
let files = this.args.filesToAttach ?? [];
Expand Down Expand Up @@ -189,4 +231,26 @@ export default class AiAssistantAttachmentPicker extends Component<Signature> {
});
return chosenCard;
});

@action
private async chooseFile() {
let file = await this.doChooseFile.perform();
if (file) {
this.args.chooseFile(file);
}
}

private doChooseFile = restartableTask(async () => {
let chosenFile: FileDef | undefined = await chooseFile();
return chosenFile;
});

@action
private removeItem(item: CardDef | FileDef) {
if (isCardInstance(item)) {
this.args.removeCard(item);
} else {
this.args.removeFile(item);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ export default class AiAssistantCardPickerUsage extends Component {
this.cards.splice(index, 1);
}

@action chooseFile(file: FileDef) {
if (!this.filesToAttach?.find((f) => f.sourceUrl === file.sourceUrl)) {
this.filesToAttach.push(file);
}
}

@action removeFile(file: FileDef) {
let index = this.filesToAttach.findIndex(
(f) => f.sourceUrl === file.sourceUrl,
);
this.filesToAttach.splice(index, 1);
}

<template>
<FreestyleUsage @name='AiAssistant::AttachmentPicker'>
<:description>
Expand All @@ -47,6 +60,9 @@ export default class AiAssistantCardPickerUsage extends Component {
@cardsToAttach={{this.cards}}
@chooseCard={{this.chooseCard}}
@removeCard={{this.removeCard}}
@chooseFile={{this.chooseFile}}
@removeFile={{this.removeFile}}
@submode={{'interact'}}
@maxNumberOfItemsToAttach={{this.maxNumberOfCards}}
@autoAttachedFiles={{this.autoAttachedFiles}}
@filesToAttach={{this.filesToAttach}}
Expand Down
4 changes: 2 additions & 2 deletions packages/host/app/components/ai-assistant/message/index.gts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Button } from '@cardstack/boxel-ui/components';
import { and, cn } from '@cardstack/boxel-ui/helpers';
import { FailureBordered } from '@cardstack/boxel-ui/icons';

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

import type CardService from '@cardstack/host/services/card-service';

Expand Down Expand Up @@ -210,7 +210,7 @@ export default class AiAssistantMessage extends Component<Signature> {
{{#if @resources.cards.length}}
<div class='cards' data-test-message-cards>
{{#each @resources.cards as |card|}}
<CardPill @card={{card}} />
<Pill @item={{card}} />
{{/each}}
</div>
{{/if}}
Expand Down
Loading
Loading