Skip to content

Commit

Permalink
Save command-result card to workspace (#1647)
Browse files Browse the repository at this point in the history
* can copy to workspace

* update realm url selection

* handling null message content

* style results isolated template

* fix arrow in copy button

* update realm url selection

* resolve bot error

* revert changes no longer needed

* fix typo

* clean up

* updating tests

* fix messages missing content

* fix getting attached resources

* update tests

* use CommandObjectField type

* updating realmURL fallback behavior

* update stackIndex logic

* update comment

* add test for when no cards are open in stack

* fix cropped search icon

* update template for CommandObjectField

* fix lint error
  • Loading branch information
burieberry authored Oct 8, 2024
1 parent e1bc789 commit 9db69e0
Show file tree
Hide file tree
Showing 20 changed files with 604 additions and 177 deletions.
314 changes: 251 additions & 63 deletions packages/base/command-result.gts

Large diffs are not rendered by default.

41 changes: 29 additions & 12 deletions packages/base/command.gts
Original file line number Diff line number Diff line change
@@ -1,34 +1,51 @@
import {
CardDef,
Component,
FieldDef,
StringField,
contains,
field,
primitive,
queryableValue,
} from './card-api';
import { CommandResult } from './command-result';

type JSONValue = string | number | boolean | null | JSONObject | [JSONValue];
export type CommandStatus = 'applied' | 'ready';

type JSONObject = { [x: string]: JSONValue };
class CommandStatusField extends StringField {
static [primitive]: CommandStatus;
}

type CommandObject = JSONObject;
class CommandObjectFieldTemplate extends Component<typeof CommandObjectField> {
<template>
<pre>{{this.stringValue}}</pre>
<style scoped>
pre {
margin: 0;
white-space: pre-wrap;
}
</style>
</template>

class CommandObjectField extends FieldDef {
static [primitive]: CommandObject;
get stringValue() {
return JSON.stringify(this.args.model, null, 2);
}
}

export type CommandStatus = 'applied' | 'ready';

class CommandStatusField extends FieldDef {
static [primitive]: CommandStatus;
export class CommandObjectField extends FieldDef {
static [primitive]: Record<string, any>;
static [queryableValue](value: Record<string, any> | undefined) {
return Boolean(value) && typeof value === 'object'
? JSON.stringify(value)
: undefined;
}
static edit = CommandObjectFieldTemplate;
static embedded = CommandObjectFieldTemplate;
}

export class CommandField extends CardDef {
export class CommandCard extends CardDef {
@field toolCallId = contains(StringField);
@field name = contains(StringField);
@field payload = contains(CommandObjectField); //arguments of toolCall. Its not called arguments due to lint
@field eventId = contains(StringField);
@field status = contains(CommandStatusField);
@field result = contains(CommandResult);
}
7 changes: 1 addition & 6 deletions packages/boxel-ui/addon/raw-icons/arrow-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/boxel-ui/addon/src/icons.gts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ import IconPencilNotCrossedOut from './icons/icon-pencil-not-crossed-out.gts';
import IconPlus from './icons/icon-plus.gts';
import IconPlusCircle from './icons/icon-plus-circle.gts';
import IconSearch from './icons/icon-search.gts';
import IconSearchThick from './icons/icon-search-thick.gts';
import IconTrash from './icons/icon-trash.gts';
import IconTurnDownRight from './icons/icon-turn-down-right.gts';
import IconX from './icons/icon-x.gts';
import ImagePlaceholder from './icons/image-placeholder.gts';
import LoadingIndicator from './icons/loading-indicator.gts';
import Lock from './icons/lock.gts';
import Profile from './icons/profile.gts';
import Search from './icons/search.gts';
import Send from './icons/send.gts';
import Sparkle from './icons/sparkle.gts';
import SuccessBordered from './icons/success-bordered.gts';
Expand Down Expand Up @@ -87,14 +87,14 @@ export const ALL_ICON_COMPONENTS = [
IconPlus,
IconPlusCircle,
IconSearch,
IconSearchThick,
IconTrash,
IconTurnDownRight,
IconX,
ImagePlaceholder,
LoadingIndicator,
Lock,
Profile,
Search,
Send,
Sparkle,
SuccessBordered,
Expand Down Expand Up @@ -138,14 +138,14 @@ export {
IconPlus,
IconPlusCircle,
IconSearch,
IconSearchThick,
IconTrash,
IconTurnDownRight,
IconX,
ImagePlaceholder,
LoadingIndicator,
Lock,
Profile,
Search,
Send,
Sparkle,
SuccessBordered,
Expand Down
19 changes: 8 additions & 11 deletions packages/boxel-ui/addon/src/icons/arrow-left.gts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ const IconComponent: TemplateOnlyComponent<Signature> = <template>
height='17.536'
viewBox='-16.5 -17.536 16.5 17.536'
...attributes
><g data-name='Group 168' style='transform:rotate(180deg)'><path
d='M5 12h14'
data-name='Path 47'
style='fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.5px'
transform='translate(-3.75 -3.232)'
/><path
d='m12 5 7 7-7 7'
data-name='Path 48'
style='fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.5px'
transform='translate(-3.75 -3.232)'
/></g></svg>
><g
fill='none'
stroke='var(--icon-color, #000)'
stroke-linecap='round'
stroke-linejoin='round'
stroke-width='var(--stroke-width, 2.5)'
style='transform:rotate(180deg)'
><path d='M1.25 8.768h14M8.25 1.768l7 7-7 7' /></g></svg>
</template>;

// @ts-expect-error this is the only way to set a name on a Template Only Component currently
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ const IconComponent: TemplateOnlyComponent<Signature> = <template>
stroke='var(--icon-color, #000)'
stroke-linecap='round'
stroke-linejoin='round'
stroke-width='1.5'
stroke-width='2'
transform='translate(.75 .75)'
><circle cx='6.222' cy='6.222' r='6.222' /><path
><circle cx='6.222' cy='6.222' r='6' /><path
d='m14 14-3.383-3.383'
/></g></svg>
</template>;

// @ts-expect-error this is the only way to set a name on a Template Only Component currently
IconComponent.name = 'Search';
IconComponent.name = 'IconSearchThick';
export default IconComponent;
17 changes: 12 additions & 5 deletions packages/host/app/components/matrix/room-message.gts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { MatrixEvent } from 'matrix-js-sdk';
import { Button } from '@cardstack/boxel-ui/components';
import { Copy as CopyIcon } from '@cardstack/boxel-ui/icons';

import { markdownToHtml } from '@cardstack/runtime-common';
import { isCardInstance, markdownToHtml } from '@cardstack/runtime-common';

import { Message } from '@cardstack/host/lib/matrix-classes/message';
import monacoModifier from '@cardstack/host/modifiers/monaco';
Expand All @@ -30,7 +30,7 @@ import { type MonacoSDK } from '@cardstack/host/services/monaco-service';
import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';

import { type CardDef } from 'https://cardstack.com/base/card-api';
import { type CommandField } from 'https://cardstack.com/base/command';
import { type CommandCard } from 'https://cardstack.com/base/command';

import ApplyButton from '../ai-assistant/apply-button';
import { type ApplyButtonState } from '../ai-assistant/apply-button';
Expand Down Expand Up @@ -113,6 +113,7 @@ export default class RoomMessage extends Component<Signature> {
}

@tracked streamingTimeout = false;
@tracked commandResult: any;

checkStreamingTimeout = task(async () => {
if (!this.isFromAssistant || !this.args.isStreaming) {
Expand All @@ -136,7 +137,11 @@ export default class RoomMessage extends Component<Signature> {
}

get getComponent() {
return this.commandService.getCommandResultComponent(this.args.message);
let { commandResult } = this.args.message;
if (!commandResult || !isCardInstance(commandResult)) {
return undefined;
}
return commandResult.constructor.getComponent(commandResult);
}

<template>
Expand Down Expand Up @@ -235,7 +240,9 @@ export default class RoomMessage extends Component<Signature> {
</div>
{{/if}}
{{#let this.getComponent as |Component|}}
<Component @format='embedded' />
{{#if Component}}
<Component @format='embedded' />
{{/if}}
{{/let}}
{{/if}}
</AiAssistantMessage>
Expand Down Expand Up @@ -400,7 +407,7 @@ export default class RoomMessage extends Component<Signature> {
return this.matrixService.failedCommandState.get(this.command.eventId);
}

run = task(async (command: CommandField, roomId: string) => {
run = task(async (command: CommandCard, roomId: string) => {
return this.commandService.run.unlinked().perform(command, roomId);
});

Expand Down
7 changes: 5 additions & 2 deletions packages/host/app/components/operator-mode/copy-button.gts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default class CopyButton extends Component<Signature> {
</span>
{{else}}
{{#if (eq this.state.direction 'left')}}
<ArrowLeft width='18px' height='18px' />
<ArrowLeft class='arrow-icon' width='18px' height='18px' />
{{/if}}
<span class='copy-text'>
Copy
Expand All @@ -79,7 +79,7 @@ export default class CopyButton extends Component<Signature> {
{{/if}}
</span>
{{#if (eq this.state.direction 'right')}}
<ArrowRight width='18px' height='18px' />
<ArrowRight class='arrow-icon' width='18px' height='18px' />
{{/if}}
{{/if}}
</BoxelButton>
Expand All @@ -96,6 +96,9 @@ export default class CopyButton extends Component<Signature> {
.copy-text {
margin: 0 var(--boxel-sp-xxs);
}
.arrow-icon {
--icon-color: var(--boxel-light);
}
</style>
</template>

Expand Down
Loading

0 comments on commit 9db69e0

Please sign in to comment.