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
27 changes: 14 additions & 13 deletions packages/base/markdown-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// construction, and so future adjustments happen in one place.

import { markdownEscape } from '@cardstack/boxel-ui/helpers';
import { isValidDate } from '@cardstack/runtime-common';
import { isValidDate, serializeBfmRef } from '@cardstack/runtime-common';

// Date formatting shared by DateField, DateTimeField, and DateRangeField so
// their markdown output is consistent. Matches the existing `en-US` `{year:
Expand Down Expand Up @@ -158,24 +158,25 @@ interface MarkdownEmbedOptions {
size?: string;
}

// Returns a BFM reference directive (`:card`/`::card`, `:file`/`::file`, …)
// for a single reference by keyword + id. Returns `''` for a missing id.
// Thin wrapper over the shared `serializeBfmRef` builder so base-realm and
// host code emit identical BFM syntax.
export function markdownEmbedForRef(
refType: string,
id: string | null | undefined,
options?: MarkdownEmbedOptions,
): string {
return serializeBfmRef(refType, id, options);
}

// Returns a BFM card-reference directive for a single card.
// Returns `''` for null/undefined cards.
export function markdownEmbedForCard(
card: CardLike | null | undefined,
options?: MarkdownEmbedOptions,
): string {
if (!card?.id) {
return '';
}
let kind = options?.kind ?? 'block';
if (kind === 'inline') {
return `:card[${card.id}]`;
}
let size = options?.size;
if (size) {
return `::card[${card.id} | ${size}]`;
}
return `::card[${card.id}]`;
return serializeBfmRef('card', card?.id, options);
}

interface MarkdownEmbedsOptions extends MarkdownEmbedOptions {
Expand Down
112 changes: 112 additions & 0 deletions packages/host/app/components/markdown-embed-chooser/pane-usage.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { action } from '@ember/object';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import FreestyleUsage from 'ember-freestyle/components/freestyle/usage';

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

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

import type StoreService from '@cardstack/host/services/store';

import type { CardDef } from 'https://cardstack.com/base/card-api';

import MarkdownEmbedPreviewPane from './pane';

export default class MarkdownEmbedPreviewPaneUsage extends Component {
@service declare private store: StoreService;

@tracked private target: CardDef | undefined;
@tracked private inserted: string | undefined;

@action private async onSelect(url: string) {
let result = await this.store.get(url);
if (!isCardErrorJSONAPI(result)) {
this.target = result as CardDef;
}
}

@action private onInsert(bfm: string) {
this.inserted = bfm;
}

<template>
<FreestyleUsage @name='MarkdownEmbedChooser::Pane'>
<:description>
The right-hand companion to the mini choosers: a live preview plus a
format dropdown, always-on W×H inputs for Fitted (with smart variant
matching), an Inline/Block toggle (Block is disabled while Atom is
selected, since atom has no block form), and a dynamic "Insert as …"
CTA. The CTA fires
Comment on lines +40 to +42
<code>onInsert</code>
with the serialized BFM — the host owns cursor insertion.
</:description>
<:example>
<div class='side-by-side'>
<div class='panel'>
<MiniCardChooser @onSelect={{this.onSelect}} />
</div>
<div class='panel'>
{{#if this.target}}
<MarkdownEmbedPreviewPane
@target={{this.target}}
@refType='card'
@onInsert={{this.onInsert}}
/>
{{else}}
<p class='hint'>Pick a card to configure its embed.</p>
{{/if}}
</div>
</div>
{{#if this.inserted}}
<p class='readout' data-test-pane-usage-readout>
Inserted:
<code>{{this.inserted}}</code>
</p>
{{/if}}
</:example>
<:api as |Args|>
<Args.Object
@name='target'
@description='Resolved CardDef or FileDef being previewed; its id is the BFM ref URL.'
@required={{true}}
/>
<Args.String
@name='refType'
@description="'card' or 'file' — which BFM keyword to emit."
@required={{true}}
/>
<Args.Action
@name='onInsert'
@description='Called with the serialized BFM directive when the CTA is clicked.'
@required={{true}}
/>
</:api>
</FreestyleUsage>
<style scoped>
.side-by-side {
display: flex;
gap: var(--boxel-sp);
height: 480px;
}
.panel {
flex: 1 1 0;
min-width: 0;
border: 1px solid var(--boxel-border-color, var(--boxel-300));
border-radius: var(--boxel-border-radius);
overflow: hidden;
}
.hint {
padding: var(--boxel-sp);
font: var(--boxel-font-sm);
color: var(--boxel-450);
}
.readout {
margin-top: var(--boxel-sp-xs);
font: var(--boxel-font-sm);
}
</style>
</template>
}
Loading
Loading