Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 27 additions & 39 deletions packages/host/app/components/markdown-embed-chooser/tab-panel.gts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ import {
import {
cardTypeName,
fileNameFromUrl,
type BfmSizeSpec,
} from '@cardstack/runtime-common/bfm-card-references';

import { maybeRelativeReference } from '@cardstack/runtime-common/url';

import MiniCardChooser from '@cardstack/host/components/card-chooser/mini';
import MiniFileChooser from '@cardstack/host/components/file-chooser/mini';

Expand All @@ -37,6 +36,7 @@ import type {
import type StoreService from '@cardstack/host/services/store';

import MarkdownEmbedPreviewPane from './pane';
import MarkdownEmbedPreview from './preview';
import TabPills from './tab-pills';

import type EmbedFormatSelection from './format-selection';
Expand All @@ -62,9 +62,8 @@ interface Signature {
// (The pane's format seed comes from the shared `@selection`, which the
// modal seeds from this same target.)
initialTarget?: MarkdownEmbedInitialTarget;
// The editing document's own URL. The label and the inserted ref are both
// relativized against it, so a fallback URL label reads as `../Type/id` —
// the same form the pane serializes into the directive.
// The editing document's own URL. The pane relativizes the inserted ref
// against it, so the directive serializes in the `../Type/id` form.
documentBaseUrl?: string;
// Fired when the user clicks "Remove" in `current` mode. The modal
// resolves its deferred with `{ remove: true }`.
Expand Down Expand Up @@ -128,31 +127,12 @@ export default class MarkdownEmbedChooserTabPanel extends Component<Signature> {
return dirty ? 'Accept' : 'Done';
}

private get currentTargetLabel(): string {
let t = this.selectedTarget;
if (!t) return this.toDisplayUrl(this.selectedUrl ?? '');
if (this.args.refType === 'file') {
return fileNameFromUrl(t.id ?? this.selectedUrl ?? '');
}
return (
(t as CardDef).cardTitle ??
this.toDisplayUrl(t.id ?? this.selectedUrl ?? '')
);
}

// When the label falls back to showing a raw URL (a broken ref, or a card
// with no title), relativize it against the editing document's own URL —
// yielding the `../Type/id` form the pane serializes into the directive, so
// the label matches what gets inserted. Falls back to the absolute URL when
// there's no base or either URL can't be parsed.
private toDisplayUrl(url: string): string {
let base = this.args.documentBaseUrl;
if (!url || !base) return url;
try {
return maybeRelativeReference(new URL(url), new URL(base), undefined);
} catch {
return url;
}
// The current-target tile renders the placed card/file as a compact fitted
// chip — a fixed Double Strip (250×65), independent of the format the user
// picks for the actual embed in the preview pane. It's an identity marker for
// "what's placed here now", not the embed being configured.
private get currentTileSize(): BfmSizeSpec {
return { format: 'fitted', width: 250, height: 65 };
}
Comment on lines +130 to 136

@action
Expand Down Expand Up @@ -271,12 +251,19 @@ export default class MarkdownEmbedChooserTabPanel extends Component<Signature> {
class='markdown-embed-chooser-tab-panel__current'
data-test-markdown-embed-chooser-current
>
<span
class='markdown-embed-chooser-tab-panel__current-label'
data-test-markdown-embed-chooser-current-label
>
{{this.currentTargetLabel}}
</span>
<MarkdownEmbedPreview
class='markdown-embed-chooser-tab-panel__current-preview'
@target={{this.selectedTarget}}
@format='fitted'
@sizeSpec={{this.currentTileSize}}
Comment on lines +254 to +258
@kind='block'
@brokenUrl={{this.brokenUrl}}
@brokenState={{this.brokenState}}
@brokenDisplayName={{this.brokenDisplayName}}
@brokenItemType={{this.brokenItemType}}
@errorDoc={{this.brokenErrorDoc}}
data-test-markdown-embed-chooser-current-preview
/>
<div class='markdown-embed-chooser-tab-panel__current-actions'>
<BoxelButton
{{on 'click' this.startReplace}}
Expand Down Expand Up @@ -415,9 +402,10 @@ export default class MarkdownEmbedChooserTabPanel extends Component<Signature> {
padding: var(--boxel-sp);
text-align: center;
}
.markdown-embed-chooser-tab-panel__current-label {
font: 600 var(--boxel-font);
word-break: break-word;
/* The fitted chip carries its own fixed footprint; keep it from
stretching to the centered column's cross axis. */
.markdown-embed-chooser-tab-panel__current-preview {
flex: 0 0 auto;
}
Comment on lines +405 to 409
.markdown-embed-chooser-tab-panel__current-actions {
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,17 +380,14 @@ module('Integration | markdown-embed-chooser-modal', function (hooks) {
'[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current]',
)
.exists('the current-target tile renders for the broken preload');
// The label falls back to the ref (no title to show). It relativizes
// against the editing document's URL, so it reads as the `../`-relative
// path — the same form the pane serializes into the directive.
// The tile itself renders the compact broken visual (not a text label),
// so a broken preload reads as the same warning box the resolved chip would
// occupy.
assert
.dom(
'[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current-label]',
'[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current] [data-test-broken-link-template]',
)
.hasText(
'../books/ghost',
'a broken ref labels as its document-relative path',
);
.exists('the current-target tile shows the compact broken visual');
assert
.dom('[data-test-markdown-embed-chooser-remove]')
.exists('Remove is available');
Expand All @@ -402,7 +399,7 @@ module('Integration | markdown-embed-chooser-modal', function (hooks) {
await pending;
});

test('a broken ref in a different realm than the document keeps its full URL as the label', async function (assert) {
test('edit-mode preload renders the current-target tile as a fitted card chip', async function (assert) {
await render(
<template>
<HostContextProvider>
Expand All @@ -414,33 +411,64 @@ module('Integration | markdown-embed-chooser-modal', function (hooks) {
let svc = getService(
'markdown-embed-chooser',
) as MarkdownEmbedChooserService;
// A broken ref in the base realm while the editing document lives in the
// test realm — the two are in different namespaces, so the ref can't be
// relativized and keeps its absolute URL.
let brokenUrl = `${baseRealm.url}ghost-card`;
let pending = svc.editEmbed({
refType: 'card',
url: brokenUrl,
url: mango,
sizeSpec: 'embedded',
documentBaseUrl: `${testRealmURL}posts/my-post`,
});
// The tile renders the resolved card itself, in fitted format — not a text
// label. The card body paints asynchronously, so wait for it. The pane
// preview stays on the picked format (embedded), so the fitted render of
// this card is unique to the tile.
await waitFor(
'[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-broken-link-template]',
`[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current] [data-test-card="${mango}"][data-test-card-format="fitted"]`,
{ timeout: 5000 },
);
assert
.dom(
'[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current-label]',
`[data-test-markdown-embed-chooser-tab-panel="card"] [data-test-markdown-embed-chooser-current] [data-test-card="${mango}"][data-test-card-format="fitted"]`,
)
.hasText(
brokenUrl,
'a broken ref in another realm keeps its absolute URL',
.exists(
'the current-target tile renders the card itself in fitted format',
);

svc.resolve(undefined);
await pending;
});

test('edit-mode preload of a file renders the current-target tile as a fitted chip', async function (assert) {
await render(
<template>
<HostContextProvider>
<MarkdownEmbedChooserModal />
</HostContextProvider>
</template>,
);

let svc = getService(
'markdown-embed-chooser',
) as MarkdownEmbedChooserService;
let pending = svc.editEmbed({
refType: 'file',
url: readme,
sizeSpec: 'embedded',
});
await waitFor(
'[data-test-markdown-embed-chooser-tab-panel="file"] [data-test-markdown-embed-chooser-current-preview]',
{ timeout: 5000 },
);

// Files render through the same fitted path as cards.
assert
.dom(
'[data-test-markdown-embed-chooser-tab-panel="file"] [data-test-markdown-embed-chooser-current] [data-test-markdown-embed-preview-format="fitted"]',
)
.exists('the file current-target tile renders in its fitted template');

svc.resolve(undefined);
await pending;
});

test('Remove resolves the deferred with { remove: true }', async function (assert) {
await render(
<template>
Expand Down
Loading