wip: more rework and docs

This commit is contained in:
2026-02-26 11:01:17 +01:00
parent affd62ca79
commit 00a9d22a36
18 changed files with 149 additions and 226 deletions

View File

@@ -328,7 +328,7 @@ Available UI Render Tools (use these to show rich interactive elements):
- render_card: Show an information card with title, body, and action buttons.
- render_metric: Show a single KPI or statistic prominently.
- render_list: Show a bulleted list of items.
- render_tabs: Organize information into switchable tabs.
- render_tabs: Organize information into switchable tabs. Tab content supports all content types: text, metrics, lists, charts, and tables.
When answering questions:
1. USE THE TOOLS to find information. Never make up data about posts or media.
@@ -338,7 +338,8 @@ When answering questions:
5. When asked to describe or analyze an image, use the view_image tool to see the actual image content.
6. When presenting data, statistics, or comparisons, prefer using render tools (render_chart, render_table, render_metric) to show rich interactive UI instead of plain text.
7. When you need user input for a multi-field operation, use render_form to present a structured form.
8. Use render_card with action buttons when presenting items the user might want to navigate to (e.g., posts, media).`;
8. Use render_card with action buttons when presenting items the user might want to navigate to (e.g., posts, media).
9. When comparing data across multiple dimensions (e.g., statistics per year), use render_tabs with embedded charts or tables in each tab.`;
}
/**

View File

@@ -1018,7 +1018,7 @@ export class OpenCodeManager {
},
{
name: 'render_tabs',
description: 'Render a tabbed interface in the chat UI. Use this when you want to organize information into multiple tabs that the user can switch between.',
description: 'Render a tabbed interface in the chat UI. Use this when you want to organize information into multiple tabs that the user can switch between. Each tab can contain any combination of text, metrics, lists, charts, and tables.',
input_schema: {
type: 'object',
properties: {
@@ -1033,7 +1033,28 @@ export class OpenCodeManager {
items: {
type: 'object',
properties: {
type: { type: 'string', enum: ['text', 'metric', 'list'], description: 'Content type' },
type: { type: 'string', enum: ['text', 'metric', 'list', 'chart', 'table'], description: 'Content type' },
text: { type: 'string', description: 'Text content (for type text)' },
label: { type: 'string', description: 'Label (for type metric)' },
value: { type: 'string', description: 'Display value (for type metric)' },
title: { type: 'string', description: 'Title (for type list, chart, or table)' },
items: { type: 'array', items: { type: 'string' }, description: 'Items (for type list)' },
chartType: { type: 'string', enum: ['bar', 'line', 'pie'], description: 'Chart type (for type chart)' },
series: {
type: 'array',
items: {
type: 'object',
properties: { label: { type: 'string' }, value: { type: 'number' } },
required: ['label', 'value'],
},
description: 'Data series (for type chart)',
},
columns: { type: 'array', items: { type: 'string' }, description: 'Column headers (for type table)' },
rows: {
type: 'array',
items: { type: 'array', items: { type: 'string' } },
description: 'Table rows (for type table)',
},
},
required: ['type'],
},

View File

@@ -17,7 +17,7 @@ interface SeriesEntry {
export const A2UIChart: React.FC<A2UIComponentProps> = ({ component }) => {
const chartType = String(component.properties.chartType ?? 'bar');
const title = component.properties.title as string | undefined;
const series = (component.boundValue as SeriesEntry[]) ?? [];
const series = (component.boundValue as SeriesEntry[]) ?? (component.properties.series as SeriesEntry[]) ?? [];
const maxValue = Math.max(...series.map((entry) => entry.value), 0);
return (

View File

@@ -11,7 +11,7 @@ interface A2UIComponentProps {
export const A2UIList: React.FC<A2UIComponentProps> = ({ component }) => {
const title = component.properties.title as string | undefined;
const items = (component.boundValue as string[]) ?? [];
const items = (component.boundValue as string[]) ?? (component.properties.items as string[]) ?? [];
return (
<div>

View File

@@ -11,7 +11,7 @@ interface A2UIComponentProps {
export const A2UITable: React.FC<A2UIComponentProps> = ({ component }) => {
const columns = (component.properties.columns as string[]) ?? [];
const rows = (component.boundValue as string[][]) ?? [];
const rows = (component.boundValue as string[][]) ?? (component.properties.rows as string[][]) ?? [];
const title = component.properties.title as string | undefined;
return (

View File

@@ -250,7 +250,7 @@ export const AssistantSidebar: React.FC = () => {
{actionError && <p className="assistant-sidebar-error chat-surface-error">{actionError}</p>}
{surfaceMode.showWelcomeTips && messages.length === 0 && !isStreaming && (
{surfaceMode.showWelcomeTips && messages.filter(m => m.role !== 'system' && m.role !== 'tool').length === 0 && !isStreaming && (
<div className="assistant-sidebar-raw-message chat-surface-section">
{tr('chat.welcomeDescription')}
</div>

View File

@@ -98,6 +98,8 @@
justify-content: center;
text-align: center;
padding: 32px;
min-height: 100%;
box-sizing: border-box;
color: var(--vscode-descriptionForeground);
}

View File

@@ -344,17 +344,17 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({ conversationId }) => {
</div>
<div className="chat-messages chat-surface-scroll">
{surfaceMode.showWelcomeTips && messages.length === 0 && !isStreaming && (
{surfaceMode.showWelcomeTips && messages.filter(m => m.role !== 'system' && m.role !== 'tool').length === 0 && !isStreaming && (
<div className="chat-welcome">
<div className="chat-welcome-icon">{'\u{1F916}'}</div>
<h2>{tr('chat.welcomeTitle')}</h2>
<p>{tr('chat.welcomeDescription')}</p>
<ul>
<li>{tr('chat.welcomeTipSearch')}</li>
<li>{tr('chat.welcomeTipDetails')}</li>
<li>{tr('chat.welcomeTipTags')}</li>
<li>{tr('chat.welcomeTipChart')}</li>
<li>{tr('chat.welcomeTipTable')}</li>
<li>{tr('chat.welcomeTipMetadata')}</li>
<li>{tr('chat.welcomeTipImages')}</li>
<li>{tr('chat.welcomeTipTabs')}</li>
</ul>
</div>
)}

View File

@@ -196,12 +196,12 @@
"chat.apiKeyValidationFailed": "API-Schlüssel konnte nicht validiert werden.",
"chat.newChat": "Neuer Chat",
"chat.welcomeTitle": "Willkommen beim KI-Assistenten",
"chat.welcomeDescription": "Ich kann dir helfen, deine Beiträge und Medien zu verwalten. Frag mich zum Beispiel:",
"chat.welcomeDescription": "Ich kann dir helfen, deinen Blog mit anschaulichen Darstellungen zu verwalten. Frag mich zum Beispiel:",
"chat.welcomeTipSearch": "Nach Beiträgen zu einem bestimmten Thema suchen",
"chat.welcomeTipDetails": "Details zu einem bestimmten Beitrag anzeigen",
"chat.welcomeTipTags": "Alle Tags oder Kategorien in deinem Blog auflisten",
"chat.welcomeTipChart": "Ein Diagramm der pro Monat veröffentlichten Beiträge anzeigen",
"chat.welcomeTipTable": "Meine letzten Beiträge in einer Tabelle vergleichen",
"chat.welcomeTipMetadata": "Metadaten für Beiträge oder Medien aktualisieren",
"chat.welcomeTipImages": "Alle Bilder in deiner Mediathek auflisten",
"chat.welcomeTipTabs": "Beitragsstatistiken nach Jahr in Tabs mit Diagrammen anzeigen",
"chat.role.you": "Du",
"chat.role.assistant": "Assistent",
"chat.stop": "Stopp",

View File

@@ -196,12 +196,12 @@
"chat.apiKeyValidationFailed": "Failed to validate API key.",
"chat.newChat": "New Chat",
"chat.welcomeTitle": "Welcome to the AI Assistant",
"chat.welcomeDescription": "I can help you manage your posts and media. Try asking me to:",
"chat.welcomeDescription": "I can help you manage your blog with rich visualizations. Try asking me to:",
"chat.welcomeTipSearch": "Search for posts about a specific topic",
"chat.welcomeTipDetails": "Get details about a specific post",
"chat.welcomeTipTags": "List all tags or categories in your blog",
"chat.welcomeTipChart": "Show a chart of posts published per month",
"chat.welcomeTipTable": "Compare my recent posts in a table",
"chat.welcomeTipMetadata": "Update metadata for posts or media",
"chat.welcomeTipImages": "List all images in your media library",
"chat.welcomeTipTabs": "Show post statistics by year in tabs with charts",
"chat.role.you": "You",
"chat.role.assistant": "Assistant",
"chat.stop": "Stop",

View File

@@ -196,12 +196,12 @@
"chat.apiKeyValidationFailed": "No se pudo validar la clave API.",
"chat.newChat": "Nuevo chat",
"chat.welcomeTitle": "Bienvenido al asistente de IA",
"chat.welcomeDescription": "Puedo ayudarte a gestionar tus entradas y medios. Prueba a pedirme que:",
"chat.welcomeDescription": "Puedo ayudarte a gestionar tu blog con visualizaciones interactivas. Prueba a pedirme que:",
"chat.welcomeTipSearch": "Busque entradas sobre un tema específico",
"chat.welcomeTipDetails": "Muestre detalles de una entrada específica",
"chat.welcomeTipTags": "Lista todas las etiquetas o categorías de tu blog",
"chat.welcomeTipChart": "Muestre un gráfico de entradas publicadas por mes",
"chat.welcomeTipTable": "Compare mis entradas recientes en una tabla",
"chat.welcomeTipMetadata": "Actualice metadatos de entradas o medios",
"chat.welcomeTipImages": "Liste todas las imágenes de tu biblioteca de medios",
"chat.welcomeTipTabs": "Muestre estadísticas por año en pestañas con gráficos",
"chat.role.you": "Tú",
"chat.role.assistant": "Asistente",
"chat.stop": "Detener",

View File

@@ -196,12 +196,12 @@
"chat.apiKeyValidationFailed": "Impossible de valider la clé API.",
"chat.newChat": "Nouveau chat",
"chat.welcomeTitle": "Bienvenue dans lassistant IA",
"chat.welcomeDescription": "Je peux vous aider à gérer vos articles et médias. Essayez par exemple :",
"chat.welcomeDescription": "Je peux vous aider à gérer votre blog avec des visualisations riches. Essayez par exemple :",
"chat.welcomeTipSearch": "Rechercher des articles sur un sujet précis",
"chat.welcomeTipDetails": "Afficher les détails dun article précis",
"chat.welcomeTipTags": "Lister toutes les étiquettes ou catégories de votre blog",
"chat.welcomeTipChart": "Afficher un graphique des articles publiés par mois",
"chat.welcomeTipTable": "Comparer mes derniers articles dans un tableau",
"chat.welcomeTipMetadata": "Mettre à jour les métadonnées des articles ou médias",
"chat.welcomeTipImages": "Lister toutes les images de votre bibliothèque média",
"chat.welcomeTipTabs": "Afficher les statistiques par année dans des onglets avec graphiques",
"chat.role.you": "Vous",
"chat.role.assistant": "Assistant IA",
"chat.stop": "Arrêter",

View File

@@ -196,12 +196,12 @@
"chat.apiKeyValidationFailed": "Impossibile convalidare la chiave API.",
"chat.newChat": "Nuova chat",
"chat.welcomeTitle": "Benvenuto nellassistente IA",
"chat.welcomeDescription": "Posso aiutarti a gestire post e media. Prova a chiedermi di:",
"chat.welcomeDescription": "Posso aiutarti a gestire il tuo blog con visualizzazioni interattive. Prova a chiedermi di:",
"chat.welcomeTipSearch": "Cercare post su un argomento specifico",
"chat.welcomeTipDetails": "Ottieni dettagli su un post specifico",
"chat.welcomeTipTags": "Elenca tutti i tag o le categorie del tuo blog",
"chat.welcomeTipChart": "Mostrare un grafico dei post pubblicati per mese",
"chat.welcomeTipTable": "Confrontare i miei post recenti in una tabella",
"chat.welcomeTipMetadata": "Aggiornare i metadati di post o media",
"chat.welcomeTipImages": "Elenca tutte le immagini nella tua libreria media",
"chat.welcomeTipTabs": "Mostrare statistiche per anno in schede con grafici",
"chat.role.you": "Tu",
"chat.role.assistant": "Assistente",
"chat.stop": "Ferma",

View File

@@ -359,35 +359,10 @@ const DATA_STRUCTURES_V1: PythonApiDataStructureContractV1[] = [
{ name: 'maskedKey', type: 'string', required: true, description: 'Masked key representation for UI display.' },
],
},
{
name: 'ProtocolNeedsInputField',
description: 'A required clarification input field used for needsInput prompts.',
fields: [
{ name: 'key', type: 'string', required: true, description: 'Stable field key used in submitted values.' },
{ name: 'label', type: 'string', required: true, description: 'User-facing field label.' },
{ name: 'inputType', type: "'text' | 'textarea' | 'select' | 'checkbox' | 'date' | 'number'", required: true, description: 'Rendered input control type.' },
{ name: 'required', type: 'boolean', required: false, description: 'Whether user input is required.' },
{ name: 'options', type: 'Array<{ label: string; value: string }>', required: false, description: 'Selectable options for select controls.' },
{ name: 'placeholder', type: 'string', required: false, description: 'Optional placeholder text for text-like controls.' },
{ name: 'defaultValue', type: 'string | number | boolean', required: false, description: 'Default field value shown in UI.' },
],
},
{
name: 'ProtocolAction',
description: 'A declarative assistant action exposed to the UI runtime.',
fields: [
{ name: 'id', type: 'string', required: true, description: 'Stable action id within a response envelope.' },
{ name: 'action', type: 'string', required: true, description: 'Action name to dispatch in renderer.' },
{ name: 'label', type: 'string', required: false, description: 'Optional user-facing action label.' },
{ name: 'payload', type: 'Record<string, unknown>', required: false, description: 'Optional action payload arguments.' },
{ name: 'policy', type: "'silent' | 'confirm' | 'danger'", required: true, description: 'Action confirmation policy level.' },
{ name: 'requiresConfirmation', type: 'boolean', required: true, description: 'Whether confirmation is required before dispatch.' },
],
},
];
export const BDS_PYTHON_API_CONTRACT_V1: PythonApiContractV1 = {
version: '1.5.0',
version: '1.6.0',
generatedAt: '2026-02-25T00:00:00.000Z',
methods: METHODS_V1,
dataStructures: DATA_STRUCTURES_V1,