From todoist-extensions
Design and generate a Doist Card UI with JSON and TypeScript output
npx claudepluginhub caleb-terry/caleb-plugins --plugin todoist-extensionsDesign a Doist Card UI for a Todoist extension — interactively build the layout, generate both raw card JSON and TypeScript code using `@doist/ui-extensions-core`, and preview the structure. Load the `ui-extensions` skill before designing. Use `references/doist-cards.md` as the single source of truth for all element types, properties, and constraints. ## Step 1: Understand the card's purpose Ask in a single message: - "What should this card do?" — offer common patterns: - **Form** — collect user input and submit - **Information display** — show data with formatting - **Action conf...
Design a Doist Card UI for a Todoist extension — interactively build the layout, generate both raw card JSON and TypeScript code using @doist/ui-extensions-core, and preview the structure.
Load the ui-extensions skill before designing. Use references/doist-cards.md as the single source of truth for all element types, properties, and constraints.
Ask in a single message:
"What should this card do?" — offer common patterns:
"What surface is this card for?" — context menu, composer, or settings
Based on the purpose, design the card structure. Apply these Doist Card design principles:
Layout rules:
TextBlock with size: "large" and weight: "bolder" for headersseparator: true to visually divide sectionsColumnSet with Column elements for side-by-side layout (width: "auto" or "stretch")Container to group related elements with optional backgroundImage or minHeightwrap: true on TextBlocks that may contain long textInput patterns:
id — this is how action.inputs maps valueslabel property on inputs for accessible form labelsisRequired: true and errorMessage for validationplaceholder text to guide usersInput.ChoiceSet with style: "compact" for dropdowns, style: "expanded" for radio buttonsInput.Text with rows: 3+ for multiline and inputStyle for semantic types (email, url, tel)Action patterns:
actions arraystyle: "positive" for the primary action buttonstyle: "destructive" for dangerous actions (delete, remove)Action.Submit with data to pass identifiers back to the serverAction.OpenUrl for external linksAction.Clipboard for copy-to-clipboardassociatedInputs: "auto" on submit actions to include form dataOutput the complete card JSON. Every card must include these required root properties:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"doistCardVersion": "0.6",
"version": "1.4",
"body": [],
"actions": []
}
Set autoFocusId to the first input element's id if the card has inputs.
Validate every element against the schema in references/doist-cards.md:
type, text required; size, weight, color, isSubtle, wrap, horizontalAlignment optionaltype, url required; width, height, altText, aspectRatio, size, selectAction optionaltype, id required; placeholder, label, value, isRequired, errorMessage, rows, inputStyle, regex, inlineAction optionaltype, id, choices required; label, value, isRequired, isSearchable, style, orientation, selectAction optionaltype, id, title required; value, label, wrap, isRequired, errorMessage, selectAction optionaltype, id required; label, value, min, max, isRequired, errorMessage optionaltype, id required; label, value, isRequired, errorMessage optionaltype required; items, selectAction, minHeight, backgroundImage, verticalContentAlignment, bleed optionaltype required; columns, horizontalAlignment optionaltype required; items, width, verticalContentAlignment, selectAction optionaltype, actions required; horizontalAlignment optionaltype, inlines required; horizontalAlignment optionalGenerate the equivalent TypeScript code that builds the same card programmatically. Import from @doist/ui-extensions-core where types are available, otherwise use typed object literals:
import type { DoistCardResponse } from '../types'
export function buildMyCard(data?: Record<string, unknown>): DoistCardResponse {
return {
card: {
type: 'AdaptiveCard',
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
doistCardVersion: '0.6',
version: '1.4',
body: [
// elements here
],
actions: [
// actions here
],
},
}
}
Make the card function accept parameters so it can be dynamic (e.g., pre-filling input values, showing different content based on data).
Display an ASCII preview of the card layout:
+----------------------------------------+
| [Header Text] large |
|----------------------------------------|
| [Description text wraps across the |
| full width of the card] |
|----------------------------------------|
| Label: |
| [________________________] text input |
| |
| Option: |
| ( ) Choice A |
| (*) Choice B |
| ( ) Choice C |
|----------------------------------------|
| [Cancel] [*Submit*] |
+----------------------------------------+
Annotate the preview with element types so the mapping is clear.
If the user has an existing extension project:
src/cards/[card-name].tssrc/cards/[card-name].json (useful for testing/debugging)If standalone: