diff --git a/docs/spec.md b/docs/spec.md index 8ed143d713c..b6fb02db660 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -64,7 +64,6 @@ Each spec type has specific characteristics and use cases: **Example Use Cases**: - `SocialMediaLink` - Composite field for social platform data -- `MaybeBase64Field` - String field with base64 encoding capabilities - `TextAreaField` - Multi-line text input field - `GeoPointField` - Coordinate field for maps diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index 2fab222c7ab..8de7404ab5f 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -1,8 +1,10 @@ import Modifier from 'ember-modifier'; import GlimmerComponent from '@glimmer/component'; +import { concat } from '@ember/helper'; import { isEqual } from 'lodash'; import { WatchedArray } from './watched-array'; import { BoxelInput, CopyButton } from '@cardstack/boxel-ui/components'; +import { copyCardURLToClipboard } from '@cardstack/boxel-ui/helpers'; import { type MenuItemOptions, not } from '@cardstack/boxel-ui/helpers'; import { getBoxComponent, @@ -16,6 +18,7 @@ import { getLinksToManyComponent } from './links-to-many-component'; import { assertIsSerializerName, baseRef, + byteStreamToUint8Array, CardContextName, CardError, CodeRef, @@ -31,6 +34,7 @@ import { getSerializer, humanReadable, identifyCard, + inferContentType, isBaseInstance, isCardError, isCardInstance as _isCardInstance, @@ -104,6 +108,7 @@ import FieldDefEditTemplate from './default-templates/field-edit'; import MarkdownTemplate from './default-templates/markdown'; import CaptionsIcon from '@cardstack/boxel-icons/captions'; import LetterCaseIcon from '@cardstack/boxel-icons/letter-case'; +import HashIcon from '@cardstack/boxel-icons/hash'; import MarkdownIcon from '@cardstack/boxel-icons/align-box-left-middle'; import RectangleEllipsisIcon from '@cardstack/boxel-icons/rectangle-ellipsis'; import TextAreaIcon from '@cardstack/boxel-icons/align-left'; @@ -111,9 +116,19 @@ import ThemeIcon from '@cardstack/boxel-icons/palette'; import ImportIcon from '@cardstack/boxel-icons/import'; import FilePencilIcon from '@cardstack/boxel-icons/file-pencil'; import WandIcon from '@cardstack/boxel-icons/wand'; +import FileIcon from '@cardstack/boxel-icons/file'; +import ArrowLeft from '@cardstack/boxel-icons/arrow-left'; +import LinkIcon from '@cardstack/boxel-icons/link'; +import Eye from '@cardstack/boxel-icons/eye'; +import CodeIcon from '@cardstack/boxel-icons/code'; // normalizeEnumOptions used by enum moved to packages/base/enum.gts import PatchThemeCommand from '@cardstack/boxel-host/commands/patch-theme'; import CopyAndEditCommand from '@cardstack/boxel-host/commands/copy-and-edit'; +import CopyFileToRealmCommand from '@cardstack/boxel-host/commands/copy-file-to-realm'; +import OpenInInteractModeCommand from '@cardstack/boxel-host/commands/open-in-interact-mode'; +import ShowFileCommand from '@cardstack/boxel-host/commands/show-file'; +import SwitchSubmodeCommand from '@cardstack/boxel-host/commands/switch-submode'; +import { md5 } from 'super-fast-md5'; import { callSerializeHook, @@ -154,12 +169,13 @@ import { type NotLoadedValue, } from './field-support'; import { type GetMenuItemParams, getDefaultCardMenuItems } from './menu-items'; +import { TextInputValidator } from './text-input-validator'; import { LinkableDocument, SingleFileMetaDocument, } from '@cardstack/runtime-common/document-types'; import type { FileMetaResource } from '@cardstack/runtime-common'; -import type { FileDef } from './file-api'; +import { NumberSerializer } from '@cardstack/runtime-common'; export const BULK_GENERATED_ITEM_COUNT = 3; @@ -2163,7 +2179,10 @@ export class BaseDef { if (isNotLoadedValue(rawValue)) { let normalizedId = rawValue.reference; if (value[relativeTo]) { - normalizedId = resolveCardReference(normalizedId, value[relativeTo]); + normalizedId = resolveCardReference( + normalizedId, + value[relativeTo], + ); } return [fieldName, { id: makeAbsoluteURL(rawValue.reference) }]; } @@ -2333,21 +2352,486 @@ export class StringField extends FieldDef { }; } -// TODO: This is a simple workaround until the thumbnailURL is converted into an actual image field -export class MaybeBase64Field extends StringField { - static embedded = class Embedded extends Component { - get isBase64() { - return this.args.model?.startsWith('data:'); - } +export function deserializeForUI(value: string | number | null): number | null { + const validationError = NumberSerializer.validate(value); + if (validationError) { + return null; + } + + return NumberSerializer.deserializeSync(value); +} + +export function serializeForUI(val: number | null): string | undefined { + let serialized = NumberSerializer.serialize(val); + if (serialized != null) { + return String(serialized); + } + return undefined; +} + +class NumberFieldView extends Component { + +} + +export class NumberField extends FieldDef { + static displayName = 'Number'; + static icon = HashIcon; + static [primitive]: number; + static [fieldSerializer] = 'number'; + static [useIndexBasedKey]: never; + static embedded = NumberFieldView; + static atom = NumberFieldView; + + static edit = class Edit extends Component { + + textInputValidator: TextInputValidator = new TextInputValidator( + () => this.args.model, + (inputVal) => this.args.set(inputVal), + deserializeForUI, + serializeForUI, + NumberSerializer.validate, + ); }; - static atom = MaybeBase64Field.embedded; +} + +class FileView extends Component { + +} + +class FileEdit extends Component { + +} + +export type SerializedFile = { + sourceUrl: string; + url: string; + name: string; + contentType: string; + contentHash?: string; + contentSize?: number; +} & Extra; + +export type ByteStream = ReadableStream | Uint8Array; + +export class FileContentMismatchError extends Error { + name = 'FileContentMismatchError'; +} + +export interface SerializedFileDef { + url?: string; + sourceUrl: string; + name?: string; + contentHash?: string; + contentSize?: number; + contentType?: string; + content?: string; + error?: string; +} + +export class FileDef extends BaseDef { + static displayName = 'File'; + static isFileDef = true; + static icon = FileIcon; + [isSavedInstance] = true; + + static assignInitialFieldValue( + instance: BaseDef, + fieldName: string, + value: any, + ) { + if (fieldName === 'id') { + let deserialized = getDataBucket(instance); + deserialized.set('id', value); + } else { + super.assignInitialFieldValue(instance, fieldName, value); + } + } + + @field id = contains(ReadOnlyField); + @field sourceUrl = contains(StringField); + @field url = contains(StringField); + @field name = contains(StringField); + @field contentType = contains(StringField); + @field contentHash = contains(StringField); + @field contentSize = contains(NumberField); + + static embedded: BaseDefComponent = FileView; + static fitted: BaseDefComponent = FileView; + static isolated: BaseDefComponent = FileView; + static atom: BaseDefComponent = FileView; + static edit: BaseDefComponent = FileEdit; + + static async extractAttributes( + url: string, + getStream: () => Promise, + options: { contentHash?: string; contentSize?: number } = {}, + ): Promise { + let parsed = new URL(url); + let name = decodeURIComponent( + parsed.pathname.split('/').pop() ?? parsed.pathname, + ); + let contentType = inferContentType(name); + let contentHash: string | undefined = options.contentHash; + let contentSize: number | undefined = options.contentSize; + if (!contentHash || contentSize === undefined) { + let bytes = await byteStreamToUint8Array(await getStream()); + if (!contentHash) { + try { + contentHash = md5(bytes); + } catch { + contentHash = md5(new TextDecoder().decode(bytes)); + } + } + if (contentSize === undefined) { + contentSize = bytes.byteLength; + } + } + + return { + sourceUrl: url, + url, + name, + contentType, + contentHash, + contentSize, + }; + } + + serialize() { + return { + sourceUrl: this.sourceUrl, + url: this.url, + name: this.name, + contentType: this.contentType, + contentHash: this.contentHash, + contentSize: this.contentSize, + }; + } + + [getMenuItems](params: GetMenuItemParams): MenuItemOptions[] { + return getDefaultFileMenuItems(this, params); + } +} + +export function createFileDef({ + url, + sourceUrl, + name, + contentType, + contentHash, + contentSize, +}: SerializedFileDef) { + return new FileDef({ + url, + sourceUrl, + name, + contentType, + contentHash, + contentSize, + }); +} + +export function getDefaultFileMenuItems( + fileDefInstance: FileDef, + params: GetMenuItemParams, +): MenuItemOptions[] { + let fileDefInstanceId = fileDefInstance.id as unknown as string; + let menuItems: MenuItemOptions[] = []; + if ( + ['interact', 'code-mode-preview', 'code-mode-playground'].includes( + params.menuContext, + ) + ) { + menuItems.push({ + label: 'Copy File URL', + action: () => copyCardURLToClipboard(fileDefInstanceId), + icon: LinkIcon, + disabled: !fileDefInstanceId, + }); + } + if (params.menuContext === 'interact') { + if (fileDefInstanceId && params.canEdit) { + // TODO: add menu item to delete the file + } + } + if ( + params.menuContext === 'ai-assistant' && + params.menuContextParams.canEditActiveRealm + ) { + menuItems.push({ + label: 'Copy to Workspace', + action: async () => { + const { newFileUrl } = await new CopyFileToRealmCommand( + params.commandContext, + ).execute({ + sourceFileUrl: fileDefInstance.sourceUrl, + targetRealm: params.menuContextParams.activeRealmURL, + }); + + await new ShowFileCommand(params.commandContext).execute({ + fileUrl: newFileUrl, + }); + }, + icon: ArrowLeft, + }); + } + if ( + ['code-mode-preview', 'code-mode-playground'].includes(params.menuContext) + ) { + menuItems.push({ + label: 'Open in Interact Mode', + action: () => { + new OpenInInteractModeCommand(params.commandContext).execute({ + cardId: fileDefInstanceId, + format: params.format === 'edit' ? 'edit' : 'isolated', + }); + }, + icon: Eye, + }); + } + if (params.menuContext === 'code-mode-playground') { + menuItems.push({ + label: 'Open in Code Mode', + action: async () => { + await new SwitchSubmodeCommand(params.commandContext).execute({ + submode: 'code', + codePath: fileDefInstanceId + ? new URL(fileDefInstanceId).href + : undefined, + }); + }, + icon: CodeIcon, + }); + } + return menuItems; +} + +// ImageDef +class Isolated extends Component { + +} + +class Atom extends Component { + +} + +class Embedded extends Component { + +} + +class Fitted extends Component { + get backgroundImageStyle() { + if (this.args.model.url) { + return `background-image: url(${this.args.model.url});`; + } + return undefined; + } + + +} + +export class ImageDef extends FileDef { + static displayName = 'Image'; + static acceptTypes = 'image/*'; + + @field width = contains(NumberField); + @field height = contains(NumberField); + + static isolated: BaseDefComponent = Isolated; + static embedded: BaseDefComponent = Embedded; + static atom: BaseDefComponent = Atom; + static fitted: BaseDefComponent = Fitted; } export class TextAreaField extends StringField { @@ -2452,7 +2936,8 @@ export class CardInfoField extends FieldDef { static displayName = 'Card Info'; @field name = contains(StringField); @field summary = contains(StringField); - @field cardThumbnailURL = contains(MaybeBase64Field); + @field cardThumbnail = linksTo(() => ImageDef); + @field cardThumbnailURL = contains(StringField); @field theme = linksTo(() => Theme); @field notes = contains(MarkdownField); } @@ -2490,10 +2975,7 @@ export class CardDef extends BaseDef { return this.cardInfo.summary; }, }); - // TODO: this will probably be an image or image url field card when we have it - // UPDATE: we now have a Base64ImageField card. we can probably refactor this - // to use it directly now (or wait until a better image field comes along) - @field cardThumbnailURL = contains(MaybeBase64Field, { + @field cardThumbnailURL = contains(StringField, { computeVia: function (this: CardDef) { return this.cardInfo.cardThumbnailURL; }, @@ -2790,7 +3272,10 @@ function lazilyLoadLink( inflightLoads = new Map(); inflightLinkLoads.set(instance, inflightLoads); } - let reference = resolveCardReference(link, instance.id ?? instance[relativeTo]); + let reference = resolveCardReference( + link, + instance.id ?? instance[relativeTo], + ); let key = `${field.name}/${reference}`; let promise = inflightLoads.get(key); let store = getStore(instance); diff --git a/packages/base/card-serialization.ts b/packages/base/card-serialization.ts index a494d64b6f8..503347f196f 100644 --- a/packages/base/card-serialization.ts +++ b/packages/base/card-serialization.ts @@ -13,8 +13,7 @@ import type { Meta, RuntimeDependencyTrackingContext, } from '@cardstack/runtime-common'; -import type { BaseDef, BaseDefConstructor, CardDef } from './card-api'; -import type { FileDef } from './file-api'; +import type { BaseDef, BaseDefConstructor, CardDef, FileDef } from './card-api'; import type { ResourceID } from '@cardstack/runtime-common'; // --- Runtime Imports --- diff --git a/packages/base/default-templates/card-info.gts b/packages/base/default-templates/card-info.gts index 108797f08fb..50f1f19e59e 100644 --- a/packages/base/default-templates/card-info.gts +++ b/packages/base/default-templates/card-info.gts @@ -217,6 +217,14 @@ class CardInfoEditor extends GlimmerComponent { {{#if this.isThumbnailEditorVisible}}
+ + <@fields.cardInfo.cardThumbnail /> + { - -} - -class Edit extends Component { - -} - -export type SerializedFile = { - sourceUrl: string; - url: string; - name: string; - contentType: string; - contentHash?: string; - contentSize?: number; -} & Extra; - -export type ByteStream = ReadableStream | Uint8Array; - -// Throw this error from extractAttributes when the file content doesn't match this FileDef's -// expectations so the extractor can fall back to a superclass/base FileDef. -export class FileContentMismatchError extends Error { - name = 'FileContentMismatchError'; -} - -export class FileDef extends BaseDef { - static displayName = 'File'; - static isFileDef = true; - static icon = FileIcon; - [isSavedInstance] = true; - - static assignInitialFieldValue( - instance: BaseDef, - fieldName: string, - value: any, - ) { - if (fieldName === 'id') { - // Similar to CardDef, set 'id' directly in the deserialized cache - // to avoid triggering recomputes during instantiation - let deserialized = getDataBucket(instance); - deserialized.set('id', value); - } else { - super.assignInitialFieldValue(instance, fieldName, value); - } - } - - @field id = contains(ReadOnlyField); - @field sourceUrl = contains(StringField); - @field url = contains(StringField); - @field name = contains(StringField); - @field contentType = contains(StringField); - @field contentHash = contains(StringField); - @field contentSize = contains(NumberField); - - static embedded: BaseDefComponent = View; - static fitted: BaseDefComponent = View; - static isolated: BaseDefComponent = View; - static atom: BaseDefComponent = View; - static edit: BaseDefComponent = Edit; - - static async extractAttributes( - url: string, - getStream: () => Promise, - options: { contentHash?: string; contentSize?: number } = {}, - ): Promise { - let parsed = new URL(url); - let name = decodeURIComponent( - parsed.pathname.split('/').pop() ?? parsed.pathname, - ); - let contentType = inferContentType(name); - let contentHash: string | undefined = options.contentHash; - let contentSize: number | undefined = options.contentSize; - if (!contentHash || contentSize === undefined) { - let bytes = await byteStreamToUint8Array(await getStream()); - if (!contentHash) { - try { - contentHash = md5(bytes); - } catch { - contentHash = md5(new TextDecoder().decode(bytes)); - } - } - if (contentSize === undefined) { - contentSize = bytes.byteLength; - } - } - - return { - sourceUrl: url, - url, - name, - contentType, - contentHash, - contentSize, - }; - } - - serialize() { - return { - sourceUrl: this.sourceUrl, - url: this.url, - name: this.name, - contentType: this.contentType, - contentHash: this.contentHash, - contentSize: this.contentSize, - }; - } - - [getMenuItems](params: GetMenuItemParams): MenuItemOptions[] { - return getDefaultFileMenuItems(this, params); - } -} - -export interface SerializedFileDef { - url?: string; - sourceUrl: string; - name?: string; - contentHash?: string; - contentSize?: number; - contentType?: string; - content?: string; - error?: string; -} - -export function createFileDef({ - url, - sourceUrl, - name, - contentType, - contentHash, - contentSize, -}: SerializedFileDef) { - return new FileDef({ url, sourceUrl, name, contentType, contentHash, contentSize }); -} - -export function getDefaultFileMenuItems( - fileDefInstance: FileDef, - params: GetMenuItemParams, -): MenuItemOptions[] { - let fileDefInstanceId = fileDefInstance.id as unknown as string; - let menuItems: MenuItemOptions[] = []; - if ( - ['interact', 'code-mode-preview', 'code-mode-playground'].includes( - params.menuContext, - ) - ) { - menuItems.push({ - label: 'Copy File URL', - action: () => copyCardURLToClipboard(fileDefInstanceId), - icon: LinkIcon, - disabled: !fileDefInstanceId, - }); - } - if (params.menuContext === 'interact') { - if (fileDefInstanceId && params.canEdit) { - // TODO: add menu item to delete the file - } - } - if ( - params.menuContext === 'ai-assistant' && - params.menuContextParams.canEditActiveRealm - ) { - menuItems.push({ - label: 'Copy to Workspace', - action: async () => { - const { newFileUrl } = await new CopyFileToRealmCommand( - params.commandContext, - ).execute({ - sourceFileUrl: fileDefInstance.sourceUrl, - targetRealm: params.menuContextParams.activeRealmURL, - }); - - await new ShowFileCommand(params.commandContext).execute({ - fileUrl: newFileUrl, - }); - }, - icon: ArrowLeft, - }); - } - if ( - ['code-mode-preview', 'code-mode-playground'].includes(params.menuContext) - ) { - menuItems.push({ - label: 'Open in Interact Mode', - action: () => { - new OpenInInteractModeCommand(params.commandContext).execute({ - cardId: fileDefInstanceId, - format: params.format === 'edit' ? 'edit' : 'isolated', - }); - }, - icon: Eye, - }); - } - if (params.menuContext === 'code-mode-playground') { - menuItems.push({ - label: 'Open in Code Mode', - action: async () => { - await new SwitchSubmodeCommand(params.commandContext).execute({ - submode: 'code', - codePath: fileDefInstanceId - ? new URL(fileDefInstanceId).href - : undefined, - }); - }, - icon: CodeIcon, - }); - } - return menuItems; -} diff --git a/packages/base/image-file-def.gts b/packages/base/image-file-def.gts index ea00c671d07..62de126d588 100644 --- a/packages/base/image-file-def.gts +++ b/packages/base/image-file-def.gts @@ -1,205 +1 @@ -import NumberField from './number'; -import { BaseDefComponent, Component, contains, field } from './card-api'; -import { FileDef } from './file-api'; - -class Isolated extends Component { - -} - -class Atom extends Component { - -} - -class Embedded extends Component { - -} - -class Fitted extends Component { - get backgroundImageStyle() { - if (this.args.model.url) { - return `background-image: url(${this.args.model.url});`; - } - return undefined; - } - - -} - -export class ImageDef extends FileDef { - static displayName = 'Image'; - static acceptTypes = 'image/*'; - - @field width = contains(NumberField); - @field height = contains(NumberField); - - static isolated: BaseDefComponent = Isolated; - static embedded: BaseDefComponent = Embedded; - static atom: BaseDefComponent = Atom; - static fitted: BaseDefComponent = Fitted; -} +export { ImageDef } from './card-api'; diff --git a/packages/base/number.gts b/packages/base/number.gts index 33e2e1606d9..9c3913e527b 100644 --- a/packages/base/number.gts +++ b/packages/base/number.gts @@ -1,59 +1,5 @@ -import { primitive, Component, useIndexBasedKey, FieldDef } from './card-api'; -import { BoxelInput } from '@cardstack/boxel-ui/components'; -import { TextInputValidator } from './text-input-validator'; -import { not } from '@cardstack/boxel-ui/helpers'; -import HashIcon from '@cardstack/boxel-icons/hash'; -import { fieldSerializer, NumberSerializer } from '@cardstack/runtime-common'; - -export function deserializeForUI(value: string | number | null): number | null { - const validationError = NumberSerializer.validate(value); - if (validationError) { - return null; - } - - return NumberSerializer.deserializeSync(value); -} - -export function serializeForUI(val: number | null): string | undefined { - let serialized = NumberSerializer.serialize(val); - if (serialized != null) { - return String(serialized); - } - return undefined; -} - -class View extends Component { - -} - -export default class NumberField extends FieldDef { - static displayName = 'Number'; - static icon = HashIcon; - static [primitive]: number; - static [fieldSerializer] = 'number'; - static [useIndexBasedKey]: never; - static embedded = View; - static atom = View; - - static edit = class Edit extends Component { - - - textInputValidator: TextInputValidator = new TextInputValidator( - () => this.args.model, - (inputVal) => this.args.set(inputVal), - deserializeForUI, - serializeForUI, - NumberSerializer.validate, - ); - }; -} +export { + NumberField as default, + deserializeForUI, + serializeForUI, +} from './card-api'; diff --git a/packages/experiments-realm/puppy-card.gts b/packages/experiments-realm/puppy-card.gts index 8a180f40c6e..be38e9fb859 100644 --- a/packages/experiments-realm/puppy-card.gts +++ b/packages/experiments-realm/puppy-card.gts @@ -1,7 +1,6 @@ import Base64ImageField from 'https://cardstack.com/base/base64-image'; import StringField from 'https://cardstack.com/base/string'; import { - MaybeBase64Field, CardDef, field, contains, @@ -18,7 +17,7 @@ export class PuppyCard extends CardDef { return this.name; }, }); - @field cardThumbnailURL = contains(MaybeBase64Field, { + @field cardThumbnailURL = contains(StringField, { computeVia: function (this: PuppyCard) { return this.picture.base64; }, diff --git a/packages/host/tests/cards/puppy-card.gts b/packages/host/tests/cards/puppy-card.gts index 48566435719..2a87fe059e1 100644 --- a/packages/host/tests/cards/puppy-card.gts +++ b/packages/host/tests/cards/puppy-card.gts @@ -1,10 +1,5 @@ import Base64ImageField from 'https://cardstack.com/base/base64-image'; -import { - MaybeBase64Field, - CardDef, - field, - contains, -} from 'https://cardstack.com/base/card-api'; +import { CardDef, field, contains } from 'https://cardstack.com/base/card-api'; import StringField from 'https://cardstack.com/base/string'; export class PuppyCard extends CardDef { @@ -16,7 +11,7 @@ export class PuppyCard extends CardDef { return this.name; }, }); - @field cardThumbnailURL = contains(MaybeBase64Field, { + @field cardThumbnailURL = contains(StringField, { computeVia: function (this: PuppyCard) { return this.picture.base64; }, diff --git a/packages/host/tests/helpers/base-realm.ts b/packages/host/tests/helpers/base-realm.ts index 7e1073ebb40..ee1a0e3cb05 100644 --- a/packages/host/tests/helpers/base-realm.ts +++ b/packages/host/tests/helpers/base-realm.ts @@ -93,7 +93,6 @@ let containsMany: (typeof CardAPIModule)['containsMany']; let isCard: (typeof CardAPIModule)['isCard']; let linksTo: (typeof CardAPIModule)['linksTo']; let linksToMany: (typeof CardAPIModule)['linksToMany']; -let MaybeBase64Field: (typeof CardAPIModule)['MaybeBase64Field']; let createFromSerialized: (typeof CardAPIModule)['createFromSerialized']; let updateFromSerialized: (typeof CardAPIModule)['updateFromSerialized']; let serializeCard: (typeof CardAPIModule)['serializeCard']; @@ -229,7 +228,6 @@ async function initialize() { unsubscribeFromChanges, flushLogs, queryableValue, - MaybeBase64Field, getFieldDescription, ReadOnlyField, instanceOf, @@ -280,7 +278,6 @@ export { isCard, linksTo, linksToMany, - MaybeBase64Field, createFromSerialized, updateFromSerialized, serializeCard, diff --git a/packages/host/tests/integration/components/card-basics-test.gts b/packages/host/tests/integration/components/card-basics-test.gts index 521a0cf4e1d..94d2207040d 100644 --- a/packages/host/tests/integration/components/card-basics-test.gts +++ b/packages/host/tests/integration/components/card-basics-test.gts @@ -74,7 +74,6 @@ import { linksTo, linksToMany, MarkdownField, - MaybeBase64Field, NumberField, PhoneNumberField, queryableValue, @@ -1215,7 +1214,7 @@ module('Integration | card-basics', function (hooks) { return this.firstName; }, }); - @field cardThumbnailURL = contains(MaybeBase64Field, { + @field cardThumbnailURL = contains(StringField, { computeVia: function (this: Person) { return this.image.base64; }, @@ -1273,7 +1272,7 @@ module('Integration | card-basics', function (hooks) { return this.firstName; }, }); - @field cardThumbnailURL = contains(MaybeBase64Field, { + @field cardThumbnailURL = contains(StringField, { computeVia: function (this: Person) { return this.image.base64; }, diff --git a/packages/realm-server/tests/helpers/index.ts b/packages/realm-server/tests/helpers/index.ts index 682c15398e5..04d94f9aa9e 100644 --- a/packages/realm-server/tests/helpers/index.ts +++ b/packages/realm-server/tests/helpers/index.ts @@ -2080,7 +2080,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: true, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2116,7 +2116,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: false, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2170,7 +2170,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: true, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2206,7 +2206,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: false, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2296,7 +2296,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: false, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2341,7 +2341,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: true, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2404,7 +2404,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: false, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2449,7 +2449,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: true, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2514,7 +2514,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: false, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true, @@ -2552,7 +2552,7 @@ export const cardDefinition: Definition['fields'] = { type: 'contains', isComputed: true, fieldOrCard: { - name: 'MaybeBase64Field', + name: 'StringField', module: 'https://cardstack.com/base/card-api', }, isPrimitive: true,