feat: Create tabs in stories - #1211
Conversation
Introduces the <tabs>/<tab> story markup and all UI for it: tabbed rendering on every surface (side panel, expanded, standalone, shared), markup validation, a switch-only tab bar in preview, and a tab-aware visual editor (add/rename/reorder/delete + per-tab content editing). Agent guidance for tabs lands in a follow-up commit on this branch.
🧹 Preview RemovedThe preview deployment for this PR has been cleaned up. |
…tory thumbnails for stories that use tabs
There was a problem hiding this comment.
All reported issues were addressed across 25 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Bl3f
left a comment
There was a problem hiding this comment.
I think there are 2 bugs that should be fixed in the workflow (and rather than fixed it's more than your PR added this kind of wrong behaviour):
- when you edit a story (there is already a story and you asked for a change) to add a tab it empties the story without any warning which is a bit weird
- add the end of the edition (when the streaming restart on the chat) the story falls back to the previous one and when the stream ends on the chat the story finally ends on the edited story
| 'SQL result tables are embedded via <table query_id="..." title="..." />.', | ||
| 'Use <grid cols="2">...</grid> to display charts side by side in a responsive grid.', | ||
| 'Use <tabs> with <tab title="...">...</tab> blocks to organize a story into top-level tabs.', | ||
| 'Default to a single flowing story. Use <tabs> only when the user asks for tabs, or when the content splits into clearly distinct sections that are better separated than stacked (e.g. overview vs. detail, one topic/department/metric per tab). Avoid tabs for a short or single-topic story. Always follow the user\'s explicit request (e.g. "a tab per chart" means one chart per tab). When using tabs, the story must start with <tabs> and contain only <tab title="...">...</tab> blocks — no text outside a tab.', |
There was a problem hiding this comment.
Is it the best thing to force the story to start with when we have tabs? cant we just insert a or even just insert a and consider what after in the tab?
Or maybe there is a token optimisation to do, because the thing is when you move a story from non-tab to tab it requires to rewrite the full story then in the current situation of the story tool (mode=update), a single search and replace...
There was a problem hiding this comment.
You're right
At first I wanted to do it this way because I wanted no content outside tabs since that would mean lower level tabs and we didn't want that. I also wanted it to be efficient for the UI to know if we're using tabs or not.
But actually even so we don't need this at the beginning, we can just grep "<tab" to know if tabs are enabled and declare a compilation error if there is content outside tabs when they're enabled.
So I'll remove the <tabs> at the beginning
Still for token cost it's still as costly and that's because need to ensure there is no content outside tabs blocks when tabs are enabled. So we need to rewrite the full story it seems.
|
|
||
| {editable && editingIndex !== index && ( | ||
| <div className='mr-1 flex items-center opacity-0 transition-opacity group-hover/tab:opacity-100 group-focus-within/tab:opacity-100'> | ||
| <AlertDialog> |
There was a problem hiding this comment.
There was a problem hiding this comment.
There was a problem hiding this comment.
I prefer we stick to the round button and keep the same style. It's weird, you should be able to do whatever you want.
There was a problem hiding this comment.
okay okay I'll do that
| .optional() | ||
| .describe( | ||
| 'The markdown content. Required for "create" (initial content) and "replace" (new content). Can include charts via <chart query_id="..." /> blocks and SQL tables via <table query_id="..." /> blocks. Use <grid cols="2">...</grid> to lay out charts side by side in a responsive grid.', | ||
| `The markdown content. Required for "create" (initial content) and "replace" (new content). Can include charts via <chart query_id="..." /> blocks and SQL tables via <table query_id="..." /> blocks. Use <grid cols="2">...</grid> to lay out charts side by side in a responsive grid. Default to a single flowing story. Use <tabs> only when the user asks for tabs, or when the content splits into clearly distinct sections that are better separated than stacked (e.g. overview vs. detail, one topic/department/metric per tab). Avoid tabs for a short or single-topic story. Always follow the user's explicit request (e.g. "a tab per chart" → one chart per tab). When using tabs, the story must start with <tabs> and contain only <tab title="…">…</tab> blocks — no text outside a tab. |
There was a problem hiding this comment.
Not sure we need to details or copy the same text as the description of the tools, we can mention the tabs feature but it does not require to repeat the same desc as we define in apps/backend/src/agents/tools/story.ts
There was a problem hiding this comment.
you're right, I changed that
|
You're right there were two weird bugs in the preview when adding tabs. One where preview went blank and another where we would come back to old version for a moment before showing the final version with tabs added. |
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 17 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Replace shadcn AlertDialog with Dialog styled like RemoveMemberDialog: left-aligned title/text and right-aligned rounded-full outline/destructive buttons.
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Previously shouldUseDraftStory keyed off chat-wide isAgentRunning, so any agent run in the chat could yank a story viewer from a selected historical version to the latest draft. Drive the override and the completion bridge off this story's own streaming state (draftStory.isStreaming) instead, so version navigation is preserved for stories that are not being edited.
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic


Summary
Adds tabs to stories via
<tabs><tab title="…">…</tab></tabs>markup.<tabs>when the prompt calls for it (schema + prompt examples).Closes #1126
Demo
CleanShot.2026-07-19.at.13.46.52.mp4