Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add agentic task log demo with reusable treebark templates #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
Copilot wants to merge 30 commits into main
base: main
Choose a base branch
Loading
from copilot/add-demo-log-output
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d238f26
Initial plan
Copilot Nov 19, 2025
5047128
Add agentic task log demo with chat bubbles and execution phases
Copilot Nov 19, 2025
23dbc8c
Fix chat bubble styling: user bubbles now blue and right-aligned
Copilot Nov 19, 2025
9222ce3
Add chart visualizations to show analysis results
Copilot Nov 20, 2025
af9d770
Change charts to show agent execution metrics instead of business data
Copilot Nov 20, 2025
982fe7f
Implement reusable treebark templates with templateId head syntax
Copilot Dec 9, 2025
23f283e
Fix template resolution logic per code review
Copilot Dec 9, 2025
e7e0ba3
Update schema source to support reusable treebark templates
Copilot Dec 9, 2025
e3d7cb6
Simplify treebark templates: remove head syntax, use templateId property
Copilot Dec 9, 2025
c2d7c1a
Use templateId property to explicitly control SET vs GET behavior
Copilot Dec 9, 2025
751728b
Fix: string template without templateId is treated as inline, not lookup
Copilot Dec 9, 2025
ea8a6b4
Fix comment: reference template example should use templateId
Copilot Dec 9, 2025
f5b04c7
Fix validation: allow templateId without template for GET pattern
Copilot Dec 9, 2025
7609d7e
Remove treebarkTemplates from resources - not needed for bookkeeping
Copilot Dec 10, 2025
71835aa
Use TemplateElement type instead of object for template values
Copilot Dec 10, 2025
da23414
Remove usage examples from treebark plugin comment
Copilot Dec 10, 2025
6a30976
Revert schema change: template should be optional TemplateElement
Copilot Dec 10, 2025
cd0ed4c
Remove treebarkTemplates from demo, use templateId SET/GET pattern
Copilot Dec 10, 2025
5c3897c
Replace templateId with explicit setTemplate/getTemplate properties
Copilot Dec 10, 2025
e3a0d3d
Use reusable templates for execution logs and replace variables with ...
Copilot Dec 10, 2025
dde4540
Fix schema validation: use dataSourceName/content and variableId
Copilot Dec 10, 2025
7e94893
Wrap execution log content in arrays as required by schema
Copilot Dec 10, 2025
16ecc87
Use Vega formula transform to add phase field instead of nesting data
Copilot Dec 10, 2025
075e4e9
Consolidate execution logs into single phases array data source
Copilot Dec 10, 2025
e3cacf9
Update compiler validation for treebark setTemplate/getTemplate
Copilot Dec 10, 2025
1f072e6
Fix validation logic order and improve error message clarity
Copilot Dec 11, 2025
2299b47
Relax validation: reorder checks to show clearer error messages for t...
Copilot Dec 11, 2025
583e076
Add reusable template example to treebark primer document
Copilot Dec 11, 2025
f34bb14
Add setTemplate and getTemplate to compiler markdown output
Copilot Dec 11, 2025
1f3d7c2
Fix execution phases: use flat arrays and add phase headers to markdown
Copilot Dec 11, 2025
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
8 changes: 7 additions & 1 deletion packages/compiler/src/md.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: Ve
break;
}
case 'treebark': {
const { template, data, variableId } = element;
const { template, data, variableId, setTemplate, getTemplate } = element;
const treebarkSpec: Plugins.TreebarkSpec = {
template,
};
Expand All @@ -316,6 +316,12 @@ function groupMarkdown(group: ElementGroup, variables: Variable[], vegaScope: Ve
if (variableId) {
treebarkSpec.variableId = variableId;
}
if (setTemplate) {
treebarkSpec.setTemplate = setTemplate;
}
if (getTemplate) {
treebarkSpec.getTemplate = getTemplate;
}
addSpec('treebark', treebarkSpec);
break;
}
Expand Down
43 changes: 38 additions & 5 deletions packages/compiler/src/validate/element.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,34 @@ export async function validateElement(element: PageElement, groupIndex: number,
case 'treebark': {
const treebarkElement = element as TreebarkElement;

// Template is required
if (!treebarkElement.template) {
errors.push('Treebark element must have a template property');
} else {
errors.push(...validateOptionalObject(treebarkElement.template, 'template', 'Treebark'));
// Validate: setTemplate and getTemplate are mutually exclusive (check first)
if (treebarkElement.setTemplate && treebarkElement.getTemplate) {
errors.push('Treebark element cannot have both setTemplate and getTemplate');
}

// Validate: setTemplate requires template
if (treebarkElement.setTemplate && !treebarkElement.template) {
errors.push('Treebark element with setTemplate must have a template property');
}

// Validate: getTemplate should not have template
if (treebarkElement.getTemplate && treebarkElement.template) {
errors.push('Treebark element with getTemplate should not have a template property (it references an existing template)');
}

// Validate: at least one of template, setTemplate, or getTemplate must be present
// (Check this after setTemplate/getTemplate-specific validations to avoid confusing messages)
if (!treebarkElement.template && !treebarkElement.setTemplate && !treebarkElement.getTemplate) {
errors.push('Treebark element must have at least one of: template, setTemplate, or getTemplate');
}

// Validate template if present - can be object or string
if (treebarkElement.template !== undefined) {
if (typeof treebarkElement.template !== 'object' && typeof treebarkElement.template !== 'string') {
errors.push('Treebark element template must be an object or a string');
} else if (typeof treebarkElement.template === 'object' && treebarkElement.template !== null) {
errors.push(...validateOptionalObject(treebarkElement.template, 'template', 'Treebark'));
}
}

// Validate data if present
Expand All @@ -153,6 +176,16 @@ export async function validateElement(element: PageElement, groupIndex: number,
if (treebarkElement.variableId) {
errors.push(...validateVariableID(treebarkElement.variableId));
}

// Validate setTemplate if present
if (treebarkElement.setTemplate) {
errors.push(...validateRequiredString(treebarkElement.setTemplate, 'setTemplate', 'Treebark'));
}

// Validate getTemplate if present
if (treebarkElement.getTemplate) {
errors.push(...validateRequiredString(treebarkElement.getTemplate, 'getTemplate', 'Treebark'));
}

break;
}
Expand Down
117 changes: 70 additions & 47 deletions packages/markdown/src/plugins/treebark.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,57 +1,26 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/

/*
* Treebark Plugin - Renders cards and structured HTML using Treebark templates
*
* USAGE EXAMPLES:
*
* 1. Static Data:
* ```treebark
* {
* "template": {
* "div": {
* "class": "card",
* "$children": ["Hello {{name}}!"]
* }
* },
* "data": { "name": "World" }
* }
* ```
*
* 2. Dynamic Data via Signal (data source → cards):
* ```treebark
* {
* "template": {
* "div": {
* "class": "card",
* "$bind": ".",
* "$children": [
* { "h3": "{{Title}}" },
* { "p": "{{Director}}" }
* ]
* }
* },
* "variableId": "movieData"
* }
* ```
*/
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/

/**
* Treebark Plugin - Renders cards and structured HTML using Treebark templates
*/

import { Plugin, RawFlaggableSpec, IInstance } from '../factory.js';
import { ErrorHandler } from '../renderer.js';
import { flaggablePlugin } from './config.js';
import { pluginClassName } from './util.js';
import { PluginNames } from './interfaces.js';
import { TreebarkElementProps } from '@microsoft/chartifact-schema';
import { renderToDOM } from 'treebark';
import { renderToDOM, TemplateElement } from 'treebark';

interface TreebarkInstance {
id: string;
spec: TreebarkElementProps;
container: Element;
lastRenderedData: string;
resolvedTemplate: TemplateElement;
}

export interface TreebarkSpec extends TreebarkElementProps { }
Expand All @@ -63,10 +32,34 @@ function inspectTreebarkSpec(spec: TreebarkSpec): RawFlaggableSpec<TreebarkSpec>
const reasons: string[] = [];
let hasFlags = false;

// Validate template
if (!spec.template || typeof spec.template !== 'object') {
// Validate: either template, setTemplate, or getTemplate must be present
if (!spec.template && !spec.setTemplate && !spec.getTemplate) {
hasFlags = true;
reasons.push('Either template, setTemplate, or getTemplate is required');
}

// Validate: setTemplate and getTemplate are mutually exclusive
if (spec.setTemplate && spec.getTemplate) {
hasFlags = true;
reasons.push('setTemplate and getTemplate cannot both be specified');
}

// Validate: setTemplate requires template
if (spec.setTemplate && !spec.template) {
hasFlags = true;
reasons.push('template must be an object');
reasons.push('setTemplate requires template to be provided');
}

// Validate: getTemplate should not have template
if (spec.getTemplate && spec.template) {
hasFlags = true;
reasons.push('getTemplate should not have template (it references an existing template)');
}

// If template is provided, it must be object or string
if (spec.template && typeof spec.template !== 'object' && typeof spec.template !== 'string') {
hasFlags = true;
reasons.push('template must be an object or a string');
}

// If both data and variableId are provided, warn but allow it
Expand All @@ -84,8 +77,11 @@ function inspectTreebarkSpec(spec: TreebarkSpec): RawFlaggableSpec<TreebarkSpec>
export const treebarkPlugin: Plugin<TreebarkSpec> = {
...flaggablePlugin<TreebarkSpec>(pluginName, className, inspectTreebarkSpec),
hydrateComponent: async (renderer, errorHandler, specs) => {
const { signalBus } = renderer;
const { signalBus, document } = renderer;
const treebarkInstances: TreebarkInstance[] = [];

// Template registry: starts empty, adds inline-defined templates during hydration
const templateRegistry: Record<string, TemplateElement> = {};

for (let index = 0; index < specs.length; index++) {
const specReview = specs[index];
Expand All @@ -98,6 +94,32 @@ export const treebarkPlugin: Plugin<TreebarkSpec> = {
}

const spec = specReview.approvedSpec;

let resolvedTemplate: TemplateElement;

// Explicit SET/GET semantics
if (spec.setTemplate) {
// SET: Register and use the template
templateRegistry[spec.setTemplate] = spec.template;
resolvedTemplate = spec.template;
} else if (spec.getTemplate) {
// GET: Lookup the template
resolvedTemplate = templateRegistry[spec.getTemplate];
if (!resolvedTemplate) {
container.innerHTML = `<div class="error">Template '${spec.getTemplate}' not found</div>`;
errorHandler(
new Error(`Template '${spec.getTemplate}' not found`),
pluginName,
index,
'resolve',
container
);
continue;
}
} else {
// INLINE: Use template directly (can be object or string)
resolvedTemplate = spec.template;
}

// Create container for the rendered content
container.innerHTML = `<div class="treebark-loading">Loading...</div>`;
Expand All @@ -107,6 +129,7 @@ export const treebarkPlugin: Plugin<TreebarkSpec> = {
spec,
container,
lastRenderedData: null,
resolvedTemplate,
};
treebarkInstances.push(treebarkInstance);

Expand Down Expand Up @@ -155,7 +178,7 @@ async function renderTreebark(
errorHandler: ErrorHandler,
index: number
) {
const { spec, container } = instance;
const { container, resolvedTemplate } = instance;

try {
// Create a stable key for caching based on data content
Expand All @@ -166,9 +189,9 @@ async function renderTreebark(
return;
}

// Render using treebark
// Render using treebark with resolved template
const html = renderToDOM({
template: spec.template,
template: resolvedTemplate,
data: data as any,
});

Expand Down
8 changes: 7 additions & 1 deletion packages/schema-doc/src/interactive.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,17 @@ export interface TreebarkElement extends TreebarkElementProps {

export interface TreebarkElementProps extends OptionalVariableControl {
/** Treebark template object for rendering HTML structure */
template: TemplateElement;
template?: TemplateElement;

/** Static data object (optional) */
data?: object;

/** Register a template with this name for reuse (SET operation) */
setTemplate?: string;

/** Use a previously registered template by name (GET operation) */
getTemplate?: string;

/** Dynamic option: variableId to intake a signal and behave as data */
}

Expand Down
2 changes: 1 addition & 1 deletion packages/schema-doc/src/page.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface InteractiveDocument {
style?: PageStyle;

resources?: {
charts?: { [chartKey: string]: Vega_or_VegaLite_spec }
charts?: { [chartKey: string]: Vega_or_VegaLite_spec };
};

/** Optional comments from the author */
Expand Down
Loading

AltStyle によって変換されたページ (->オリジナル) /