Skip to content

Add "create card instance" command #2180

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/base/command.gts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class WriteTextFileInput extends CardDef {
}

export class CreateInstanceInput extends CardDef {
@field parent = contains(JsonField);
@field module = contains(CodeRefField);
@field realm = contains(StringField);
}
Expand Down
44 changes: 44 additions & 0 deletions packages/host/app/commands/create-card-instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { type LooseSingleCardDocument } from '@cardstack/runtime-common';

import { getCard } from '@cardstack/host/resources/card-resource';

import type { CardDef } from 'https://cardstack.com/base/card-api';
import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

export default class CreateCardInstanceCommand extends HostBaseCommand<
typeof BaseCommandModule.CreateInstanceInput,
typeof CardDef | undefined
> {
description = `Create a new card instance given a card ref and realm.`;

async getInputType() {
let commandModule = await this.loadCommandModule();
const { CreateInstanceInput } = commandModule;
return CreateInstanceInput;
}

protected async run(
input: BaseCommandModule.CreateInstanceInput,
): Promise<CardDef | undefined> {
if (!input.parent || !input.module || !input.realm) {
throw new Error(
"Create instance command can't run because it doesn't have all the fields in arguments returned by open ai",
);
}
let doc: LooseSingleCardDocument = {
data: {
meta: {
adoptsFrom: input.module,
realmURL: input.realm,
},
},
};
let cardResource = getCard(input.parent, () => doc, {
isAutoSave: () => true,
});
await cardResource.loaded; // this await should not be necessary when card-resource is refactored
return cardResource.card;
}
}
5 changes: 5 additions & 0 deletions packages/host/app/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as AddFieldToCardDefinitionCommandModule from './add-field-to-card-defi
import * as AddSkillsToRoomCommandModule from './add-skills-to-room';
import * as CopyCardCommandModule from './copy-card';
import * as CreateAIAssistantRoomCommandModule from './create-ai-assistant-room';
import * as CreateCardInstanceCommandModule from './create-card-instance';
import * as OpenAiAssistantRoomCommandModule from './open-ai-assistant-room';
import * as PatchCardCommandModule from './patch-card';
import * as ReloadCardCommandModule from './reload-card';
Expand All @@ -29,6 +30,10 @@ export function shimHostCommands(virtualNetwork: VirtualNetwork) {
'@cardstack/boxel-host/commands/copy-card',
CopyCardCommandModule,
);
virtualNetwork.shimModule(
'@cardstack/boxel-host/commands/create-card-instance',
CreateCardInstanceCommandModule,
);
virtualNetwork.shimModule(
'@cardstack/boxel-host/commands/create-ai-assistant-room',
CreateAIAssistantRoomCommandModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { action } from '@ember/object';
import type Owner from '@ember/owner';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import Folder from '@cardstack/boxel-icons/folder';
import { task } from 'ember-concurrency';
Expand All @@ -31,16 +30,17 @@ import {
cardTypeIcon,
chooseCard,
type Query,
type LooseSingleCardDocument,
type ResolvedCodeRef,
} from '@cardstack/runtime-common';

import { getCard } from '@cardstack/host/resources/card-resource';
import CreateCardInstanceCommand from '@cardstack/host/commands/create-card-instance';

import { getCard } from '@cardstack/host/resources/card-resource';
import { getCodeRef, type CardType } from '@cardstack/host/resources/card-type';
import { ModuleContentsResource } from '@cardstack/host/resources/module-contents';

import type CardService from '@cardstack/host/services/card-service';
import type CommandService from '@cardstack/host/services/command-service';
import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';
import type RealmService from '@cardstack/host/services/realm';
import type RealmServerService from '@cardstack/host/services/realm-server';
Expand Down Expand Up @@ -375,11 +375,11 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
</template>

@service private declare cardService: CardService;
@service private declare commandService: CommandService;
@service private declare operatorModeStateService: OperatorModeStateService;
@service private declare realm: RealmService;
@service private declare realmServer: RealmServerService;
@service declare recentFilesService: RecentFilesService;
@tracked newCardJSON: LooseSingleCardDocument | undefined;
private playgroundSelections: Record<
string, // moduleId
{ cardId: string; format: Format }
Expand Down Expand Up @@ -432,8 +432,7 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {

private cardResource = getCard(
this,
() =>
this.newCardJSON ?? this.playgroundSelections[this.args.moduleId]?.cardId,
() => this.playgroundSelections[this.args.moduleId]?.cardId,
{ isAutoSave: () => true },
);

Expand Down Expand Up @@ -480,7 +479,6 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
}

private persistSelections = (cardId: string, format = this.format) => {
this.newCardJSON = undefined;
this.playgroundSelections[this.args.moduleId] = { cardId, format };
window.localStorage.setItem(
PlaygroundSelections,
Expand Down Expand Up @@ -511,20 +509,16 @@ class PlaygroundPanelContent extends Component<PlaygroundContentSignature> {
}
});

// TODO: convert this to @action once we no longer need to await below
private createNew = task(async () => {
this.newCardJSON = {
data: {
meta: {
adoptsFrom: this.args.codeRef,
realmURL: this.operatorModeStateService.realmURL.href,
},
},
};
await this.cardResource.loaded; // TODO: remove await when card-resource is refactored
if (this.card) {
this.recentFilesService.addRecentFileUrl(`${this.card.id}.json`);
this.persistSelections(this.card.id, 'edit'); // open new instance in playground in edit format
let { commandContext } = this.commandService;
let card = await new CreateCardInstanceCommand(commandContext).execute({
parent: this,
module: this.args.codeRef,
realm: this.operatorModeStateService.realmURL.href,
});
if (card) {
this.recentFilesService.addRecentFileUrl(`${card.id}.json`);
this.persistSelections(card.id, 'edit'); // open new instance in playground in edit format
}
});

Expand Down