From 6d14d23327f861d297ccb99b4f6207b02bf9043a Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 12:38:13 +0800 Subject: [PATCH 01/12] Move FileDef, ImageDef, and NumberField into card-api --- packages/base/card-api.gts | 517 +++++++++++++++++- packages/base/file-api.gts | 270 +-------- packages/base/image-file-def.gts | 206 +------ packages/base/number.gts | 64 +-- .../tests/helpers/interact-submode-setup.gts | 2 +- 5 files changed, 527 insertions(+), 532 deletions(-) diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index 2870c584b7f..33031eaee2e 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -1,9 +1,14 @@ +import { concat } from '@ember/helper'; import Modifier from 'ember-modifier'; import GlimmerComponent from '@glimmer/component'; import { isEqual } from 'lodash'; import { WatchedArray } from './watched-array'; import { BoxelInput, CopyButton } from '@cardstack/boxel-ui/components'; -import { type MenuItemOptions, not } from '@cardstack/boxel-ui/helpers'; +import { + copyCardURLToClipboard, + type MenuItemOptions, + not, +} from '@cardstack/boxel-ui/helpers'; import { getBoxComponent, type BoxComponent, @@ -21,6 +26,7 @@ import { CodeRef, CommandContext, Deferred, + byteStreamToUint8Array, fields, fieldSerializer, fieldsUntracked, @@ -31,6 +37,7 @@ import { getSerializer, humanReadable, identifyCard, + inferContentType, isBaseInstance, isCardError, isCardInstance as _isCardInstance, @@ -51,6 +58,7 @@ import { relativeTo, SingleCardDocument, uuidv4, + NumberSerializer, type Format, type Meta, type CardFields, @@ -103,6 +111,7 @@ import MissingTemplate from './default-templates/missing-template'; import FieldDefEditTemplate from './default-templates/field-edit'; import MarkdownTemplate from './default-templates/markdown'; import CaptionsIcon from '@cardstack/boxel-icons/captions'; +import FileIcon from '@cardstack/boxel-icons/file'; import LetterCaseIcon from '@cardstack/boxel-icons/letter-case'; import MarkdownIcon from '@cardstack/boxel-icons/align-box-left-middle'; import RectangleEllipsisIcon from '@cardstack/boxel-icons/rectangle-ellipsis'; @@ -111,9 +120,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 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'; +import HashIcon from '@cardstack/boxel-icons/hash'; // 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, @@ -153,13 +172,13 @@ import { setRealmContextOnField, type NotLoadedValue, } from './field-support'; +import { TextInputValidator } from './text-input-validator'; import { type GetMenuItemParams, getDefaultCardMenuItems } from './menu-items'; import { LinkableDocument, SingleFileMetaDocument, } from '@cardstack/runtime-common/document-types'; import type { FileMetaResource } from '@cardstack/runtime-common'; -import type { FileDef } from './file-api'; export const BULK_GENERATED_ITEM_COUNT = 3; @@ -253,6 +272,17 @@ export type FieldFormats = { }; type Setter = (value: any) => void; +export type SerializedFile = { + sourceUrl: string; + url: string; + name: string; + contentType: string; + contentHash?: string; + contentSize?: number; +} & Extra; + +export type ByteStream = ReadableStream | Uint8Array; + interface Options { computeVia?: () => unknown; description?: string; @@ -2163,7 +2193,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) }]; } @@ -2448,6 +2481,479 @@ export class MarkdownField extends StringField { }; } +export function deserializeForUI(value: string | number | null): number | null { + let 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; +} + +export class NumberField extends FieldDef { + static displayName = 'Number'; + static icon = HashIcon; + static [primitive]: number; + static [fieldSerializer] = 'number'; + static [useIndexBasedKey]: never; + static embedded = class View extends Component { + + }; + static atom = this.embedded; + + static edit = class Edit extends Component { + + + textInputValidator: TextInputValidator = new TextInputValidator( + () => this.args.model, + (inputVal) => this.args.set(inputVal), + deserializeForUI, + serializeForUI, + NumberSerializer.validate, + ); + }; +} + +export interface SerializedFileDef { + url?: string; + sourceUrl: string; + name?: string; + contentHash?: string; + contentSize?: number; + contentType?: string; + content?: string; + error?: string; +} + +// 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; + + get [realmURL](): URL | undefined { + let realmURLString = getCardMeta(this, 'realmURL'); + return realmURLString ? new URL(realmURLString) : undefined; + } + + 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 = class View extends Component< + typeof this + > { + + }; + static fitted = this.embedded; + static isolated = this.embedded; + static atom = this.embedded; + static edit: BaseDefComponent = class Edit extends Component { + + }; + + 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 () => { + let { 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; +} + +export class ImageDef extends FileDef { + static displayName = 'Image'; + static acceptTypes = 'image/*'; + + @field width = contains(NumberField); + @field height = contains(NumberField); + + static isolated: BaseDefComponent = class Isolated extends Component< + typeof this + > { + + }; + static atom: BaseDefComponent = class Atom extends Component { + + }; + static embedded: BaseDefComponent = class Embedded extends Component< + typeof this + > { + + }; + static fitted: BaseDefComponent = class Fitted extends Component< + typeof this + > { + get backgroundImageStyle() { + if (this.args.model.url) { + return `background-image: url(${this.args.model.url});`; + } + return undefined; + } + + + }; +} + export class CardInfoField extends FieldDef { static displayName = 'Card Info'; @field name = contains(StringField); @@ -2790,7 +3296,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/file-api.gts b/packages/base/file-api.gts index aab737ad92a..f8579fde5a5 100644 --- a/packages/base/file-api.gts +++ b/packages/base/file-api.gts @@ -1,265 +1,9 @@ -import { concat } from '@ember/helper'; -import FileIcon from '@cardstack/boxel-icons/file'; -import type { MenuItemOptions } from '@cardstack/boxel-ui/helpers'; -import { copyCardURLToClipboard } from '@cardstack/boxel-ui/helpers'; -import { - byteStreamToUint8Array, - getMenuItems, - inferContentType, -} from '@cardstack/runtime-common'; -import { md5 } from 'super-fast-md5'; -import { - BaseDef, - BaseDefComponent, - Component, - GetMenuItemParams, - ReadOnlyField, - StringField, - contains, - field, - getCardMeta, - getDataBucket, - realmURL, +export { + FileContentMismatchError, + FileDef, + FileDef as default, + createFileDef, + getDefaultFileMenuItems, } from './card-api'; -import NumberField from './number'; -import ArrowLeft from '@cardstack/boxel-icons/arrow-left'; -import LinkIcon from '@cardstack/boxel-icons/link'; -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 Eye from '@cardstack/boxel-icons/eye'; -import SwitchSubmodeCommand from '@cardstack/boxel-host/commands/switch-submode'; -import CodeIcon from '@cardstack/boxel-icons/code'; -import { isSavedInstance } from './-private'; -class View extends Component { - -} - -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; - - get [realmURL](): URL | undefined { - let realmURLString = getCardMeta(this, 'realmURL'); - return realmURLString ? new URL(realmURLString) : undefined; - } - - 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; -} +export type { ByteStream, SerializedFile, SerializedFileDef } from './card-api'; diff --git a/packages/base/image-file-def.gts b/packages/base/image-file-def.gts index ea00c671d07..d7f4df96254 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 as default } 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/host/tests/helpers/interact-submode-setup.gts b/packages/host/tests/helpers/interact-submode-setup.gts index 048184f1859..eea21922ab4 100644 --- a/packages/host/tests/helpers/interact-submode-setup.gts +++ b/packages/host/tests/helpers/interact-submode-setup.gts @@ -81,7 +81,7 @@ export function setupInteractSubmodeTests( let { Spec } = spec; let { CardsGrid } = cardsGrid; let { FileDef } = fileApi; - let { ImageDef } = imageFileApi; + let { default: ImageDef } = imageFileApi; class Pet extends CardDef { static displayName = 'Pet'; From 485ed43bb144426f035a07744bf4d39608fcc196 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 12:38:43 +0800 Subject: [PATCH 02/12] Update ImageDef consumers to use default image-file-def import --- packages/base/avif-image-def.gts | 2 +- packages/base/gif-image-def.gts | 2 +- packages/base/jpg-image-def.gts | 2 +- packages/base/png-image-def.gts | 2 +- packages/base/svg-image-def.gts | 2 +- packages/base/webp-image-def.gts | 2 +- packages/experiments-realm/image-def-playground.gts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/base/avif-image-def.gts b/packages/base/avif-image-def.gts index fbf86464df7..62a83990617 100644 --- a/packages/base/avif-image-def.gts +++ b/packages/base/avif-image-def.gts @@ -1,5 +1,5 @@ import { readFirstBytes } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractAvifDimensions } from './avif-meta-extractor'; diff --git a/packages/base/gif-image-def.gts b/packages/base/gif-image-def.gts index 5e68fca9c61..a3335993436 100644 --- a/packages/base/gif-image-def.gts +++ b/packages/base/gif-image-def.gts @@ -1,5 +1,5 @@ import { readFirstBytes } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractGifDimensions } from './gif-meta-extractor'; diff --git a/packages/base/jpg-image-def.gts b/packages/base/jpg-image-def.gts index 9d2faf62bf1..3469fab67b5 100644 --- a/packages/base/jpg-image-def.gts +++ b/packages/base/jpg-image-def.gts @@ -1,5 +1,5 @@ import { readFirstBytes } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractJpgDimensions } from './jpg-meta-extractor'; diff --git a/packages/base/png-image-def.gts b/packages/base/png-image-def.gts index 05dbfbd0bc5..5fd820ab02b 100644 --- a/packages/base/png-image-def.gts +++ b/packages/base/png-image-def.gts @@ -1,5 +1,5 @@ import { readFirstBytes } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractPngDimensions } from './png-meta-extractor'; diff --git a/packages/base/svg-image-def.gts b/packages/base/svg-image-def.gts index 5e341b99551..61400217214 100644 --- a/packages/base/svg-image-def.gts +++ b/packages/base/svg-image-def.gts @@ -1,5 +1,5 @@ import { byteStreamToUint8Array } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractSvgDimensions } from './svg-meta-extractor'; diff --git a/packages/base/webp-image-def.gts b/packages/base/webp-image-def.gts index dda8887f7a2..18a325e97a9 100644 --- a/packages/base/webp-image-def.gts +++ b/packages/base/webp-image-def.gts @@ -1,5 +1,5 @@ import { readFirstBytes } from '@cardstack/runtime-common'; -import { ImageDef } from './image-file-def'; +import ImageDef from './image-file-def'; import { type ByteStream, type SerializedFile } from './file-api'; import { extractWebpDimensions } from './webp-meta-extractor'; diff --git a/packages/experiments-realm/image-def-playground.gts b/packages/experiments-realm/image-def-playground.gts index 9f5925fe674..33354b9ae75 100644 --- a/packages/experiments-realm/image-def-playground.gts +++ b/packages/experiments-realm/image-def-playground.gts @@ -4,11 +4,11 @@ import { StringField, contains, field, + ImageDef, linksTo, linksToMany, } from 'https://cardstack.com/base/card-api'; import { gt } from '@cardstack/boxel-ui/helpers'; -import { ImageDef } from 'https://cardstack.com/base/image-file-def'; /** * Playground card for demonstrating ImageDef capabilities. From 0eceb9c9ef731295d3626c216bee268277e576cd Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 15:45:52 +0800 Subject: [PATCH 03/12] fix: update FileDef module refs from file-api to card-api, making its canonical module URL --- packages/experiments-realm/file-search-playground.gts | 4 ++-- packages/host/tests/cards/file-query-card.gts | 2 +- .../integration/components/file-def-serialization-test.gts | 2 +- .../integration/components/prerendered-card-search-test.gts | 2 +- packages/host/tests/integration/realm-querying-test.gts | 4 ++-- .../host/tests/integration/resources/search-data-test.ts | 2 +- packages/host/tests/integration/resources/search-test.ts | 4 ++-- packages/host/tests/integration/store-test.gts | 6 +++--- packages/host/tests/unit/services/render-service-test.ts | 2 +- packages/realm-server/tests/indexing-test.ts | 2 +- packages/realm-server/tests/prerendering-test.ts | 2 +- packages/realm-server/tests/realm-endpoints/search-test.ts | 4 ++-- packages/realm-server/tests/search-prerendered-test.ts | 2 +- packages/runtime-common/constants.ts | 2 +- packages/runtime-common/realm-index-query-engine.ts | 2 +- 15 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/experiments-realm/file-search-playground.gts b/packages/experiments-realm/file-search-playground.gts index 1f69ec29911..a1397d02174 100644 --- a/packages/experiments-realm/file-search-playground.gts +++ b/packages/experiments-realm/file-search-playground.gts @@ -15,13 +15,13 @@ const fileSearchQuery = { every: [ { type: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, }, { on: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, contains: { diff --git a/packages/host/tests/cards/file-query-card.gts b/packages/host/tests/cards/file-query-card.gts index 341033feb39..57bbb064e8e 100644 --- a/packages/host/tests/cards/file-query-card.gts +++ b/packages/host/tests/cards/file-query-card.gts @@ -9,7 +9,7 @@ import StringField from 'https://cardstack.com/base/string'; const fileSearchQuery = { filter: { - type: { module: 'https://cardstack.com/base/file-api', name: 'FileDef' }, + type: { module: 'https://cardstack.com/base/card-api', name: 'FileDef' }, }, realm: '$thisRealm', }; diff --git a/packages/host/tests/integration/components/file-def-serialization-test.gts b/packages/host/tests/integration/components/file-def-serialization-test.gts index 1bcb2b8f7a9..9d82eb91e3c 100644 --- a/packages/host/tests/integration/components/file-def-serialization-test.gts +++ b/packages/host/tests/integration/components/file-def-serialization-test.gts @@ -49,7 +49,7 @@ module('Integration | serializeFileDef', function (hooks) { let doc = serializeFileDef(fileDef); assert.deepEqual(doc.data.meta.adoptsFrom, { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }); }); diff --git a/packages/host/tests/integration/components/prerendered-card-search-test.gts b/packages/host/tests/integration/components/prerendered-card-search-test.gts index e943c17ef5a..ce833905c7a 100644 --- a/packages/host/tests/integration/components/prerendered-card-search-test.gts +++ b/packages/host/tests/integration/components/prerendered-card-search-test.gts @@ -464,7 +464,7 @@ module(`Integration | prerendered-card-search`, function (hooks) { let query: Query = { filter: { on: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, eq: { diff --git a/packages/host/tests/integration/realm-querying-test.gts b/packages/host/tests/integration/realm-querying-test.gts index 152664e2cc1..60085d7a8e3 100644 --- a/packages/host/tests/integration/realm-querying-test.gts +++ b/packages/host/tests/integration/realm-querying-test.gts @@ -744,7 +744,7 @@ module(`Integration | realm querying`, function (hooks) { }); test('can search for file-meta entries by FileDef type', async function (assert) { - let fileDefRef = { module: `${baseRealm.url}file-api`, name: 'FileDef' }; + let fileDefRef = { module: `${baseRealm.url}card-api`, name: 'FileDef' }; let result = (await queryEngine.searchCards({ filter: { type: fileDefRef, @@ -764,7 +764,7 @@ module(`Integration | realm querying`, function (hooks) { }); test('can search for file-meta entries by url', async function (assert) { - let fileDefRef = { module: `${baseRealm.url}file-api`, name: 'FileDef' }; + let fileDefRef = { module: `${baseRealm.url}card-api`, name: 'FileDef' }; let targetUrl = `${testRealmURL}files/sample.txt`; let result = (await queryEngine.searchCards({ filter: { diff --git a/packages/host/tests/integration/resources/search-data-test.ts b/packages/host/tests/integration/resources/search-data-test.ts index 31ea5d7f301..ce3123afbb7 100644 --- a/packages/host/tests/integration/resources/search-data-test.ts +++ b/packages/host/tests/integration/resources/search-data-test.ts @@ -207,7 +207,7 @@ module(`Integration | search data resource`, function (hooks) { let query: DataQuery = { filter: { type: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, }, diff --git a/packages/host/tests/integration/resources/search-test.ts b/packages/host/tests/integration/resources/search-test.ts index 4ed5d520057..9aa071a4032 100644 --- a/packages/host/tests/integration/resources/search-test.ts +++ b/packages/host/tests/integration/resources/search-test.ts @@ -595,7 +595,7 @@ module(`Integration | search resource`, function (hooks) { let query: Query = { filter: { type: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, }, @@ -634,7 +634,7 @@ module(`Integration | search resource`, function (hooks) { let query: Query = { filter: { type: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, }, diff --git a/packages/host/tests/integration/store-test.gts b/packages/host/tests/integration/store-test.gts index 70f0c16183b..6ac63e9e7ba 100644 --- a/packages/host/tests/integration/store-test.gts +++ b/packages/host/tests/integration/store-test.gts @@ -1277,7 +1277,7 @@ module('Integration | Store', function (hooks) { }, meta: { adoptsFrom: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, }, @@ -1293,7 +1293,7 @@ module('Integration | Store', function (hooks) { }, meta: { adoptsFrom: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, }, @@ -1309,7 +1309,7 @@ module('Integration | Store', function (hooks) { }, meta: { adoptsFrom: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, }, diff --git a/packages/host/tests/unit/services/render-service-test.ts b/packages/host/tests/unit/services/render-service-test.ts index 508af4bc9ea..1f69d3f5caa 100644 --- a/packages/host/tests/unit/services/render-service-test.ts +++ b/packages/host/tests/unit/services/render-service-test.ts @@ -134,7 +134,7 @@ module('Unit | Service | render-service', function (hooks) { type: 'file-meta', meta: { adoptsFrom: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, }, diff --git a/packages/realm-server/tests/indexing-test.ts b/packages/realm-server/tests/indexing-test.ts index 2908295fc39..0abcb8c6075 100644 --- a/packages/realm-server/tests/indexing-test.ts +++ b/packages/realm-server/tests/indexing-test.ts @@ -1078,7 +1078,7 @@ module(basename(__filename), function () { // Mutate the index row so we can validate that the response must come from the index, // not from filesystem metadata. await testDbAdapter.execute( - `UPDATE boxel_index SET search_doc = '{"name":"from-index.txt","contentType":"application/x-index-test"}'::jsonb, pristine_doc = '{"id":"${testRealm}random-file.txt","type":"file-meta","attributes":{"name":"from-pristine.txt","contentType":"application/x-pristine","custom":"present"},"meta":{"adoptsFrom":{"module":"https://cardstack.com/base/file-api","name":"FileDef"}}}'::jsonb WHERE url = '${testRealm}random-file.txt'`, + `UPDATE boxel_index SET search_doc = '{"name":"from-index.txt","contentType":"application/x-index-test"}'::jsonb, pristine_doc = '{"id":"${testRealm}random-file.txt","type":"file-meta","attributes":{"name":"from-pristine.txt","contentType":"application/x-pristine","custom":"present"},"meta":{"adoptsFrom":{"module":"https://cardstack.com/base/card-api","name":"FileDef"}}}'::jsonb WHERE url = '${testRealm}random-file.txt'`, ); let response = await fetch(`${testRealm}random-file.txt`, { headers: { Accept: SupportedMimeType.FileMeta }, diff --git a/packages/realm-server/tests/prerendering-test.ts b/packages/realm-server/tests/prerendering-test.ts index a6cdea4c4e2..1da0adb2332 100644 --- a/packages/realm-server/tests/prerendering-test.ts +++ b/packages/realm-server/tests/prerendering-test.ts @@ -1920,7 +1920,7 @@ module(basename(__filename), function () { return { filter: { on: { - module: 'https://cardstack.com/base/file-api', + module: 'https://cardstack.com/base/card-api', name: 'FileDef', }, eq: { diff --git a/packages/realm-server/tests/realm-endpoints/search-test.ts b/packages/realm-server/tests/realm-endpoints/search-test.ts index 9e230b5ad6a..b27d981d380 100644 --- a/packages/realm-server/tests/realm-endpoints/search-test.ts +++ b/packages/realm-server/tests/realm-endpoints/search-test.ts @@ -47,7 +47,7 @@ module(`realm-endpoints/${basename(__filename)}`, function () { return { filter: { type: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, }, @@ -223,7 +223,7 @@ module(`realm-endpoints/${basename(__filename)}`, function () { .send({ filter: { on: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, eq: { diff --git a/packages/realm-server/tests/search-prerendered-test.ts b/packages/realm-server/tests/search-prerendered-test.ts index 33260a4d18b..014e1b1379b 100644 --- a/packages/realm-server/tests/search-prerendered-test.ts +++ b/packages/realm-server/tests/search-prerendered-test.ts @@ -159,7 +159,7 @@ module(basename(__filename), function () { let queryBase: Query = { filter: { on: { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }, eq: { diff --git a/packages/runtime-common/constants.ts b/packages/runtime-common/constants.ts index 0c85ca32bcf..fdc477bc139 100644 --- a/packages/runtime-common/constants.ts +++ b/packages/runtime-common/constants.ts @@ -28,7 +28,7 @@ export const skillCardRef: ResolvedCodeRef = { name: 'Skill', }; export const baseFileRef: ResolvedCodeRef = { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }; diff --git a/packages/runtime-common/realm-index-query-engine.ts b/packages/runtime-common/realm-index-query-engine.ts index 5741d2c46ba..bf5d0a12a21 100644 --- a/packages/runtime-common/realm-index-query-engine.ts +++ b/packages/runtime-common/realm-index-query-engine.ts @@ -1218,7 +1218,7 @@ function fileResourceFromIndex( (isCodeRef(fileEntry.resource?.meta?.adoptsFrom) ? fileEntry.resource?.meta?.adoptsFrom : { - module: `${baseRealm.url}file-api`, + module: `${baseRealm.url}card-api`, name: 'FileDef', }); let resourceAttributes = fileEntry.resource?.attributes ?? {}; From ea4205b46229d1b2125fc5c475d2f49bb1ce8cc8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 15:56:30 +0800 Subject: [PATCH 04/12] fix prerenderedtest --- packages/host/tests/integration/realm-indexing-test.gts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/host/tests/integration/realm-indexing-test.gts b/packages/host/tests/integration/realm-indexing-test.gts index bad0bc0e64e..cfd4b39590c 100644 --- a/packages/host/tests/integration/realm-indexing-test.gts +++ b/packages/host/tests/integration/realm-indexing-test.gts @@ -4609,6 +4609,7 @@ module(`Integration | realm indexing`, function (hooks) { 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/captions', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/code', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/eye', + 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/file', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/file-pencil', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/folder-pen', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/git-branch', @@ -4657,6 +4658,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://cardstack.com/base/watched-array', 'https://packages/@cardstack/boxel-host/commands/copy-and-edit', 'https://packages/@cardstack/boxel-host/commands/copy-card', + 'https://packages/@cardstack/boxel-host/commands/copy-file-to-realm', 'https://packages/@cardstack/boxel-host/commands/create-ai-assistant-room', 'https://packages/@cardstack/boxel-host/commands/generate-example-cards', 'https://packages/@cardstack/boxel-host/commands/generate-readme-spec', @@ -4666,6 +4668,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/@cardstack/boxel-host/commands/populate-with-sample-data', 'https://packages/@cardstack/boxel-host/commands/send-ai-assistant-message', 'https://packages/@cardstack/boxel-host/commands/show-card', + 'https://packages/@cardstack/boxel-host/commands/show-file', 'https://packages/@cardstack/boxel-host/commands/switch-submode', 'https://packages/@cardstack/boxel-ui/components', 'https://packages/@cardstack/boxel-ui/helpers', @@ -4689,6 +4692,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/ember-provide-consume-context', 'https://packages/ember-resources', 'https://packages/lodash', + 'https://packages/super-fast-md5', 'https://packages/tracked-built-ins', ], 'the card references for the instance are correct', From 349208593b7ab02eda1760cae8f440f2d88c63f9 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 17:34:33 +0800 Subject: [PATCH 05/12] add cardThumbnail field --- packages/base/card-api.gts | 3 ++- packages/base/default-templates/card-info.gts | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index 33031eaee2e..48281b14312 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -2958,6 +2958,7 @@ export class CardInfoField extends FieldDef { static displayName = 'Card Info'; @field name = contains(StringField); @field summary = contains(StringField); + @field cardThumbnail = linksTo(ImageDef); @field cardThumbnailURL = contains(MaybeBase64Field); @field theme = linksTo(() => Theme); @field notes = contains(MarkdownField); @@ -3001,7 +3002,7 @@ export class CardDef extends BaseDef { // to use it directly now (or wait until a better image field comes along) @field cardThumbnailURL = contains(MaybeBase64Field, { computeVia: function (this: CardDef) { - return this.cardInfo.cardThumbnailURL; + return this.cardInfo.cardThumbnail?.url ?? this.cardInfo.cardThumbnailURL; }, }); static displayName = 'Card'; diff --git a/packages/base/default-templates/card-info.gts b/packages/base/default-templates/card-info.gts index 108797f08fb..80bf3ec11ae 100644 --- a/packages/base/default-templates/card-info.gts +++ b/packages/base/default-templates/card-info.gts @@ -6,6 +6,7 @@ import CaptionsIcon from '@cardstack/boxel-icons/captions'; import NameIcon from '@cardstack/boxel-icons/folder-pen'; import SummaryIcon from '@cardstack/boxel-icons/notepad-text'; import LinkIcon from '@cardstack/boxel-icons/link'; +import ImageIcon from '@cardstack/boxel-icons/image'; import ThemeIcon from '@cardstack/boxel-icons/palette'; import type { CardOrFieldTypeIcon, CardDef, FieldsTypeFor } from '../card-api'; @@ -217,6 +218,14 @@ class CardInfoEditor extends GlimmerComponent { {{#if this.isThumbnailEditorVisible}}
+ + <@fields.cardInfo.cardThumbnail /> + { private get showThumbnailPlaceholder() { return ( + !this.args.model?.cardInfo?.cardThumbnail && !this.args.model?.cardInfo?.cardThumbnailURL && this.args.model?.cardThumbnailURL ); From 87476212382eca1803cc79f00f61166d0c3e794b Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Apr 2026 17:39:38 +0800 Subject: [PATCH 06/12] fix test --- packages/host/tests/integration/realm-indexing-test.gts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/host/tests/integration/realm-indexing-test.gts b/packages/host/tests/integration/realm-indexing-test.gts index aa7d12a1a27..bb76b4bd9ea 100644 --- a/packages/host/tests/integration/realm-indexing-test.gts +++ b/packages/host/tests/integration/realm-indexing-test.gts @@ -4482,9 +4482,11 @@ module(`Integration | realm indexing`, function (hooks) { 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/captions', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/code', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/eye', + 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/file', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/file-pencil', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/folder-pen', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/hash', + 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/image', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/import', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/letter-case', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/link', @@ -4522,6 +4524,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://cardstack.com/base/watched-array', 'https://packages/@cardstack/boxel-host/commands/copy-and-edit', 'https://packages/@cardstack/boxel-host/commands/copy-card', + 'https://packages/@cardstack/boxel-host/commands/copy-file-to-realm', 'https://packages/@cardstack/boxel-host/commands/create-ai-assistant-room', 'https://packages/@cardstack/boxel-host/commands/generate-example-cards', 'https://packages/@cardstack/boxel-host/commands/open-create-listing-modal', @@ -4530,6 +4533,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/@cardstack/boxel-host/commands/populate-with-sample-data', 'https://packages/@cardstack/boxel-host/commands/send-ai-assistant-message', 'https://packages/@cardstack/boxel-host/commands/show-card', + 'https://packages/@cardstack/boxel-host/commands/show-file', 'https://packages/@cardstack/boxel-host/commands/switch-submode', 'https://packages/@cardstack/boxel-ui/components', 'https://packages/@cardstack/boxel-ui/helpers', @@ -4552,6 +4556,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/ember-modifier', 'https://packages/ember-provide-consume-context', 'https://packages/lodash', + 'https://packages/super-fast-md5', 'https://packages/tracked-built-ins', ], 'the card references for the instance are correct', From fa904a69a20a74ee440c97611fd9d0a71f69b852 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 11:14:52 +0800 Subject: [PATCH 07/12] fix test --- packages/base/image-file-def.gts | 2 +- .../acceptance/code-submode/editor-test.ts | 5 ++ .../tests/acceptance/prerender-meta-test.gts | 17 +++- .../commands/patch-instance-test.gts | 5 ++ .../components/operator-mode-links-test.gts | 5 ++ .../tests/integration/realm-indexing-test.gts | 90 +++++++++++++------ .../tests/unit/ai-function-generation-test.ts | 1 + 7 files changed, 94 insertions(+), 31 deletions(-) diff --git a/packages/base/image-file-def.gts b/packages/base/image-file-def.gts index d7f4df96254..1b64bb79ab0 100644 --- a/packages/base/image-file-def.gts +++ b/packages/base/image-file-def.gts @@ -1 +1 @@ -export { ImageDef as default } from './card-api'; +export { ImageDef, ImageDef as default } from './card-api'; diff --git a/packages/host/tests/acceptance/code-submode/editor-test.ts b/packages/host/tests/acceptance/code-submode/editor-test.ts index 508af2711d9..8d10ce206d8 100644 --- a/packages/host/tests/acceptance/code-submode/editor-test.ts +++ b/packages/host/tests/acceptance/code-submode/editor-test.ts @@ -527,6 +527,11 @@ module('Acceptance | code submode | editor tests', function (hooks) { }, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, diff --git a/packages/host/tests/acceptance/prerender-meta-test.gts b/packages/host/tests/acceptance/prerender-meta-test.gts index 2ea9ccbe899..c5b51a0d96e 100644 --- a/packages/host/tests/acceptance/prerender-meta-test.gts +++ b/packages/host/tests/acceptance/prerender-meta-test.gts @@ -346,7 +346,9 @@ module('Acceptance | prerender | meta', function (hooks) { { id: `${testRealmURL}Pet/mango`, _cardType: 'Pet', - cardInfo: {}, + cardInfo: { + cardThumbnail: null, + }, name: 'Mango', cardTitle: 'Mango', }, @@ -364,7 +366,9 @@ module('Acceptance | prerender | meta', function (hooks) { { id: `${testRealmURL}Pet/paper`, _cardType: 'Cat', - cardInfo: {}, + cardInfo: { + cardThumbnail: null, + }, name: 'Paper', cardTitle: 'Paper', aliases: ['Satan', "Satan's Mistress"], @@ -387,6 +391,7 @@ module('Acceptance | prerender | meta', function (hooks) { id: `${testRealmURL}Person/jade`, _cardType: 'Person', cardInfo: { + cardThumbnail: null, theme: null, }, name: 'Jade', @@ -396,6 +401,7 @@ module('Acceptance | prerender | meta', function (hooks) { friend: { id: `${testRealmURL}Person/hassan`, cardInfo: { + cardThumbnail: null, theme: null, }, name: 'Hassan', @@ -430,6 +436,7 @@ module('Acceptance | prerender | meta', function (hooks) { id: `${testRealmURL}Person/hassan`, _cardType: 'Person', cardInfo: { + cardThumbnail: null, theme: null, }, name: 'Hassan', @@ -442,6 +449,7 @@ module('Acceptance | prerender | meta', function (hooks) { name: 'Mango', cardTitle: 'Mango', cardInfo: { + cardThumbnail: null, theme: null, }, }, @@ -450,6 +458,7 @@ module('Acceptance | prerender | meta', function (hooks) { name: 'Van Gogh', cardTitle: 'Van Gogh', cardInfo: { + cardThumbnail: null, theme: null, }, }, @@ -460,6 +469,7 @@ module('Acceptance | prerender | meta', function (hooks) { aliases: ['Satan', "Satan's Mistress"], emergencyContacts: null, cardInfo: { + cardThumbnail: null, theme: null, }, }, @@ -481,6 +491,7 @@ module('Acceptance | prerender | meta', function (hooks) { _cardType: 'Cat', aliases: null, cardInfo: { + cardThumbnail: null, theme: null, }, emergencyContacts: [ @@ -493,6 +504,7 @@ module('Acceptance | prerender | meta', function (hooks) { numOfPets: '0', pets: null, cardInfo: { + cardThumbnail: null, theme: null, }, friend: { @@ -507,6 +519,7 @@ module('Acceptance | prerender | meta', function (hooks) { name: 'Hassan', cardTitle: 'Hassan', cardInfo: { + cardThumbnail: null, theme: null, }, numOfPets: '3', diff --git a/packages/host/tests/integration/commands/patch-instance-test.gts b/packages/host/tests/integration/commands/patch-instance-test.gts index e5cee817dfc..ca46595eed5 100644 --- a/packages/host/tests/integration/commands/patch-instance-test.gts +++ b/packages/host/tests/integration/commands/patch-instance-test.gts @@ -139,6 +139,7 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -209,6 +210,7 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -279,6 +281,7 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -347,6 +350,7 @@ module('Integration | commands | patch-instance', function (hooks) { }, 'friends.0': { links: { self: `./germaine` } }, 'friends.1': { links: { self: `./queenzy` } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -493,6 +497,7 @@ module('Integration | commands | patch-instance', function (hooks) { instance.relationships, { bestFriend: { links: { self: `./queenzy` } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, friends: { links: { diff --git a/packages/host/tests/integration/components/operator-mode-links-test.gts b/packages/host/tests/integration/components/operator-mode-links-test.gts index a80417c9a58..efceadd7edf 100644 --- a/packages/host/tests/integration/components/operator-mode-links-test.gts +++ b/packages/host/tests/integration/components/operator-mode-links-test.gts @@ -522,6 +522,11 @@ module('Integration | operator-mode | links', function (hooks) { }, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, diff --git a/packages/host/tests/integration/realm-indexing-test.gts b/packages/host/tests/integration/realm-indexing-test.gts index bb76b4bd9ea..9cee5b082b3 100644 --- a/packages/host/tests/integration/realm-indexing-test.gts +++ b/packages/host/tests/integration/realm-indexing-test.gts @@ -146,6 +146,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -171,7 +172,7 @@ module(`Integration | realm indexing`, function (hooks) { resourceCreatedAt: await getFileCreatedAt(realm, 'empty.json'), }, links: { - self: `${testRealmURL}empty`, + self: `./empty`, }, }, ]); @@ -416,6 +417,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -515,6 +517,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -572,6 +575,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -668,6 +672,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -765,6 +770,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -858,6 +864,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -900,7 +907,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -988,6 +995,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -1030,7 +1038,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -1066,6 +1074,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: skillCardRef, @@ -1103,7 +1112,7 @@ module(`Integration | realm indexing`, function (hooks) { cardTitle: 'Switch Submode', }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -2101,6 +2110,7 @@ module(`Integration | realm indexing`, function (hooks) { links: { self: `./mango` }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }); } else { assert.ok( @@ -2240,6 +2250,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2285,6 +2296,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2326,6 +2338,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2477,7 +2490,7 @@ module(`Integration | realm indexing`, function (hooks) { cardDescription: 'Person', fullName: 'Hassan Abdel-Rahman', _cardType: 'Person', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, `search doc includes fullName field`, ); @@ -2561,7 +2574,7 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Publication/pacific`, }, views: 5, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, `post 1 search doc includes publication relationship`, ); @@ -2609,7 +2622,7 @@ module(`Integration | realm indexing`, function (hooks) { views: 24, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, `publication search doc includes featuredPosts relationship via isUsed=true`, ); @@ -2703,7 +2716,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); // we should be able to perform a structured clone of the search doc (this // emulates the limitations of the postMessage used to communicate between @@ -2792,6 +2805,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { id: `${testRealmURL}Pet/vanGogh`, type: 'card' }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2835,6 +2849,7 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { owner: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { module: `${testModuleRealm}pet`, name: 'Pet' }, @@ -2871,6 +2886,7 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { owner: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { module: `${testModuleRealm}pet`, name: 'Pet' }, @@ -2919,21 +2935,21 @@ module(`Integration | realm indexing`, function (hooks) { firstName: 'Mango', owner: null, cardTitle: 'Mango', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, { id: `${testRealmURL}Pet/vanGogh`, firstName: 'Van Gogh', owner: null, cardTitle: 'Van Gogh', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, ], friend: null, cardTitle: 'Hassan Pet Person', cardDescription: 'A person with pets', cardThumbnailURL: null, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -2953,6 +2969,7 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { pets: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2989,6 +3006,7 @@ module(`Integration | realm indexing`, function (hooks) { pets: { links: { self: null } }, friend: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3039,7 +3057,7 @@ module(`Integration | realm indexing`, function (hooks) { cardTitle: 'Burcu Pet Person', cardDescription: 'A person with pets', cardThumbnailURL: null, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3128,6 +3146,7 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -3182,7 +3201,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3294,6 +3313,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3348,9 +3368,9 @@ module(`Integration | realm indexing`, function (hooks) { friend: { id: `${testRealmURL}Friend/vanGogh`, }, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3445,6 +3465,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3496,6 +3517,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3551,10 +3573,10 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Friend/hassan`, }, cardDescription: 'Dog friend', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, cardTitle: 'Hassan', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3593,6 +3615,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3644,6 +3667,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3700,9 +3724,9 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Friend/mango`, }, cardDescription: 'Dog owner', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3771,6 +3795,7 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3821,7 +3846,7 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Friend/hassan`, }, cardTitle: 'Hassan', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }); } else { assert.ok( @@ -3925,6 +3950,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -3971,6 +3997,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4011,6 +4038,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4059,17 +4087,17 @@ module(`Integration | realm indexing`, function (hooks) { firstName: 'Mango', cardTitle: 'Mango', friends: [{ id: hassanID }], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, { id: vanGoghID, firstName: 'Van Gogh', friends: [{ id: hassanID }], cardTitle: 'Van Gogh', - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, 'hassan searchData is correct', ); @@ -4100,6 +4128,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4149,6 +4178,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4189,6 +4219,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4247,13 +4278,13 @@ module(`Integration | realm indexing`, function (hooks) { id: hassanID, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, 'mango searchData is correct', ); @@ -4284,6 +4315,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4333,6 +4365,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4373,6 +4406,7 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, + 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4436,10 +4470,10 @@ module(`Integration | realm indexing`, function (hooks) { }, { id: vanGoghID }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { theme: null }, + cardInfo: { cardThumbnail: null, theme: null }, }, 'vanGogh searchData is correct', ); diff --git a/packages/host/tests/unit/ai-function-generation-test.ts b/packages/host/tests/unit/ai-function-generation-test.ts index 8d91fa737f9..f28d8e6d426 100644 --- a/packages/host/tests/unit/ai-function-generation-test.ts +++ b/packages/host/tests/unit/ai-function-generation-test.ts @@ -100,6 +100,7 @@ module('Unit | ai-function-generation-test', function (hooks) { const cardDefRelationshipsProperties: { [fieldName: string]: RelationshipSchema; } = { + 'cardInfo.cardThumbnail': linkedRelationship, 'cardInfo.theme': linkedRelationship, }; From 8d2a8bd493cf556f87dd799511041aab5975f2ba Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 12:04:01 +0800 Subject: [PATCH 08/12] move template to default template --- packages/base/card-api.gts | 311 +----------------- .../base/default-templates/file-def-edit.gts | 27 ++ .../base/default-templates/image-def-atom.gts | 41 +++ .../default-templates/image-def-embedded.gts | 35 ++ .../default-templates/image-def-fitted.gts | 62 ++++ .../default-templates/image-def-isolated.gts | 66 ++++ packages/base/file-menu-items.ts | 92 ++++++ 7 files changed, 336 insertions(+), 298 deletions(-) create mode 100644 packages/base/default-templates/file-def-edit.gts create mode 100644 packages/base/default-templates/image-def-atom.gts create mode 100644 packages/base/default-templates/image-def-embedded.gts create mode 100644 packages/base/default-templates/image-def-fitted.gts create mode 100644 packages/base/default-templates/image-def-isolated.gts create mode 100644 packages/base/file-menu-items.ts diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index 5e9db10c6aa..afec66f066d 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -1,14 +1,9 @@ -import { concat } from '@ember/helper'; import Modifier from 'ember-modifier'; import GlimmerComponent from '@glimmer/component'; import { isEqual } from 'lodash'; import { WatchedArray } from './watched-array'; import { BoxelInput, CopyButton } from '@cardstack/boxel-ui/components'; -import { - copyCardURLToClipboard, - type MenuItemOptions, - not, -} from '@cardstack/boxel-ui/helpers'; +import { type MenuItemOptions, not } from '@cardstack/boxel-ui/helpers'; import { getBoxComponent, type BoxComponent, @@ -110,6 +105,11 @@ import DefaultHeadTemplate from './default-templates/head'; import MissingTemplate from './default-templates/missing-template'; import FieldDefEditTemplate from './default-templates/field-edit'; import MarkdownTemplate from './default-templates/markdown'; +import FileDefEditTemplate from './default-templates/file-def-edit'; +import ImageDefAtomTemplate from './default-templates/image-def-atom'; +import ImageDefEmbeddedTemplate from './default-templates/image-def-embedded'; +import ImageDefFittedTemplate from './default-templates/image-def-fitted'; +import ImageDefIsolatedTemplate from './default-templates/image-def-isolated'; import CaptionsIcon from '@cardstack/boxel-icons/captions'; import FileIcon from '@cardstack/boxel-icons/file'; import LetterCaseIcon from '@cardstack/boxel-icons/letter-case'; @@ -120,18 +120,10 @@ 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 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'; import HashIcon from '@cardstack/boxel-icons/hash'; // 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 { @@ -174,6 +166,7 @@ import { } from './field-support'; import { TextInputValidator } from './text-input-validator'; import { type GetMenuItemParams, getDefaultCardMenuItems } from './menu-items'; +import { getDefaultFileMenuItems } from './file-menu-items'; import { LinkableDocument, SingleFileMetaDocument, @@ -2630,25 +2623,7 @@ export class FileDef extends BaseDef { static fitted = this.embedded; static isolated = this.embedded; static atom = this.embedded; - static edit: BaseDefComponent = class Edit extends Component { - - }; + static edit: BaseDefComponent = FileDefEditTemplate; static async extractAttributes( url: string, @@ -2720,80 +2695,7 @@ export function createFileDef({ }); } -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 () => { - let { 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; -} +export { getDefaultFileMenuItems } from './file-menu-items'; export class ImageDef extends FileDef { static displayName = 'Image'; @@ -2802,197 +2704,10 @@ export class ImageDef extends FileDef { @field width = contains(NumberField); @field height = contains(NumberField); - static isolated: BaseDefComponent = class Isolated extends Component< - typeof this - > { - - }; - static atom: BaseDefComponent = class Atom extends Component { - - }; - static embedded: BaseDefComponent = class Embedded extends Component< - typeof this - > { - - }; - static fitted: BaseDefComponent = class Fitted extends Component< - typeof this - > { - get backgroundImageStyle() { - if (this.args.model.url) { - return `background-image: url(${this.args.model.url});`; - } - return undefined; - } - - - }; + static isolated: BaseDefComponent = ImageDefIsolatedTemplate; + static atom: BaseDefComponent = ImageDefAtomTemplate; + static embedded: BaseDefComponent = ImageDefEmbeddedTemplate; + static fitted: BaseDefComponent = ImageDefFittedTemplate; } export class CardInfoField extends FieldDef { diff --git a/packages/base/default-templates/file-def-edit.gts b/packages/base/default-templates/file-def-edit.gts new file mode 100644 index 00000000000..3b99ff3cfc8 --- /dev/null +++ b/packages/base/default-templates/file-def-edit.gts @@ -0,0 +1,27 @@ +import GlimmerComponent from '@glimmer/component'; +import { concat } from '@ember/helper'; +import type { FileDef } from '../card-api'; + +export default class FileDefEditTemplate extends GlimmerComponent<{ + Args: { + model: FileDef; + }; +}> { + +} diff --git a/packages/base/default-templates/image-def-atom.gts b/packages/base/default-templates/image-def-atom.gts new file mode 100644 index 00000000000..a095504dbe4 --- /dev/null +++ b/packages/base/default-templates/image-def-atom.gts @@ -0,0 +1,41 @@ +import GlimmerComponent from '@glimmer/component'; +import type { ImageDef } from '../card-api'; + +export default class ImageDefAtomTemplate extends GlimmerComponent<{ + Args: { + model: ImageDef; + }; +}> { + +} diff --git a/packages/base/default-templates/image-def-embedded.gts b/packages/base/default-templates/image-def-embedded.gts new file mode 100644 index 00000000000..f1e75fc300b --- /dev/null +++ b/packages/base/default-templates/image-def-embedded.gts @@ -0,0 +1,35 @@ +import GlimmerComponent from '@glimmer/component'; +import type { ImageDef } from '../card-api'; + +export default class ImageDefEmbeddedTemplate extends GlimmerComponent<{ + Args: { + model: ImageDef; + }; +}> { + +} diff --git a/packages/base/default-templates/image-def-fitted.gts b/packages/base/default-templates/image-def-fitted.gts new file mode 100644 index 00000000000..ec785c5f612 --- /dev/null +++ b/packages/base/default-templates/image-def-fitted.gts @@ -0,0 +1,62 @@ +import GlimmerComponent from '@glimmer/component'; + +import setBackgroundImage from '../helpers/set-background-image'; +import type { ImageDef } from '../card-api'; + +export default class ImageDefFittedTemplate extends GlimmerComponent<{ + Args: { + model: ImageDef; + }; +}> { + +} diff --git a/packages/base/default-templates/image-def-isolated.gts b/packages/base/default-templates/image-def-isolated.gts new file mode 100644 index 00000000000..1c7d281fee3 --- /dev/null +++ b/packages/base/default-templates/image-def-isolated.gts @@ -0,0 +1,66 @@ +import GlimmerComponent from '@glimmer/component'; +import type { ImageDef } from '../card-api'; + +export default class ImageDefIsolatedTemplate extends GlimmerComponent<{ + Args: { + model: ImageDef; + }; +}> { + +} diff --git a/packages/base/file-menu-items.ts b/packages/base/file-menu-items.ts new file mode 100644 index 00000000000..6ea72f10480 --- /dev/null +++ b/packages/base/file-menu-items.ts @@ -0,0 +1,92 @@ +import { + copyCardURLToClipboard, + type MenuItemOptions, +} from '@cardstack/boxel-ui/helpers'; + +import ArrowLeft from '@cardstack/boxel-icons/arrow-left'; +import CodeIcon from '@cardstack/boxel-icons/code'; +import Eye from '@cardstack/boxel-icons/eye'; +import LinkIcon from '@cardstack/boxel-icons/link'; + +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 type { FileDef } from './card-api'; +import type { GetMenuItemParams } from './menu-items'; + +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 () => { + let { 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; +} From d40bb73cdaaf644d0e39e9568b3e5f3560d4b3e9 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 17:30:39 +0800 Subject: [PATCH 09/12] fix host test --- packages/base/brand-guide.gts | 6 +- packages/base/card-api.gts | 9 +- packages/base/default-templates/head.gts | 5 +- .../tests/integration/realm-indexing-test.gts | 104 ++++++++---------- .../host/tests/integration/realm-test.gts | 11 +- 5 files changed, 65 insertions(+), 70 deletions(-) diff --git a/packages/base/brand-guide.gts b/packages/base/brand-guide.gts index 4d091d2cf17..f8b1a250e8f 100644 --- a/packages/base/brand-guide.gts +++ b/packages/base/brand-guide.gts @@ -1081,8 +1081,10 @@ export default class BrandGuide extends DetailedStyleRef { @field cardThumbnailURL = contains(StringField, { computeVia: function (this: BrandGuide) { - return this.cardInfo?.cardThumbnailURL?.length - ? this.cardInfo?.cardThumbnailURL + let thumbnailURL = + this.cardInfo?.cardThumbnail?.url ?? this.cardInfo?.cardThumbnailURL; + return thumbnailURL?.length + ? thumbnailURL : this.markUsage?.socialMediaProfileIcon; }, }); diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index afec66f066d..3d7dea9f99f 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -2714,7 +2714,7 @@ export class CardInfoField extends FieldDef { static displayName = 'Card Info'; @field name = contains(StringField); @field summary = contains(StringField); - @field cardThumbnail = linksTo(ImageDef); + @field cardThumbnail = linksTo(() => ImageDef); @field cardThumbnailURL = contains(MaybeBase64Field); @field theme = linksTo(() => Theme); @field notes = contains(MarkdownField); @@ -2758,7 +2758,7 @@ export class CardDef extends BaseDef { // to use it directly now (or wait until a better image field comes along) @field cardThumbnailURL = contains(MaybeBase64Field, { computeVia: function (this: CardDef) { - return this.cardInfo.cardThumbnail?.url ?? this.cardInfo.cardThumbnailURL; + return this.cardInfo.cardThumbnailURL; }, }); static displayName = 'Card'; @@ -3053,10 +3053,7 @@ 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/default-templates/head.gts b/packages/base/default-templates/head.gts index 75a75bc178d..6a93fbdb0db 100644 --- a/packages/base/default-templates/head.gts +++ b/packages/base/default-templates/head.gts @@ -19,7 +19,10 @@ export default class DefaultHeadTemplate extends GlimmerComponent<{ } get image(): string | undefined { - return this.args.model?.cardThumbnailURL; + return ( + this.args.model?.cardInfo?.cardThumbnail?.url ?? + this.args.model?.cardThumbnailURL + ); } get themeIcon(): string | undefined { diff --git a/packages/host/tests/integration/realm-indexing-test.gts b/packages/host/tests/integration/realm-indexing-test.gts index 9cee5b082b3..a769d4edd8a 100644 --- a/packages/host/tests/integration/realm-indexing-test.gts +++ b/packages/host/tests/integration/realm-indexing-test.gts @@ -146,7 +146,6 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -172,7 +171,7 @@ module(`Integration | realm indexing`, function (hooks) { resourceCreatedAt: await getFileCreatedAt(realm, 'empty.json'), }, links: { - self: `./empty`, + self: `${testRealmURL}empty`, }, }, ]); @@ -417,7 +416,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -517,7 +515,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -575,7 +572,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -672,7 +668,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -770,7 +765,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -864,7 +858,6 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -907,7 +900,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -995,7 +988,6 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -1038,7 +1030,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -1074,7 +1066,6 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: skillCardRef, @@ -1112,7 +1103,7 @@ module(`Integration | realm indexing`, function (hooks) { cardTitle: 'Switch Submode', }, ], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -2110,7 +2101,6 @@ module(`Integration | realm indexing`, function (hooks) { links: { self: `./mango` }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }); } else { assert.ok( @@ -2250,7 +2240,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2295,8 +2284,8 @@ module(`Integration | realm indexing`, function (hooks) { cardInfo, }, relationships: { - 'cardInfo.theme': { links: { self: null } }, 'cardInfo.cardThumbnail': { links: { self: null } }, + 'cardInfo.theme': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2337,8 +2326,8 @@ module(`Integration | realm indexing`, function (hooks) { cardInfo, }, relationships: { - 'cardInfo.theme': { links: { self: null } }, 'cardInfo.cardThumbnail': { links: { self: null } }, + 'cardInfo.theme': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2488,6 +2477,7 @@ module(`Integration | realm indexing`, function (hooks) { posts: 100, cardTitle: 'Hassan Abdel-Rahman', cardDescription: 'Person', + cardThumbnailURL: null, fullName: 'Hassan Abdel-Rahman', _cardType: 'Person', cardInfo: { cardThumbnail: null, theme: null }, @@ -2575,6 +2565,7 @@ module(`Integration | realm indexing`, function (hooks) { }, views: 5, cardInfo: { cardThumbnail: null, theme: null }, + cardThumbnailURL: null, }, `post 1 search doc includes publication relationship`, ); @@ -2595,9 +2586,8 @@ module(`Integration | realm indexing`, function (hooks) { fullName: ' ', cardTitle: ' ', }, - cardInfo: { - theme: null, - }, + cardInfo: { cardThumbnail: null, theme: null }, + cardThumbnailURL: null, id: `${testRealmURL}Post/1`, publication: { id: `${testRealmURL}Publication/pacific`, @@ -2611,9 +2601,8 @@ module(`Integration | realm indexing`, function (hooks) { fullName: ' ', cardTitle: ' ', }, - cardInfo: { - theme: null, - }, + cardInfo: { cardThumbnail: null, theme: null }, + cardThumbnailURL: null, id: `${testRealmURL}Post/2`, publication: { id: `${testRealmURL}Publication/pacific`, @@ -2623,6 +2612,7 @@ module(`Integration | realm indexing`, function (hooks) { }, ], cardInfo: { cardThumbnail: null, theme: null }, + cardThumbnailURL: null, }, `publication search doc includes featuredPosts relationship via isUsed=true`, ); @@ -2716,7 +2706,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); // we should be able to perform a structured clone of the search doc (this // emulates the limitations of the postMessage used to communicate between @@ -2805,7 +2795,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { id: `${testRealmURL}Pet/vanGogh`, type: 'card' }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2849,7 +2838,6 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { owner: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { module: `${testModuleRealm}pet`, name: 'Pet' }, @@ -2886,7 +2874,6 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { owner: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { module: `${testModuleRealm}pet`, name: 'Pet' }, @@ -2949,7 +2936,7 @@ module(`Integration | realm indexing`, function (hooks) { cardTitle: 'Hassan Pet Person', cardDescription: 'A person with pets', cardThumbnailURL: null, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -2969,7 +2956,6 @@ module(`Integration | realm indexing`, function (hooks) { relationships: { pets: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -2990,6 +2976,7 @@ module(`Integration | realm indexing`, function (hooks) { ); if (card?.type === 'doc') { + // debug assert.deepEqual(card.doc, { data: { id: `${testRealmURL}PetPerson/burcu`, @@ -3006,7 +2993,6 @@ module(`Integration | realm indexing`, function (hooks) { pets: { links: { self: null } }, friend: { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3057,7 +3043,7 @@ module(`Integration | realm indexing`, function (hooks) { cardTitle: 'Burcu Pet Person', cardDescription: 'A person with pets', cardThumbnailURL: null, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3146,7 +3132,6 @@ module(`Integration | realm indexing`, function (hooks) { }, relationships: { 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, linkedExamples: { links: { self: null } }, }, meta: { @@ -3201,7 +3186,7 @@ module(`Integration | realm indexing`, function (hooks) { isCard: true, isComponent: false, isField: false, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3313,7 +3298,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3368,9 +3352,9 @@ module(`Integration | realm indexing`, function (hooks) { friend: { id: `${testRealmURL}Friend/vanGogh`, }, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3465,7 +3449,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3517,7 +3500,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3573,10 +3555,10 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Friend/hassan`, }, cardDescription: 'Dog friend', - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, cardTitle: 'Hassan', - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3615,7 +3597,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3667,7 +3648,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3726,7 +3706,7 @@ module(`Integration | realm indexing`, function (hooks) { cardDescription: 'Dog owner', cardInfo: { cardThumbnail: null, theme: null }, }, - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3795,7 +3775,6 @@ module(`Integration | realm indexing`, function (hooks) { }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: { @@ -3846,7 +3825,7 @@ module(`Integration | realm indexing`, function (hooks) { id: `${testRealmURL}Friend/hassan`, }, cardTitle: 'Hassan', - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }); } else { assert.ok( @@ -3950,7 +3929,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -3997,7 +3975,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4038,7 +4015,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4087,17 +4063,17 @@ module(`Integration | realm indexing`, function (hooks) { firstName: 'Mango', cardTitle: 'Mango', friends: [{ id: hassanID }], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, { id: vanGoghID, firstName: 'Van Gogh', friends: [{ id: hassanID }], cardTitle: 'Van Gogh', - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, ], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, 'hassan searchData is correct', ); @@ -4128,7 +4104,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4178,7 +4153,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4219,7 +4193,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4278,13 +4251,13 @@ module(`Integration | realm indexing`, function (hooks) { id: hassanID, }, ], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, ], cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, 'mango searchData is correct', ); @@ -4315,7 +4288,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4365,7 +4337,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: vanGoghID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4406,7 +4377,6 @@ module(`Integration | realm indexing`, function (hooks) { data: { type: 'card', id: hassanID }, }, 'cardInfo.theme': { links: { self: null } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, }, meta: { adoptsFrom: friendsRef, @@ -4457,6 +4427,7 @@ module(`Integration | realm indexing`, function (hooks) { friends: [ { cardInfo: { + cardThumbnail: null, theme: null, }, firstName: 'Mango', @@ -4473,7 +4444,7 @@ module(`Integration | realm indexing`, function (hooks) { cardInfo: { cardThumbnail: null, theme: null }, }, ], - cardInfo: { cardThumbnail: null, theme: null }, + cardInfo: { theme: null }, }, 'vanGogh searchData is correct', ); @@ -4538,13 +4509,19 @@ module(`Integration | realm indexing`, function (hooks) { 'https://cardstack.com/base/default-templates/card-info', 'https://cardstack.com/base/default-templates/embedded', 'https://cardstack.com/base/default-templates/field-edit', + 'https://cardstack.com/base/default-templates/file-def-edit', 'https://cardstack.com/base/default-templates/fitted', 'https://cardstack.com/base/default-templates/head', + 'https://cardstack.com/base/default-templates/image-def-atom', + 'https://cardstack.com/base/default-templates/image-def-embedded', + 'https://cardstack.com/base/default-templates/image-def-fitted', + 'https://cardstack.com/base/default-templates/image-def-isolated', 'https://cardstack.com/base/default-templates/isolated-and-edit', 'https://cardstack.com/base/default-templates/markdown', 'https://cardstack.com/base/default-templates/missing-template', 'https://cardstack.com/base/field-component', 'https://cardstack.com/base/field-support', + 'https://cardstack.com/base/file-menu-items', 'https://cardstack.com/base/helpers/sanitized-html', 'https://cardstack.com/base/helpers/set-background-image', 'https://cardstack.com/base/links-to-editor', @@ -4654,6 +4631,7 @@ module(`Integration | realm indexing`, function (hooks) { 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/folder-pen', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/git-branch', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/hash', + 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/image', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/import', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/layers-subtract', 'http://localhost:4206/@cardstack/boxel-icons/v1/icons/layout-list', @@ -4677,13 +4655,19 @@ module(`Integration | realm indexing`, function (hooks) { 'https://cardstack.com/base/default-templates/card-info', 'https://cardstack.com/base/default-templates/embedded', 'https://cardstack.com/base/default-templates/field-edit', + 'https://cardstack.com/base/default-templates/file-def-edit', 'https://cardstack.com/base/default-templates/fitted', 'https://cardstack.com/base/default-templates/head', + 'https://cardstack.com/base/default-templates/image-def-atom', + 'https://cardstack.com/base/default-templates/image-def-embedded', + 'https://cardstack.com/base/default-templates/image-def-fitted', + 'https://cardstack.com/base/default-templates/image-def-isolated', 'https://cardstack.com/base/default-templates/isolated-and-edit', 'https://cardstack.com/base/default-templates/markdown', 'https://cardstack.com/base/default-templates/missing-template', 'https://cardstack.com/base/field-component', 'https://cardstack.com/base/field-support', + 'https://cardstack.com/base/file-menu-items', 'https://cardstack.com/base/helpers/sanitized-html', 'https://cardstack.com/base/helpers/set-background-image', 'https://cardstack.com/base/links-to-editor', diff --git a/packages/host/tests/integration/realm-test.gts b/packages/host/tests/integration/realm-test.gts index f2794734e71..a49f4b59ad4 100644 --- a/packages/host/tests/integration/realm-test.gts +++ b/packages/host/tests/integration/realm-test.gts @@ -254,6 +254,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { @@ -374,7 +375,10 @@ module('Integration | realm', function (hooks) { cardTitle: 'Hassan Abdel-Rahman', cardInfo, }, - relationships: { 'cardInfo.theme': { links: { self: null } } }, + relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, + 'cardInfo.theme': { links: { self: null } }, + }, meta: { adoptsFrom: { module: 'http://localhost:4202/test/person', @@ -766,6 +770,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { @@ -975,6 +980,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { @@ -1328,6 +1334,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { @@ -2310,6 +2317,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { @@ -3203,6 +3211,7 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { From a13a4967aabd8ce911ba4c4fbbd6b17870482861 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 18:46:31 +0800 Subject: [PATCH 10/12] fix host and realm test --- .../acceptance/code-submode/editor-test.ts | 5 - .../tests/acceptance/prerender-meta-test.gts | 61 +++----- .../commands/patch-instance-test.gts | 4 - .../host/tests/integration/realm-test.gts | 1 - .../realm-server/tests/card-endpoints-test.ts | 140 ++++++++++++++++++ .../tests/card-source-endpoints-test.ts | 5 + packages/realm-server/tests/indexing-test.ts | 7 + .../tests/realm-endpoints-test.ts | 5 + 8 files changed, 181 insertions(+), 47 deletions(-) diff --git a/packages/host/tests/acceptance/code-submode/editor-test.ts b/packages/host/tests/acceptance/code-submode/editor-test.ts index 8d10ce206d8..508af2711d9 100644 --- a/packages/host/tests/acceptance/code-submode/editor-test.ts +++ b/packages/host/tests/acceptance/code-submode/editor-test.ts @@ -527,11 +527,6 @@ module('Acceptance | code submode | editor tests', function (hooks) { }, }, relationships: { - 'cardInfo.cardThumbnail': { - links: { - self: null, - }, - }, 'cardInfo.theme': { links: { self: null, diff --git a/packages/host/tests/acceptance/prerender-meta-test.gts b/packages/host/tests/acceptance/prerender-meta-test.gts index c5b51a0d96e..2e4a7001be0 100644 --- a/packages/host/tests/acceptance/prerender-meta-test.gts +++ b/packages/host/tests/acceptance/prerender-meta-test.gts @@ -346,9 +346,7 @@ module('Acceptance | prerender | meta', function (hooks) { { id: `${testRealmURL}Pet/mango`, _cardType: 'Pet', - cardInfo: { - cardThumbnail: null, - }, + cardInfo: {}, name: 'Mango', cardTitle: 'Mango', }, @@ -366,9 +364,7 @@ module('Acceptance | prerender | meta', function (hooks) { { id: `${testRealmURL}Pet/paper`, _cardType: 'Cat', - cardInfo: { - cardThumbnail: null, - }, + cardInfo: {}, name: 'Paper', cardTitle: 'Paper', aliases: ['Satan', "Satan's Mistress"], @@ -391,7 +387,6 @@ module('Acceptance | prerender | meta', function (hooks) { id: `${testRealmURL}Person/jade`, _cardType: 'Person', cardInfo: { - cardThumbnail: null, theme: null, }, name: 'Jade', @@ -401,7 +396,6 @@ module('Acceptance | prerender | meta', function (hooks) { friend: { id: `${testRealmURL}Person/hassan`, cardInfo: { - cardThumbnail: null, theme: null, }, name: 'Hassan', @@ -436,7 +430,6 @@ module('Acceptance | prerender | meta', function (hooks) { id: `${testRealmURL}Person/hassan`, _cardType: 'Person', cardInfo: { - cardThumbnail: null, theme: null, }, name: 'Hassan', @@ -444,36 +437,33 @@ module('Acceptance | prerender | meta', function (hooks) { friend: null, numOfPets: '3', pets: [ - { - id: `${testRealmURL}Pet/mango`, - name: 'Mango', - cardTitle: 'Mango', - cardInfo: { - cardThumbnail: null, - theme: null, + { + id: `${testRealmURL}Pet/mango`, + name: 'Mango', + cardTitle: 'Mango', + cardInfo: { + theme: null, + }, }, - }, - { - id: `${testRealmURL}Pet/vangogh`, - name: 'Van Gogh', - cardTitle: 'Van Gogh', - cardInfo: { - cardThumbnail: null, - theme: null, + { + id: `${testRealmURL}Pet/vangogh`, + name: 'Van Gogh', + cardTitle: 'Van Gogh', + cardInfo: { + theme: null, + }, }, - }, - { - id: `${testRealmURL}Pet/paper`, + { + id: `${testRealmURL}Pet/paper`, name: 'Paper', cardTitle: 'Paper', - aliases: ['Satan', "Satan's Mistress"], - emergencyContacts: null, - cardInfo: { - cardThumbnail: null, - theme: null, + aliases: ['Satan', "Satan's Mistress"], + emergencyContacts: null, + cardInfo: { + theme: null, + }, }, - }, - ], + ], }, 'search doc is correct', ); @@ -491,7 +481,6 @@ module('Acceptance | prerender | meta', function (hooks) { _cardType: 'Cat', aliases: null, cardInfo: { - cardThumbnail: null, theme: null, }, emergencyContacts: [ @@ -504,7 +493,6 @@ module('Acceptance | prerender | meta', function (hooks) { numOfPets: '0', pets: null, cardInfo: { - cardThumbnail: null, theme: null, }, friend: { @@ -519,7 +507,6 @@ module('Acceptance | prerender | meta', function (hooks) { name: 'Hassan', cardTitle: 'Hassan', cardInfo: { - cardThumbnail: null, theme: null, }, numOfPets: '3', diff --git a/packages/host/tests/integration/commands/patch-instance-test.gts b/packages/host/tests/integration/commands/patch-instance-test.gts index ca46595eed5..b52a7abea47 100644 --- a/packages/host/tests/integration/commands/patch-instance-test.gts +++ b/packages/host/tests/integration/commands/patch-instance-test.gts @@ -139,7 +139,6 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, - 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -210,7 +209,6 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, - 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -281,7 +279,6 @@ module('Integration | commands | patch-instance', function (hooks) { self: null, }, }, - 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', @@ -350,7 +347,6 @@ module('Integration | commands | patch-instance', function (hooks) { }, 'friends.0': { links: { self: `./germaine` } }, 'friends.1': { links: { self: `./queenzy` } }, - 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, 'the relationships are correct', diff --git a/packages/host/tests/integration/realm-test.gts b/packages/host/tests/integration/realm-test.gts index a49f4b59ad4..6f213461c4e 100644 --- a/packages/host/tests/integration/realm-test.gts +++ b/packages/host/tests/integration/realm-test.gts @@ -376,7 +376,6 @@ module('Integration | realm', function (hooks) { cardInfo, }, relationships: { - 'cardInfo.cardThumbnail': { links: { self: null } }, 'cardInfo.theme': { links: { self: null } }, }, meta: { diff --git a/packages/realm-server/tests/card-endpoints-test.ts b/packages/realm-server/tests/card-endpoints-test.ts index ffa4bc71601..67fe68f3d50 100644 --- a/packages/realm-server/tests/card-endpoints-test.ts +++ b/packages/realm-server/tests/card-endpoints-test.ts @@ -182,6 +182,11 @@ module(basename(__filename), function () { cardThumbnailURL: null, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -927,6 +932,11 @@ module(basename(__filename), function () { realmURL: testRealmHref, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1488,6 +1498,11 @@ module(basename(__filename), function () { id: `${testRealmHref}Friend/local-id-1`, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1551,6 +1566,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1580,6 +1600,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1609,6 +1634,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1675,6 +1705,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1720,6 +1755,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1749,6 +1789,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1798,6 +1843,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -1850,6 +1900,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2064,6 +2119,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2243,6 +2303,11 @@ module(basename(__filename), function () { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2597,6 +2662,11 @@ module(basename(__filename), function () { self: './Friend/local-id-1', }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2748,6 +2818,11 @@ module(basename(__filename), function () { id: `${testRealmHref}Friend/local-id-1`, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2811,6 +2886,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2840,6 +2920,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2869,6 +2954,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2935,6 +3025,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -2980,6 +3075,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3009,6 +3109,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3058,6 +3163,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3110,6 +3220,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3228,6 +3343,11 @@ module(basename(__filename), function () { self: './FriendWithUsedLink/local-id-1', }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3311,6 +3431,11 @@ module(basename(__filename), function () { id: `${testRealmHref}FriendWithUsedLink/local-id-1`, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3357,6 +3482,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3406,6 +3536,11 @@ module(basename(__filename), function () { self: null, }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -3504,6 +3639,11 @@ module(basename(__filename), function () { friend: { links: { self: './jade' }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, diff --git a/packages/realm-server/tests/card-source-endpoints-test.ts b/packages/realm-server/tests/card-source-endpoints-test.ts index 7b089a924e7..15ce9d56ff5 100644 --- a/packages/realm-server/tests/card-source-endpoints-test.ts +++ b/packages/realm-server/tests/card-source-endpoints-test.ts @@ -897,6 +897,11 @@ module(basename(__filename), function () { cardInfo, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, diff --git a/packages/realm-server/tests/indexing-test.ts b/packages/realm-server/tests/indexing-test.ts index 0abcb8c6075..2ee35f202b7 100644 --- a/packages/realm-server/tests/indexing-test.ts +++ b/packages/realm-server/tests/indexing-test.ts @@ -740,6 +740,11 @@ module(basename(__filename), function () { self: './ringo', }, }, + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, @@ -766,6 +771,7 @@ module(basename(__filename), function () { cardTitle: 'Untitled Card', firstName: 'Ringo', cardInfo: { + cardThumbnail: null, theme: null, }, }, @@ -774,6 +780,7 @@ module(basename(__filename), function () { firstName: 'Hassan', cardTitle: 'Untitled Card', cardInfo: { + cardThumbnail: null, theme: null, }, }, diff --git a/packages/realm-server/tests/realm-endpoints-test.ts b/packages/realm-server/tests/realm-endpoints-test.ts index 4a9a0a05202..75ad9dae1bb 100644 --- a/packages/realm-server/tests/realm-endpoints-test.ts +++ b/packages/realm-server/tests/realm-endpoints-test.ts @@ -1149,6 +1149,11 @@ module(basename(__filename), function () { self: newCardId, }, relationships: { + 'cardInfo.cardThumbnail': { + links: { + self: null, + }, + }, 'cardInfo.theme': { links: { self: null, From c24d798eb5f8b4af45764274c31d4f9fc3d258b8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 18:54:02 +0800 Subject: [PATCH 11/12] fix pretitier --- .../tests/acceptance/prerender-meta-test.gts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/host/tests/acceptance/prerender-meta-test.gts b/packages/host/tests/acceptance/prerender-meta-test.gts index 2e4a7001be0..2ea9ccbe899 100644 --- a/packages/host/tests/acceptance/prerender-meta-test.gts +++ b/packages/host/tests/acceptance/prerender-meta-test.gts @@ -437,33 +437,33 @@ module('Acceptance | prerender | meta', function (hooks) { friend: null, numOfPets: '3', pets: [ - { - id: `${testRealmURL}Pet/mango`, - name: 'Mango', - cardTitle: 'Mango', - cardInfo: { - theme: null, - }, + { + id: `${testRealmURL}Pet/mango`, + name: 'Mango', + cardTitle: 'Mango', + cardInfo: { + theme: null, }, - { - id: `${testRealmURL}Pet/vangogh`, - name: 'Van Gogh', - cardTitle: 'Van Gogh', - cardInfo: { - theme: null, - }, + }, + { + id: `${testRealmURL}Pet/vangogh`, + name: 'Van Gogh', + cardTitle: 'Van Gogh', + cardInfo: { + theme: null, }, - { - id: `${testRealmURL}Pet/paper`, + }, + { + id: `${testRealmURL}Pet/paper`, name: 'Paper', cardTitle: 'Paper', - aliases: ['Satan', "Satan's Mistress"], - emergencyContacts: null, - cardInfo: { - theme: null, - }, + aliases: ['Satan', "Satan's Mistress"], + emergencyContacts: null, + cardInfo: { + theme: null, }, - ], + }, + ], }, 'search doc is correct', ); From de855d61fa06986cdfb8317728114ef4186e7d65 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Apr 2026 22:07:24 +0800 Subject: [PATCH 12/12] fix indexing test --- packages/realm-server/tests/indexing-test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/realm-server/tests/indexing-test.ts b/packages/realm-server/tests/indexing-test.ts index 2ee35f202b7..d19ec874979 100644 --- a/packages/realm-server/tests/indexing-test.ts +++ b/packages/realm-server/tests/indexing-test.ts @@ -771,7 +771,6 @@ module(basename(__filename), function () { cardTitle: 'Untitled Card', firstName: 'Ringo', cardInfo: { - cardThumbnail: null, theme: null, }, },