From c0528d938fd56c1b764d6117e36f67a495a4fcb7 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Wed, 22 Jul 2026 17:04:05 -0400 Subject: [PATCH 01/51] Fixing some latent type errors While working on removing the standalone declarations build in boxel-ui, we discovered that there are many pre-existing type errors that were being hidden behind skipLibCheck. Once you start type-checking boxel-ui directly against source, it's no longer hidden behind skipLibCheck and these errors surface. This is just a partial set of them, more need to be fixed before we can actually stop pre-building the boxel-ui declarations. --- .../src/components/circle-spinner/index.gts | 2 +- .../src/components/copy-button/index.gts | 5 +++- .../ai-assistant/code-block/actions.gts | 7 ++++- .../ai-assistant/message/aibot-message.gts | 10 +++++-- .../ai-assistant/new-session-settings.gts | 1 + .../ai-assistant/skill-menu/skill-toggle.gts | 28 +++++++++++-------- .../host/app/components/host-mode/card.gts | 2 +- .../app/components/matrix/register-user.gts | 2 +- .../components/operator-mode/code-submode.gts | 2 +- 9 files changed, 38 insertions(+), 21 deletions(-) diff --git a/packages/boxel-ui/addon/src/components/circle-spinner/index.gts b/packages/boxel-ui/addon/src/components/circle-spinner/index.gts index bd271e71bea..6b143d81a6f 100644 --- a/packages/boxel-ui/addon/src/components/circle-spinner/index.gts +++ b/packages/boxel-ui/addon/src/components/circle-spinner/index.gts @@ -3,7 +3,7 @@ import Component from '@glimmer/component'; import IconCircle from '../../icons/icon-circle.gts'; interface Signature { - Element: SVGElement; + Element: SVGSVGElement; } // eslint-disable-next-line ember/no-empty-glimmer-component-classes diff --git a/packages/boxel-ui/addon/src/components/copy-button/index.gts b/packages/boxel-ui/addon/src/components/copy-button/index.gts index 6bb3c2d5995..6a7fe6c9c7f 100644 --- a/packages/boxel-ui/addon/src/components/copy-button/index.gts +++ b/packages/boxel-ui/addon/src/components/copy-button/index.gts @@ -19,7 +19,7 @@ interface Signature { offset?: number; placement?: MiddlewareState['placement']; size?: BoxelButtonSize; - textToCopy: string; + textToCopy: string | null | undefined; tooltipText?: string; variant?: BoxelButtonKind; width?: string; @@ -31,6 +31,9 @@ export default class CopyButton extends Component { @tracked private recentlyCopied = false; @action private async copyToClipboard() { + if (this.args.textToCopy == null) { + return; + } try { await navigator.clipboard.writeText(this.args.textToCopy); this.recentlyCopied = true; diff --git a/packages/host/app/components/ai-assistant/code-block/actions.gts b/packages/host/app/components/ai-assistant/code-block/actions.gts index 395087af61f..2ee9490f653 100644 --- a/packages/host/app/components/ai-assistant/code-block/actions.gts +++ b/packages/host/app/components/ai-assistant/code-block/actions.gts @@ -1,6 +1,8 @@ import type { TemplateOnlyComponent } from '@ember/component/template-only'; import { hash } from '@ember/helper'; +import type Component from '@glimmer/component'; + import { CopyButton } from '@cardstack/boxel-ui/components'; import type { CodeData } from '@cardstack/host/lib/formatted-message/utils'; @@ -11,6 +13,9 @@ import ApplyCodePatchButton, { import type { ComponentLike } from '@glint/template'; +type AsComponentLike = + C extends Component ? ComponentLike : never; + export interface CodeBlockActionsSignature { Args: { codeData?: Partial; @@ -18,7 +23,7 @@ export interface CodeBlockActionsSignature { Blocks: { default: [ { - copyCode: CopyButton; + copyCode: AsComponentLike; applyCodePatch: ComponentLike; }, ]; diff --git a/packages/host/app/components/ai-assistant/message/aibot-message.gts b/packages/host/app/components/ai-assistant/message/aibot-message.gts index 0b3fb498237..a916b863085 100644 --- a/packages/host/app/components/ai-assistant/message/aibot-message.gts +++ b/packages/host/app/components/ai-assistant/message/aibot-message.gts @@ -287,7 +287,9 @@ class HtmlGroupCodeBlock extends Component { {{! This is just to show the ✅ icon to signalize that the code patch has been applied }} { /> - + { {{/if}} - + {{/if}} diff --git a/packages/host/app/components/ai-assistant/new-session-settings.gts b/packages/host/app/components/ai-assistant/new-session-settings.gts index 65f27e9f60e..5db1b9d3e92 100644 --- a/packages/host/app/components/ai-assistant/new-session-settings.gts +++ b/packages/host/app/components/ai-assistant/new-session-settings.gts @@ -49,6 +49,7 @@ export default class NewSessionSettings extends Component { @icon='close' @variant='ghost' @size='small' + @label='close new session options' class='new-session-settings-close-button' data-test-new-session-settings-close-button {{on 'click' @onClose}} diff --git a/packages/host/app/components/ai-assistant/skill-menu/skill-toggle.gts b/packages/host/app/components/ai-assistant/skill-menu/skill-toggle.gts index c006262d903..ad8fe399087 100644 --- a/packages/host/app/components/ai-assistant/skill-menu/skill-toggle.gts +++ b/packages/host/app/components/ai-assistant/skill-menu/skill-toggle.gts @@ -63,19 +63,23 @@ export default class SkillToggle extends Component { // Title for either skill source: `cardTitle` for a Skill card; for a skill // markdown file, the title indexed from its first heading, falling back to // the frontmatter name slug only when the file has no heading. - private get displayTitle(): string | undefined { - let card = this.card as - | { - cardTitle?: string; - title?: string; - name?: string; - frontmatter?: { name?: string }; - } - | undefined; + private get displayTitle(): string { + let card = this.card as { + cardTitle?: string; + title?: string; + name?: string; + frontmatter?: { name?: string }; + }; if (!card) { - return undefined; + return 'Untitled Skill'; } - return card.cardTitle ?? card.title ?? card.frontmatter?.name ?? card.name; + return ( + card.cardTitle ?? + card.title ?? + card.frontmatter?.name ?? + card.name ?? + 'Untitled Skill' + ); } private get isCreating() { @@ -172,7 +176,7 @@ export default class SkillToggle extends Component { { diff --git a/packages/host/app/components/matrix/register-user.gts b/packages/host/app/components/matrix/register-user.gts index a83c6cb811f..203fe2942d9 100644 --- a/packages/host/app/components/matrix/register-user.gts +++ b/packages/host/app/components/matrix/register-user.gts @@ -134,7 +134,7 @@ export default class RegisterUser extends Component { data-test-username-field id='boxel-register-username' @name='username' - autocomplete='username' + @autocomplete='username' @state={{this.usernameInputState}} @value={{this.username}} @placeholder='Your username' diff --git a/packages/host/app/components/operator-mode/code-submode.gts b/packages/host/app/components/operator-mode/code-submode.gts index 51a7d0386a8..6bfdc726f4b 100644 --- a/packages/host/app/components/operator-mode/code-submode.gts +++ b/packages/host/app/components/operator-mode/code-submode.gts @@ -912,7 +912,7 @@ export default class CodeSubmode extends Component { {{else}} Date: Mon, 27 Jul 2026 10:17:09 -0500 Subject: [PATCH 02/51] Fix search bar types --- packages/host/app/components/search/search-bar.gts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/host/app/components/search/search-bar.gts b/packages/host/app/components/search/search-bar.gts index 601a1dda2d7..e103a06d348 100644 --- a/packages/host/app/components/search/search-bar.gts +++ b/packages/host/app/components/search/search-bar.gts @@ -6,7 +6,7 @@ import { modifier } from 'ember-modifier'; import { BoxelInput, - type BoxelInputBottomTreatments, + type BoxelInputBottomTreatment, } from '@cardstack/boxel-ui/components'; import { IconSearch } from '@cardstack/boxel-ui/icons'; import { autoFocus } from '@cardstack/boxel-ui/modifiers'; @@ -40,7 +40,7 @@ interface Signature { onKeyDown?: (ev: Event) => void; onInputInsertion?: (element: HTMLElement) => void; pickerDestination?: string; - bottomTreatment?: BoxelInputBottomTreatments; + bottomTreatment?: BoxelInputBottomTreatment; state?: 'none' | 'valid' | 'invalid' | 'loading' | 'initial'; id?: string; hidePickers?: boolean; From 65127aa46025bc109b8df47783cd5c9b9a01a115 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 27 Jul 2026 11:07:28 -0500 Subject: [PATCH 03/51] Change input value to be optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It already accepted undefined, so it’s not actually required. --- packages/boxel-ui/addon/src/components/input/index.gts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/boxel-ui/addon/src/components/input/index.gts b/packages/boxel-ui/addon/src/components/input/index.gts index 98b216ad616..6e5deb65bca 100644 --- a/packages/boxel-ui/addon/src/components/input/index.gts +++ b/packages/boxel-ui/addon/src/components/input/index.gts @@ -82,7 +82,7 @@ export interface Signature { size?: Values; state?: InputValidationState; type?: InputType; - value: string | number | boolean | null | undefined; + value?: string | number | boolean | null; }; Element: HTMLInputElement | HTMLTextAreaElement | HTMLDivElement; } From 93b79a196f89e67824f05f8f3d9f9497173db101 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 27 Jul 2026 11:10:21 -0500 Subject: [PATCH 04/51] Add null support for optional parameters --- .../boxel-ui/addon/src/components/button/index.gts | 2 +- .../src/components/input-group/controls/index.gts | 4 ++-- .../addon/src/components/input-group/index.gts | 4 ++-- .../addon/src/components/radio-input/index.gts | 11 ++++++++--- .../src/components/resizable-panel-group/panel.gts | 8 ++++---- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/boxel-ui/addon/src/components/button/index.gts b/packages/boxel-ui/addon/src/components/button/index.gts index db083b4f021..610bab951c4 100644 --- a/packages/boxel-ui/addon/src/components/button/index.gts +++ b/packages/boxel-ui/addon/src/components/button/index.gts @@ -55,7 +55,7 @@ interface Signature { as?: string; class?: string; disabled?: boolean; - href?: string; + href?: string | null; kind?: BoxelButtonKind; loading?: boolean; models?: any; diff --git a/packages/boxel-ui/addon/src/components/input-group/controls/index.gts b/packages/boxel-ui/addon/src/components/input-group/controls/index.gts index 5f4916d295b..f777f566aa0 100644 --- a/packages/boxel-ui/addon/src/components/input-group/controls/index.gts +++ b/packages/boxel-ui/addon/src/components/input-group/controls/index.gts @@ -14,7 +14,7 @@ interface InputSignature { placeholder?: string; readonly?: boolean; required?: boolean; - value?: string; + value?: string | null; }; Blocks: Record; Element: HTMLInputElement; @@ -38,7 +38,7 @@ export const Input: TemplateOnlyComponent = get menuItems() { - return this.args.model.options?.map((v: string) => - menuItemFunc([v, () => (this.args.model.selectedValue = v)], { - checked: this.args.model.selectedValue === v, - }), + return ( + this.args.model.options?.map((v: string) => + menuItemFunc([v, () => (this.args.model.selectedValue = v)], { + checked: this.args.model.selectedValue === v, + }), + ) ?? [] ); } } From da615907668e4dced7c166122f2055cb64264078 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 28 Jul 2026 07:57:54 -0500 Subject: [PATCH 42/51] Fix incorrect casts --- .../code-submode/playground/instance-chooser-dropdown.gts | 2 +- .../operator-mode/code-submode/playground/playground-panel.gts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts b/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts index d448324d95d..1c89226e91e 100644 --- a/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts +++ b/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts @@ -176,7 +176,7 @@ function closeInstanceChooser() { ( document.querySelector( '[data-playground-instance-chooser][aria-expanded="true"]', - ) as BoxelSelect | null + ) as HTMLElement | null )?.click(); } diff --git a/packages/host/app/components/operator-mode/code-submode/playground/playground-panel.gts b/packages/host/app/components/operator-mode/code-submode/playground/playground-panel.gts index cdf4a035ff7..0d6d4392204 100644 --- a/packages/host/app/components/operator-mode/code-submode/playground/playground-panel.gts +++ b/packages/host/app/components/operator-mode/code-submode/playground/playground-panel.gts @@ -13,7 +13,6 @@ import ToElsewhere from 'ember-elsewhere/components/to-elsewhere'; import { consume, provide } from 'ember-provide-consume-context'; import { resource, use } from 'ember-resources'; -import type { BoxelSelect } from '@cardstack/boxel-ui/components'; import { CardContainer, LoadingIndicator, @@ -909,7 +908,7 @@ export default class PlaygroundPanel extends Component { ( document.querySelector( '[data-playground-instance-chooser][aria-expanded="true"]', - ) as BoxelSelect | null + ) as HTMLElement | null )?.click(); private get currentFileDef(): FileDef | undefined { From 3951bbb082b0b8caff99cdacea54db3d5f7d6e3c Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 28 Jul 2026 07:59:30 -0500 Subject: [PATCH 43/51] Fix filtering --- packages/experiments-realm/kanban-resource.gts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/experiments-realm/kanban-resource.gts b/packages/experiments-realm/kanban-resource.gts index da93d02c8e0..135ccbce993 100644 --- a/packages/experiments-realm/kanban-resource.gts +++ b/packages/experiments-realm/kanban-resource.gts @@ -1,6 +1,6 @@ import { tracked } from '@glimmer/tracking'; import { DndColumn } from '@cardstack/boxel-ui/components'; -import { CardDef } from '@cardstack/base/card-api'; +import type { CardDef } from '@cardstack/base/card-api'; import { Resource } from 'ember-modify-based-class-resource'; @@ -34,10 +34,9 @@ class KanbanResource extends Resource { if (currentColumn) { // Maintain order of existing cards and append new ones - let existingCardIds = new Set( - currentColumn.cards.map((card: CardDef) => card.id), - ); - let existingCards = currentColumn.cards.filter((card: CardDef) => + let currentCards = currentColumn.cards as CardDef[]; + let existingCardIds = new Set(currentCards.map((card) => card.id)); + let existingCards = currentCards.filter((card) => cardsForStatus.some((c) => c.id === card.id), ); let newCards = cardsForStatus.filter( From 2f60125da6d1ceee6e5ee39e00e67dcfc53dff2c Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 28 Jul 2026 07:59:52 -0500 Subject: [PATCH 44/51] Add missing interface match --- .../host/app/components/operator-mode/new-file-button.gts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/host/app/components/operator-mode/new-file-button.gts b/packages/host/app/components/operator-mode/new-file-button.gts index 3e78c4449ab..be6aa402eaf 100644 --- a/packages/host/app/components/operator-mode/new-file-button.gts +++ b/packages/host/app/components/operator-mode/new-file-button.gts @@ -23,7 +23,10 @@ interface TriggerButtonSignature { Args: { isCollapsed?: boolean; isDisabled?: boolean; - bindings: ModifierLike<{ Args: { Positional: unknown[] } }>; + bindings: ModifierLike<{ + Args: { Positional: unknown[] }; + Element: HTMLElement; + }>; }; Element: HTMLButtonElement; } From 8b65732d9e483dbd12b05aba9d5466603ef6237f Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 28 Jul 2026 08:00:03 -0500 Subject: [PATCH 45/51] Fix element type --- .../app/components/operator-mode/code-submode/toggle-button.gts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/host/app/components/operator-mode/code-submode/toggle-button.gts b/packages/host/app/components/operator-mode/code-submode/toggle-button.gts index f6344e92e7a..81c423536ff 100644 --- a/packages/host/app/components/operator-mode/code-submode/toggle-button.gts +++ b/packages/host/app/components/operator-mode/code-submode/toggle-button.gts @@ -11,7 +11,7 @@ interface ToggleButtonSignature { disabled?: boolean; isActive: boolean; }; - Element: typeof Button.Element; + Element: HTMLButtonElement | HTMLAnchorElement; Blocks: { default: []; annotation: []; From c6d69b97d3f52c871435978433f707579af16ba2 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Tue, 28 Jul 2026 08:00:37 -0500 Subject: [PATCH 46/51] Add narrowing for proper branch types --- .../playground/instance-chooser-dropdown.gts | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts b/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts index 1c89226e91e..625c7e99212 100644 --- a/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts +++ b/packages/host/app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts @@ -180,12 +180,32 @@ function closeInstanceChooser() { )?.click(); } +type OptionItem = RenderableSearchEntryLike | FieldOption | FileDef; + +// The template branches on @isFileMeta/@isField, which the parent keeps in +// sync with the kind of options it passed; these narrow each branch's item. +function optionItems(options: OptionsDropdownSignature['Args']['options']) { + return (options ?? []) as OptionItem[]; +} + +function asFile(item: OptionItem) { + return item as FileDef; +} + +function fieldOf(item: OptionItem) { + return (item as FieldOption).field; +} + +function componentOf(item: OptionItem) { + return (item as RenderableSearchEntryLike).component; +} + export const OptionsDropdown: TemplateOnlyComponent =