mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-22 18:45:19 +02:00
Render background task output with rich markdown
Add a read-only TipTap-backed RichMarkdownViewer and use it for Background Tasks output so rendered index.md files can display the same rich fenced blocks as notes, including email, calendar, chart, table, image, embed, transcript, and Mermaid blocks. Keep the existing Source/Rendered toggle for raw markdown inspection, and hide editor-only delete controls in read-only output. Move the rich block format examples out of the LiveNote-only prompt and into the shared knowledge note style guide. This gives both LiveNote and Background Task agents the same canonical renderer contract, including exact fenced-code schemas for rich Markdown blocks and the rule to avoid emitting task blocks as agent output. Verified with: - npm run build in apps/x/apps/renderer - npm run build in apps/x/packages/core
This commit is contained in:
parent
65f8e9d678
commit
fe5e67f810
5 changed files with 322 additions and 166 deletions
|
|
@ -18,6 +18,7 @@ import { toast } from '@/lib/toast'
|
||||||
import type { ConversationItem } from '@/lib/chat-conversation'
|
import type { ConversationItem } from '@/lib/chat-conversation'
|
||||||
import { runLogToConversation } from '@/lib/run-to-conversation'
|
import { runLogToConversation } from '@/lib/run-to-conversation'
|
||||||
import { CompactConversation } from '@/components/compact-conversation'
|
import { CompactConversation } from '@/components/compact-conversation'
|
||||||
|
import { RichMarkdownViewer } from '@/components/rich-markdown-viewer'
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Trigger helpers (inlined; extract to shared <TriggersEditor> as a follow-up)
|
// Trigger helpers (inlined; extract to shared <TriggersEditor> as a follow-up)
|
||||||
|
|
@ -560,9 +561,7 @@ function OutputPane({ slug, taskName, refreshKey }: { slug: string; taskName: st
|
||||||
) : viewSource ? (
|
) : viewSource ? (
|
||||||
<pre className="overflow-x-auto whitespace-pre-wrap font-mono text-[13px] leading-relaxed">{body}</pre>
|
<pre className="overflow-x-auto whitespace-pre-wrap font-mono text-[13px] leading-relaxed">{body}</pre>
|
||||||
) : (
|
) : (
|
||||||
<Streamdown className="prose dark:prose-invert max-w-none [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
|
<RichMarkdownViewer content={body} />
|
||||||
{body}
|
|
||||||
</Streamdown>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
106
apps/x/apps/renderer/src/components/rich-markdown-viewer.tsx
Normal file
106
apps/x/apps/renderer/src/components/rich-markdown-viewer.tsx
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { EditorContent, useEditor } from '@tiptap/react'
|
||||||
|
import StarterKit from '@tiptap/starter-kit'
|
||||||
|
import Link from '@tiptap/extension-link'
|
||||||
|
import Image from '@tiptap/extension-image'
|
||||||
|
import TaskList from '@tiptap/extension-task-list'
|
||||||
|
import TaskItem from '@tiptap/extension-task-item'
|
||||||
|
import { TableKit } from '@tiptap/extension-table'
|
||||||
|
import { Markdown } from 'tiptap-markdown'
|
||||||
|
import { TaskBlockExtension } from '@/extensions/task-block'
|
||||||
|
import { PromptBlockExtension } from '@/extensions/prompt-block'
|
||||||
|
import { ImageBlockExtension } from '@/extensions/image-block'
|
||||||
|
import { EmbedBlockExtension } from '@/extensions/embed-block'
|
||||||
|
import { IframeBlockExtension } from '@/extensions/iframe-block'
|
||||||
|
import { ChartBlockExtension } from '@/extensions/chart-block'
|
||||||
|
import { TableBlockExtension } from '@/extensions/table-block'
|
||||||
|
import { CalendarBlockExtension } from '@/extensions/calendar-block'
|
||||||
|
import { EmailBlockExtension, EmailsBlockExtension } from '@/extensions/email-block'
|
||||||
|
import { TranscriptBlockExtension } from '@/extensions/transcript-block'
|
||||||
|
import { MermaidBlockExtension } from '@/extensions/mermaid-block'
|
||||||
|
import { WikiLink } from '@/extensions/wiki-link'
|
||||||
|
import '@/styles/editor.css'
|
||||||
|
|
||||||
|
const BLANK_LINE_MARKER = '\u200B'
|
||||||
|
|
||||||
|
function preprocessMarkdown(markdown: string): string {
|
||||||
|
return markdown.replace(/\n{3,}/g, (match) => {
|
||||||
|
const emptyParagraphs = match.length - 2
|
||||||
|
let result = '\n\n'
|
||||||
|
for (let i = 0; i < emptyParagraphs; i += 1) {
|
||||||
|
result += BLANK_LINE_MARKER + '\n\n'
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RichMarkdownViewer({ content }: { content: string }) {
|
||||||
|
const editor = useEditor({
|
||||||
|
editable: false,
|
||||||
|
extensions: [
|
||||||
|
StarterKit.configure({
|
||||||
|
heading: {
|
||||||
|
levels: [1, 2, 3],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Link.configure({
|
||||||
|
openOnClick: true,
|
||||||
|
HTMLAttributes: {
|
||||||
|
rel: 'noopener noreferrer',
|
||||||
|
target: '_blank',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Image.configure({
|
||||||
|
inline: false,
|
||||||
|
allowBase64: true,
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: 'editor-image',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
TaskBlockExtension,
|
||||||
|
PromptBlockExtension,
|
||||||
|
ImageBlockExtension,
|
||||||
|
EmbedBlockExtension,
|
||||||
|
IframeBlockExtension,
|
||||||
|
ChartBlockExtension,
|
||||||
|
TableBlockExtension,
|
||||||
|
CalendarBlockExtension,
|
||||||
|
EmailsBlockExtension,
|
||||||
|
EmailBlockExtension,
|
||||||
|
TranscriptBlockExtension,
|
||||||
|
MermaidBlockExtension,
|
||||||
|
WikiLink,
|
||||||
|
TaskList,
|
||||||
|
TaskItem.configure({
|
||||||
|
nested: true,
|
||||||
|
}),
|
||||||
|
TableKit.configure({
|
||||||
|
table: { resizable: false },
|
||||||
|
}),
|
||||||
|
Markdown.configure({
|
||||||
|
html: true,
|
||||||
|
breaks: true,
|
||||||
|
tightLists: false,
|
||||||
|
transformCopiedText: false,
|
||||||
|
transformPastedText: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
content: preprocessMarkdown(content),
|
||||||
|
editorProps: {
|
||||||
|
attributes: {
|
||||||
|
class: 'prose prose-sm max-w-none focus:outline-none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editor) return
|
||||||
|
editor.chain().setMeta('addToHistory', false).setContent(preprocessMarkdown(content)).run()
|
||||||
|
}, [content, editor])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tiptap-editor rich-markdown-viewer">
|
||||||
|
<EditorContent editor={editor} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -2020,3 +2020,33 @@
|
||||||
.dark .tiptap-editor .ProseMirror p.is-editor-empty:first-child::before {
|
.dark .tiptap-editor .ProseMirror p.is-editor-empty:first-child::before {
|
||||||
color: rgba(255, 255, 255, 0.3);
|
color: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read-only renderer used by surfaces that need rich blocks without editor chrome. */
|
||||||
|
.rich-markdown-viewer {
|
||||||
|
display: block;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-markdown-viewer .ProseMirror {
|
||||||
|
max-width: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-markdown-viewer .ProseMirror:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-markdown-viewer .ProseMirror .task-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .image-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .embed-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .iframe-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .chart-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .table-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .calendar-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .email-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .email-draft-block-delete,
|
||||||
|
.rich-markdown-viewer .ProseMirror .mermaid-block-delete {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/**
|
||||||
* The canonical writing style for content written into the user's knowledge
|
* The canonical writing style for content written into the user's knowledge
|
||||||
* base. Imported by both the `doc-collab` skill (so Copilot picks it up on
|
* base. Imported by the `doc-collab` skill (so Copilot picks it up on note
|
||||||
* note edits) and the live-note run-agent prompt (so background runs use the
|
* edits), the live-note run-agent prompt, and the background-task run-agent
|
||||||
* same rules without having to load the skill on every fire). One source of
|
* prompt (so background runs use the same rules without having to load the
|
||||||
* truth, two consumers.
|
* skill on every fire).
|
||||||
*
|
*
|
||||||
* If you change this guide, restart the dev server / rebuild — both consumers
|
* If you change this guide, restart the dev server / rebuild — both consumers
|
||||||
* inline it at module load.
|
* inline it at module load.
|
||||||
|
|
@ -113,6 +113,186 @@ Common note types and the target shape for each:
|
||||||
- **GitHub / issue digests**: \`- [<title>](<issue_url>) · <repo> · <state> · <updated>\`.
|
- **GitHub / issue digests**: \`- [<title>](<issue_url>) · <repo> · <state> · <updated>\`.
|
||||||
- **Tweets / social digests**: \`- [<truncated text or topic>](<post_url>) · @<author> · <when>\`.
|
- **Tweets / social digests**: \`- [<truncated text or topic>](<post_url>) · @<author> · <when>\`.
|
||||||
|
|
||||||
|
## Rich Markdown block formats
|
||||||
|
|
||||||
|
The renderer turns specially-tagged fenced code blocks into styled UI: tables, charts, calendars, emails, embeds, and more. Reach for these when the data has structure that benefits from a visual treatment; stay with plain markdown when prose, a markdown table, or bullets carry the meaning just as well. Pick **at most one block per output region** unless the user asks for a multi-section layout — and follow the exact fence language and shape, since anything unparseable renders as a small "Invalid X block" error card.
|
||||||
|
|
||||||
|
Do **not** emit \`task\` blocks — those are user-authored input mechanisms, not agent outputs.
|
||||||
|
|
||||||
|
### \`table\` — tabular data (JSON)
|
||||||
|
|
||||||
|
Use for: scoreboards, leaderboards, comparisons, multi-row status digests.
|
||||||
|
|
||||||
|
\`\`\`table
|
||||||
|
{
|
||||||
|
"title": "Top stories on Hacker News",
|
||||||
|
"columns": ["Rank", "Title", "Points", "Comments"],
|
||||||
|
"data": [
|
||||||
|
{"Rank": 1, "Title": "Show HN: ...", "Points": 842, "Comments": 312},
|
||||||
|
{"Rank": 2, "Title": "...", "Points": 530, "Comments": 144}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`columns\` (string[]), \`data\` (array of objects keyed by column name). Optional: \`title\`.
|
||||||
|
|
||||||
|
### \`chart\` — line / bar / pie chart (JSON)
|
||||||
|
|
||||||
|
Use for: time series, categorical breakdowns, share-of-total. Skip if a single sentence carries the meaning.
|
||||||
|
|
||||||
|
\`\`\`chart
|
||||||
|
{
|
||||||
|
"chart": "line",
|
||||||
|
"title": "USD/INR — last 7 days",
|
||||||
|
"x": "date",
|
||||||
|
"y": "rate",
|
||||||
|
"data": [
|
||||||
|
{"date": "2026-04-13", "rate": 83.41},
|
||||||
|
{"date": "2026-04-14", "rate": 83.38}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`chart\` ("line" | "bar" | "pie"), \`x\` (field name on each row), \`y\` (field name on each row), and **either** \`data\` (inline array of objects) **or** \`source\` (workspace path to a JSON-array file). Optional: \`title\`.
|
||||||
|
|
||||||
|
### \`mermaid\` — diagrams (raw Mermaid source)
|
||||||
|
|
||||||
|
Use for: relationship maps, flowcharts, sequence diagrams, gantt charts, mind maps.
|
||||||
|
|
||||||
|
\`\`\`mermaid
|
||||||
|
graph LR
|
||||||
|
A[Project Alpha] --> B[Sarah Chen]
|
||||||
|
A --> C[Acme Corp]
|
||||||
|
B --> D[Q3 Launch]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Body is plain Mermaid source — no JSON wrapper.
|
||||||
|
|
||||||
|
### \`calendar\` — list of events (JSON)
|
||||||
|
|
||||||
|
Use for: upcoming meetings, agenda digests, day/week views.
|
||||||
|
|
||||||
|
\`\`\`calendar
|
||||||
|
{
|
||||||
|
"title": "Today",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"summary": "1:1 with Sarah",
|
||||||
|
"start": {"dateTime": "2026-04-20T10:00:00-07:00"},
|
||||||
|
"end": {"dateTime": "2026-04-20T10:30:00-07:00"},
|
||||||
|
"location": "Zoom",
|
||||||
|
"conferenceLink": "https://zoom.us/j/..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`events\` (array). Each event optionally has \`summary\`, \`start\`/\`end\` (object with \`dateTime\` ISO string OR \`date\` "YYYY-MM-DD" for all-day), \`location\`, \`htmlLink\`, \`conferenceLink\`, \`source\`. Optional top-level: \`title\`, \`showJoinButton\` (bool).
|
||||||
|
|
||||||
|
### \`emails\` — multi-thread email digest (JSON)
|
||||||
|
|
||||||
|
Use for: surfacing a compact inbox-style digest of several relevant threads.
|
||||||
|
|
||||||
|
\`\`\`emails
|
||||||
|
{
|
||||||
|
"title": "Q3 planning threads",
|
||||||
|
"emails": [
|
||||||
|
{
|
||||||
|
"subject": "Q3 launch readiness",
|
||||||
|
"from": "sarah@acme.com",
|
||||||
|
"date": "2026-04-19T16:42:00Z",
|
||||||
|
"summary": "Sarah confirms timeline; flagged blocker on infra capacity.",
|
||||||
|
"latest_email": "Hey — quick update on Q3...\\n\\nThanks,\\nSarah"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`emails\` (array of \`email\` objects). Optional top-level: \`title\`.
|
||||||
|
|
||||||
|
### \`email\` — single email or thread digest (JSON)
|
||||||
|
|
||||||
|
Use for: surfacing one important thread — latest message body, summary of prior context, optional draft reply.
|
||||||
|
|
||||||
|
\`\`\`email
|
||||||
|
{
|
||||||
|
"subject": "Q3 launch readiness",
|
||||||
|
"from": "sarah@acme.com",
|
||||||
|
"date": "2026-04-19T16:42:00Z",
|
||||||
|
"summary": "Sarah confirms timeline; flagged blocker on infra capacity.",
|
||||||
|
"latest_email": "Hey — quick update on Q3...\\n\\nThanks,\\nSarah"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`latest_email\` (string). Optional: \`threadId\`, \`summary\`, \`subject\`, \`from\`, \`to\`, \`date\`, \`past_summary\`, \`draft_response\`, \`response_mode\` ("inline" | "assistant" | "both").
|
||||||
|
|
||||||
|
For digests of **many** threads, prefer an \`emails\` block or a compact markdown table — \`email\` is for one thread at a time.
|
||||||
|
|
||||||
|
### \`image\` — single image (JSON)
|
||||||
|
|
||||||
|
Use for: charts, screenshots, photos you have a URL or workspace path for.
|
||||||
|
|
||||||
|
\`\`\`image
|
||||||
|
{
|
||||||
|
"src": "https://example.com/forecast.png",
|
||||||
|
"alt": "Weather forecast",
|
||||||
|
"caption": "Bay Area · April 20"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`src\` (URL or workspace path). Optional: \`alt\`, \`caption\`.
|
||||||
|
|
||||||
|
### \`embed\` — YouTube / Figma / Tweet embed (JSON)
|
||||||
|
|
||||||
|
Use for: linking to a video, design, or tweet that should render inline.
|
||||||
|
|
||||||
|
\`\`\`embed
|
||||||
|
{
|
||||||
|
"provider": "youtube",
|
||||||
|
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||||
|
"caption": "Latest demo"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`provider\` ("youtube" | "figma" | "tweet" | "generic"), \`url\`. Optional: \`caption\`.
|
||||||
|
|
||||||
|
### \`iframe\` — arbitrary embedded webpage (JSON)
|
||||||
|
|
||||||
|
Use for: live dashboards, status pages, trackers — anything that has its own webpage and benefits from being live, not snapshotted.
|
||||||
|
|
||||||
|
\`\`\`iframe
|
||||||
|
{
|
||||||
|
"url": "https://status.example.com",
|
||||||
|
"title": "Service status",
|
||||||
|
"height": 600
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`url\` (must be \`https://\` or \`http://localhost\`). Optional: \`title\`, \`caption\`, \`height\` (240–1600), \`allow\` (Permissions-Policy string).
|
||||||
|
|
||||||
|
### \`transcript\` — long transcript (JSON)
|
||||||
|
|
||||||
|
Use for: meeting transcripts, voice-note dumps — bodies that benefit from a collapsible UI.
|
||||||
|
|
||||||
|
\`\`\`transcript
|
||||||
|
{"transcript": "[00:00] Speaker A: Welcome everyone..."}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`transcript\` (string).
|
||||||
|
|
||||||
|
### \`prompt\` — starter Copilot prompt (YAML)
|
||||||
|
|
||||||
|
Use for: end-of-output "next step" cards. The user clicks **Run** and the chat sidebar opens with the underlying instruction submitted to Copilot.
|
||||||
|
|
||||||
|
\`\`\`prompt
|
||||||
|
label: Draft replies to today's emails
|
||||||
|
instruction: |
|
||||||
|
For each unanswered email in the digest above, draft a 2-line reply
|
||||||
|
in my voice and present them as a checklist for me to approve.
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Required: \`label\` (short title shown on the card), \`instruction\` (the longer prompt). Note: this block uses **YAML**, not JSON.
|
||||||
|
|
||||||
## When prose IS appropriate
|
## When prose IS appropriate
|
||||||
|
|
||||||
- A **1-3 sentence opening summary** at the top of a complex note (a "lede") — concise enough to scan.
|
- A **1-3 sentence opening summary** at the top of a complex note (a "lede") — concise enough to scan.
|
||||||
|
|
|
||||||
|
|
@ -82,165 +82,6 @@ ${KNOWLEDGE_NOTE_STYLE_GUIDE}
|
||||||
|
|
||||||
The style guide above is the canonical writing style for everything you emit into the body. The objective may specify a particular shape ("3-column markdown table: Location | Local Time | Offset") — when it does, follow it exactly. When it doesn't, walk the ladder above and pick the tightest shape that fits the data.
|
The style guide above is the canonical writing style for everything you emit into the body. The objective may specify a particular shape ("3-column markdown table: Location | Local Time | Offset") — when it does, follow it exactly. When it doesn't, walk the ladder above and pick the tightest shape that fits the data.
|
||||||
|
|
||||||
# Output Block Types
|
|
||||||
|
|
||||||
The note renderer turns specially-tagged fenced code blocks into styled UI: tables, charts, calendars, embeds, and more. Reach for these when the data has structure that benefits from a visual treatment; stay with plain markdown when prose, a markdown table, or bullets carry the meaning just as well. Pick **at most one block per output region** unless the objective asks for a multi-section layout — and follow the exact fence language and shape, since anything unparseable renders as a small "Invalid X block" error card.
|
|
||||||
|
|
||||||
Do **not** emit \`task\` blocks — those are user-authored input mechanisms, not agent outputs.
|
|
||||||
|
|
||||||
## \`table\` — tabular data (JSON)
|
|
||||||
|
|
||||||
Use for: scoreboards, leaderboards, comparisons, multi-row status digests.
|
|
||||||
|
|
||||||
\`\`\`table
|
|
||||||
{
|
|
||||||
"title": "Top stories on Hacker News",
|
|
||||||
"columns": ["Rank", "Title", "Points", "Comments"],
|
|
||||||
"data": [
|
|
||||||
{"Rank": 1, "Title": "Show HN: ...", "Points": 842, "Comments": 312},
|
|
||||||
{"Rank": 2, "Title": "...", "Points": 530, "Comments": 144}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`columns\` (string[]), \`data\` (array of objects keyed by column name). Optional: \`title\`.
|
|
||||||
|
|
||||||
## \`chart\` — line / bar / pie chart (JSON)
|
|
||||||
|
|
||||||
Use for: time series, categorical breakdowns, share-of-total. Skip if a single sentence carries the meaning.
|
|
||||||
|
|
||||||
\`\`\`chart
|
|
||||||
{
|
|
||||||
"chart": "line",
|
|
||||||
"title": "USD/INR — last 7 days",
|
|
||||||
"x": "date",
|
|
||||||
"y": "rate",
|
|
||||||
"data": [
|
|
||||||
{"date": "2026-04-13", "rate": 83.41},
|
|
||||||
{"date": "2026-04-14", "rate": 83.38}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`chart\` ("line" | "bar" | "pie"), \`x\` (field name on each row), \`y\` (field name on each row), and **either** \`data\` (inline array of objects) **or** \`source\` (workspace path to a JSON-array file). Optional: \`title\`.
|
|
||||||
|
|
||||||
## \`mermaid\` — diagrams (raw Mermaid source)
|
|
||||||
|
|
||||||
Use for: relationship maps, flowcharts, sequence diagrams, gantt charts, mind maps.
|
|
||||||
|
|
||||||
\`\`\`mermaid
|
|
||||||
graph LR
|
|
||||||
A[Project Alpha] --> B[Sarah Chen]
|
|
||||||
A --> C[Acme Corp]
|
|
||||||
B --> D[Q3 Launch]
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Body is plain Mermaid source — no JSON wrapper.
|
|
||||||
|
|
||||||
## \`calendar\` — list of events (JSON)
|
|
||||||
|
|
||||||
Use for: upcoming meetings, agenda digests, day/week views.
|
|
||||||
|
|
||||||
\`\`\`calendar
|
|
||||||
{
|
|
||||||
"title": "Today",
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"summary": "1:1 with Sarah",
|
|
||||||
"start": {"dateTime": "2026-04-20T10:00:00-07:00"},
|
|
||||||
"end": {"dateTime": "2026-04-20T10:30:00-07:00"},
|
|
||||||
"location": "Zoom",
|
|
||||||
"conferenceLink": "https://zoom.us/j/..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`events\` (array). Each event optionally has \`summary\`, \`start\`/\`end\` (object with \`dateTime\` ISO string OR \`date\` "YYYY-MM-DD" for all-day), \`location\`, \`htmlLink\`, \`conferenceLink\`, \`source\`. Optional top-level: \`title\`, \`showJoinButton\` (bool).
|
|
||||||
|
|
||||||
## \`email\` — single email or thread digest (JSON)
|
|
||||||
|
|
||||||
Use for: surfacing one important thread — latest message body, summary of prior context, optional draft reply.
|
|
||||||
|
|
||||||
\`\`\`email
|
|
||||||
{
|
|
||||||
"subject": "Q3 launch readiness",
|
|
||||||
"from": "sarah@acme.com",
|
|
||||||
"date": "2026-04-19T16:42:00Z",
|
|
||||||
"summary": "Sarah confirms timeline; flagged blocker on infra capacity.",
|
|
||||||
"latest_email": "Hey — quick update on Q3...\\n\\nThanks,\\nSarah"
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`latest_email\` (string). Optional: \`threadId\`, \`summary\`, \`subject\`, \`from\`, \`to\`, \`date\`, \`past_summary\`, \`draft_response\`, \`response_mode\` ("inline" | "assistant" | "both").
|
|
||||||
|
|
||||||
For digests of **many** threads, prefer a \`table\` (Subject | From | Snippet) — \`email\` is for one thread at a time.
|
|
||||||
|
|
||||||
## \`image\` — single image (JSON)
|
|
||||||
|
|
||||||
Use for: charts, screenshots, photos you have a URL or workspace path for.
|
|
||||||
|
|
||||||
\`\`\`image
|
|
||||||
{
|
|
||||||
"src": "https://example.com/forecast.png",
|
|
||||||
"alt": "Weather forecast",
|
|
||||||
"caption": "Bay Area · April 20"
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`src\` (URL or workspace path). Optional: \`alt\`, \`caption\`.
|
|
||||||
|
|
||||||
## \`embed\` — YouTube / Figma / Tweet embed (JSON)
|
|
||||||
|
|
||||||
Use for: linking to a video, design, or tweet that should render inline.
|
|
||||||
|
|
||||||
\`\`\`embed
|
|
||||||
{
|
|
||||||
"provider": "youtube",
|
|
||||||
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
||||||
"caption": "Latest demo"
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`provider\` ("youtube" | "figma" | "tweet" | "generic"), \`url\`. Optional: \`caption\`.
|
|
||||||
|
|
||||||
## \`iframe\` — arbitrary embedded webpage (JSON)
|
|
||||||
|
|
||||||
Use for: live dashboards, status pages, trackers — anything that has its own webpage and benefits from being live, not snapshotted.
|
|
||||||
|
|
||||||
\`\`\`iframe
|
|
||||||
{
|
|
||||||
"url": "https://status.example.com",
|
|
||||||
"title": "Service status",
|
|
||||||
"height": 600
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`url\` (must be \`https://\` or \`http://localhost\`). Optional: \`title\`, \`caption\`, \`height\` (240–1600), \`allow\` (Permissions-Policy string).
|
|
||||||
|
|
||||||
## \`transcript\` — long transcript (JSON)
|
|
||||||
|
|
||||||
Use for: meeting transcripts, voice-note dumps — bodies that benefit from a collapsible UI.
|
|
||||||
|
|
||||||
\`\`\`transcript
|
|
||||||
{"transcript": "[00:00] Speaker A: Welcome everyone..."}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`transcript\` (string).
|
|
||||||
|
|
||||||
## \`prompt\` — starter Copilot prompt (YAML)
|
|
||||||
|
|
||||||
Use for: end-of-output "next step" cards. The user clicks **Run** and the chat sidebar opens with the underlying instruction submitted to Copilot, with this note attached as a file mention.
|
|
||||||
|
|
||||||
\`\`\`prompt
|
|
||||||
label: Draft replies to today's emails
|
|
||||||
instruction: |
|
|
||||||
For each unanswered email in the digest above, draft a 2-line reply
|
|
||||||
in my voice and present them as a checklist for me to approve.
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Required: \`label\` (short title shown on the card), \`instruction\` (the longer prompt). Note: this block uses **YAML**, not JSON.
|
|
||||||
|
|
||||||
# Interpreting the Objective
|
# Interpreting the Objective
|
||||||
|
|
||||||
The objective was authored in a prior conversation you cannot see. Treat it as a **self-contained spec**. If ambiguous, pick what a reasonable user of a knowledge tracker would expect:
|
The objective was authored in a prior conversation you cannot see. Treat it as a **self-contained spec**. If ambiguous, pick what a reasonable user of a knowledge tracker would expect:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue