Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2dff1d3
test: Add failing test for theme-based favicon/webclip in default hea…
backspace Feb 17, 2026
89e56c3
base: Emit favicon and apple-touch-icon from cardInfo.theme in defaul…
backspace Feb 17, 2026
08bfad4
Add lint autofixes
backspace Feb 17, 2026
a046fe6
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 17, 2026
2c8c66f
Fix test
backspace Feb 17, 2026
73ed8da
Add temporary fallbacks
backspace Feb 17, 2026
aa371fa
Remove hardcoded icons for now
backspace Feb 17, 2026
759ae6b
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 18, 2026
3c40104
Fix linksTo inside contains: propagate store to child FieldDef instances
backspace Feb 19, 2026
14ef4d4
Add diagnostic meta tag to debug theme icon resolution in CI
backspace Feb 19, 2026
1a3c9e0
Remove hardcoded fallback icons from head template, use static icons …
backspace Feb 19, 2026
7e4a93e
Fix theme icon resolution: add linked card deps + fix test indexing o…
backspace Feb 19, 2026
608503e
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 19, 2026
8201b28
Fix theme test: create card-with-theme via API for incremental indexing
backspace Feb 19, 2026
dedb4b3
Touch nested linksTo fields before store.loaded() during prerender
backspace Feb 19, 2026
3fc0d04
Add diagnostic output to theme test for CI debugging
backspace Feb 19, 2026
a690406
Fix nested linksTo deserialization for card-source format
backspace Feb 19, 2026
d61d1de
Fix prettier formatting in render.ts
backspace Feb 19, 2026
b425d82
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 19, 2026
70dbaa1
Add expanded diagnostics for theme head_html debugging
backspace Feb 19, 2026
017e1cb
Fix diagnostic query: use pristine_doc column instead of non-existent…
backspace Feb 20, 2026
18118c3
Add render route diagnostics for touchNestedLinksToFields + fix prist…
backspace Feb 20, 2026
4fa0197
Add cardInfo to test card attributes to bypass nestedRelFieldStubs path
backspace Feb 20, 2026
ee618a3
Remove diagnostics and broken nestedRelFieldStubs code
backspace Feb 20, 2026
2aee52a
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 20, 2026
b03f448
Fix theme head_html: reset model state for format transitions
backspace Feb 20, 2026
862919e
Fix indexing-test timeout: only reset model state for render.html tra…
backspace Feb 20, 2026
0539a90
Merge branch 'main' into head-format-theme-cs-9828
backspace Feb 20, 2026
145a26d
Fix prerendering timeout by pre-loading cardInfo.theme during model b…
backspace Feb 20, 2026
919e39c
Remove cardInfo.theme pre-load, keep resetModelForFormatTransition re…
backspace Feb 20, 2026
1acc412
Merge remote-tracking branch 'origin/main' into head-format-theme-cs-…
backspace Feb 20, 2026
a7b3bc9
Re-add resetModelForFormatTransition with nonce guard for page reuse
backspace Feb 21, 2026
ebcb96d
Replace resetModelForFormatTransition with cardInfo.theme pre-load
backspace Feb 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions packages/base/card-api.gts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,16 @@ class ContainsMany<FieldT extends FieldDefConstructor> implements Field<
}
let results = getter(instance, this);
propagateRealmContext(results, instance);
// Propagate the store from the parent instance to child FieldDef values
// so that nested linksTo fields can properly track their loads
let parentStore = stores.get(instance);
if (parentStore && Array.isArray(results)) {
for (let item of results) {
if (isCardOrField(item) && !stores.has(item)) {
stores.set(item, parentStore);
}
}
}
return results;
}

Expand Down Expand Up @@ -836,6 +846,14 @@ class Contains<CardT extends FieldDefConstructor> implements Field<CardT, any> {
}
let value = getter(instance, this);
propagateRealmContext(value, instance);
// Propagate the store from the parent instance to child FieldDef values
// so that nested linksTo fields can properly track their loads
if (isCardOrField(value)) {
let parentStore = stores.get(instance);
if (parentStore && !stores.has(value)) {
stores.set(value, parentStore);
}
}
return value;
}

Expand Down Expand Up @@ -1340,9 +1358,7 @@ class LinksTo<CardT extends LinkableDefConstructor> implements Field<CardT> {
<CardCrudFunctionsConsumer as |cardCrudFunctions|>
<DefaultFormatsConsumer as |defaultFormats|>
{{#if
(shouldRenderEditor
@format defaultFormats.cardDef isComputed
)
(shouldRenderEditor @format defaultFormats.cardDef isComputed)
}}
<LinksToEditor
@model={{(getInnerModel)}}
Expand Down
9 changes: 9 additions & 0 deletions packages/base/default-templates/head.gts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export default class DefaultHeadTemplate extends GlimmerComponent<{
return this.args.model?.cardThumbnailURL;
}

get themeIcon(): string | undefined {
return this.args.model?.cardInfo?.theme?.cardThumbnailURL;
}

<template>
{{! template-lint-disable no-forbidden-elements }}
<title data-test-card-head-title>{{this.title}}</title>
Expand All @@ -43,6 +47,11 @@ export default class DefaultHeadTemplate extends GlimmerComponent<{
<meta name='twitter:card' content='summary' />
{{/if}}

{{#if this.themeIcon}}
<link rel='icon' href={{this.themeIcon}} />
<link rel='apple-touch-icon' href={{this.themeIcon}} />
{{/if}}

<meta property='og:type' content='website' />
</template>
}
92 changes: 47 additions & 45 deletions packages/host/app/index.html
Original file line number Diff line number Diff line change
@@ -1,48 +1,50 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

{{content-for "head"}}

<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css" />
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/@cardstack/host.css" />
<link href="{{rootURL}}boxel-favicon.png" rel="shortcut icon" type="image/x-icon" />
<link href="{{rootURL}}boxel-webclip.png" rel="apple-touch-icon" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Serif:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet" />

{{content-for "head-footer"}}

<meta data-boxel-head-start />
<title>Boxel</title>
<meta data-boxel-head-end />

</head>

<body>
<script type="x/boundary" id="boxel-isolated-start"></script>
<script type="x/boundary" id="boxel-isolated-end"></script>

<!-- in case embercli's hooks insn't run,
<head>
<meta charset="utf-8" />
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

{{content-for "head"}}

<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css" />
<link
integrity=""
rel="stylesheet"
href="{{rootURL}}assets/@cardstack/host.css"
/>
<link href="{{rootURL}}boxel-favicon.png" rel="icon" type="image/png" />
<link href="{{rootURL}}boxel-webclip.png" rel="apple-touch-icon" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Serif:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>

{{content-for "head-footer"}}

<meta data-boxel-head-start />
<title>Boxel</title>
<meta data-boxel-head-end />
</head>

<body>
<script type="x/boundary" id="boxel-isolated-start"></script>
<script type="x/boundary" id="boxel-isolated-end"></script>

<!-- in case embercli's hooks insn't run,
we embed the following div manually -->
<div id="ember-basic-dropdown-wormhole"></div>
{{content-for "body"}}

<script type="module">
import * as ContentTag from '{{rootURL}}assets/content-tag/standalone.js';
globalThis.ContentTagGlobal = ContentTag;
</script>
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/@cardstack/host.js"></script>

{{content-for "body-footer"}}
</body>

</html>
<div id="ember-basic-dropdown-wormhole"></div>
{{content-for "body"}}

<script type="module">
import * as ContentTag from '{{rootURL}}assets/content-tag/standalone.js';
globalThis.ContentTagGlobal = ContentTag;
</script>
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/@cardstack/host.js"></script>

{{content-for "body-footer"}}
</body>
</html>
8 changes: 8 additions & 0 deletions packages/host/app/routes/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ export default class RenderRoute extends Route<Model> {
}
if (instance) {
await this.#authGuard.race(() => this.#touchIsUsedFields(instance));
// Pre-load cardInfo.theme so the head format template can render
// favicon/apple-touch-icon links. This is a nested linksTo inside a
// contains FieldDef, so #touchIsUsedFields doesn't reach it.
try {
(instance as any).cardInfo?.theme;
} catch {
// ignore — card may not have cardInfo or theme
}
}
await this.#authGuard.race(() => this.store.loaded());
if (instance) {
Expand Down
13 changes: 11 additions & 2 deletions packages/host/tests/acceptance/prerender-meta-test.gts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ module('Acceptance | prerender | meta', function (hooks) {
numOfPets: '3',
},
relationships: {
'cardInfo.theme': {
links: {
self: null,
},
},
'pets.0': {
links: {
self: '../Pet/mango',
Expand Down Expand Up @@ -346,7 +351,9 @@ module('Acceptance | prerender | meta', function (hooks) {
{
id: `${testRealmURL}Pet/mango`,
_cardType: 'Pet',
cardInfo: {},
cardInfo: {
theme: null,
},
name: 'Mango',
cardTitle: 'Mango',
},
Expand All @@ -364,7 +371,9 @@ module('Acceptance | prerender | meta', function (hooks) {
{
id: `${testRealmURL}Pet/paper`,
_cardType: 'Cat',
cardInfo: {},
cardInfo: {
theme: null,
},
name: 'Paper',
cardTitle: 'Paper',
aliases: ['Satan', "Satan's Mistress"],
Expand Down
Loading
Loading