From a166fcf5145a9474e7abd94595e04d15a32b4c54 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 19 Feb 2026 19:19:47 -0800 Subject: [PATCH 01/14] Add basic types for the generic markdown renderer. --- renderers/web_core/src/v0_8/types/types.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/renderers/web_core/src/v0_8/types/types.ts b/renderers/web_core/src/v0_8/types/types.ts index 723dd9ea1..6818c5717 100644 --- a/renderers/web_core/src/v0_8/types/types.ts +++ b/renderers/web_core/src/v0_8/types/types.ts @@ -530,3 +530,28 @@ export declare interface Surface { components: Map; styles: Record; } + +// Markdown rendering +/** + * Renders `markdown` using `options`. + * @returns The rendered HTML as a string. + */ +export declare type MarkdownRenderer = (markdown: string, options?: MarkdownRendererOptions) => string; + +/** + * A map of tag names to a list of classnames to be applied to a tag. + * + * For example, if you want to apply the class "my-class" to all "h1" tags, + * you would use `{"h1": ["my-class"]}`. + */ +export declare type MarkdownRendererTagClassMap = Record; + +/** + * The options for the markdown renderer passed from A2UI. + * + * This includes the tagClassMap, which is a map of tag names to a list of + * classnames to be applied to each tag, and configuration for the sanitizer. + */ +export declare type MarkdownRendererOptions = { + tagClassMap?: MarkdownRendererTagClassMap; +} From 07628e00e608ad8398a7755f4f271aafe63f59d7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 19 Feb 2026 19:21:10 -0800 Subject: [PATCH 02/14] Remove MarkdownIt from the lit renderer, and instead use an injected markdown renderer function. --- renderers/lit/package.json | 2 +- renderers/lit/src/0.8/ui/context/theme.ts | 2 + .../lit/src/0.8/ui/directives/markdown.ts | 130 ++++-------------- renderers/lit/src/0.8/ui/text.ts | 8 ++ 4 files changed, 35 insertions(+), 107 deletions(-) diff --git a/renderers/lit/package.json b/renderers/lit/package.json index 252709906..192498e70 100644 --- a/renderers/lit/package.json +++ b/renderers/lit/package.json @@ -89,7 +89,7 @@ "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", "lit": "^3.3.1", - "markdown-it": "^14.1.0", + "@a2ui/markdown-it-shared": "file:../markdown-it-shared", "signal-utils": "^0.21.1", "@a2ui/web_core": "file:../web_core" } diff --git a/renderers/lit/src/0.8/ui/context/theme.ts b/renderers/lit/src/0.8/ui/context/theme.ts index 646c58a7e..e3b4ba4c9 100644 --- a/renderers/lit/src/0.8/ui/context/theme.ts +++ b/renderers/lit/src/0.8/ui/context/theme.ts @@ -18,3 +18,5 @@ import { createContext } from "@lit/context"; import * as Types from "@a2ui/web_core/types/types"; export const themeContext = createContext("A2UITheme"); + +export const markdownRenderer = createContext("A2UIMarkdown"); diff --git a/renderers/lit/src/0.8/ui/directives/markdown.ts b/renderers/lit/src/0.8/ui/directives/markdown.ts index faf1b56a8..0e2df787b 100644 --- a/renderers/lit/src/0.8/ui/directives/markdown.ts +++ b/renderers/lit/src/0.8/ui/directives/markdown.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { noChange } from "lit"; +import { html, noChange, TemplateResult } from "lit"; import { Directive, DirectiveParameters, @@ -22,31 +22,13 @@ import { directive, } from "lit/directive.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; -import MarkdownIt from "markdown-it"; -import { RenderRule } from "markdown-it/lib/renderer.mjs"; -import * as Sanitizer from "./sanitizer.js"; +import * as Types from "@a2ui/web_core/types/types"; class MarkdownDirective extends Directive { - #markdownIt = MarkdownIt({ - highlight: (str, lang) => { - switch (lang) { - case "html": { - const iframe = document.createElement("iframe"); - iframe.classList.add("html-view"); - iframe.srcdoc = str; - iframe.sandbox = ""; - return iframe.innerHTML; - } - - default: - return Sanitizer.escapeNodeText(str); - } - }, - }); #lastValue: string | null = null; #lastTagClassMap: string | null = null; - update(_part: Part, [value, tagClassMap]: DirectiveParameters) { + update(_part: Part, [value, markdownRenderer, tagClassMap]: DirectiveParameters) { if ( this.#lastValue === value && JSON.stringify(tagClassMap) === this.#lastTagClassMap @@ -56,97 +38,33 @@ class MarkdownDirective extends Directive { this.#lastValue = value; this.#lastTagClassMap = JSON.stringify(tagClassMap); - return this.render(value, tagClassMap); - } - - #originalClassMap = new Map(); - #applyTagClassMap(tagClassMap: Record) { - Object.entries(tagClassMap).forEach(([tag]) => { - let tokenName; - switch (tag) { - case "p": - tokenName = "paragraph"; - break; - case "h1": - case "h2": - case "h3": - case "h4": - case "h5": - case "h6": - tokenName = "heading"; - break; - case "ul": - tokenName = "bullet_list"; - break; - case "ol": - tokenName = "ordered_list"; - break; - case "li": - tokenName = "list_item"; - break; - case "a": - tokenName = "link"; - break; - case "strong": - tokenName = "strong"; - break; - case "em": - tokenName = "em"; - break; - } - - if (!tokenName) { - return; - } - - const key = `${tokenName}_open`; - this.#markdownIt.renderer.rules[key] = ( - tokens, - idx, - options, - _env, - self - ) => { - const token = tokens[idx]; - const tokenClasses = tagClassMap[token.tag] ?? []; - for (const clazz of tokenClasses) { - token.attrJoin("class", clazz); - } - - return self.renderToken(tokens, idx, options); - }; - }); - } - - #unapplyTagClassMap() { - for (const [key] of this.#originalClassMap) { - delete this.#markdownIt.renderer.rules[key]; - } - - this.#originalClassMap.clear(); + return this.render(value, markdownRenderer, tagClassMap); } + private defaultMarkdownWarningLogged = false; /** - * Renders the markdown string to HTML using MarkdownIt. - * - * Note: MarkdownIt doesn't enable HTML in its output, so we render the - * value directly without further sanitization. - * @see https://github.com/markdown-it/markdown-it/blob/master/docs/security.md + * Renders the markdown string to HTML using the injected markdown renderer, + * if present. Otherwise, it returns the value wrapped in a span. */ - render(value: string, tagClassMap?: Record) { - if (tagClassMap) { - this.#applyTagClassMap(tagClassMap); + render(value: string, markdownRenderer?: Types.MarkdownRenderer, tagClassMap?: Record) { + console.log('MarkdownDirective', value, tagClassMap); + if (markdownRenderer) { + // The markdown renderer returns a string, which we need to convert to a + // template result using unsafeHTML. + // It is the responsibilty of the markdown renderer to sanitize the HTML. + return unsafeHTML(markdownRenderer(value, { + tagClassMap: tagClassMap, + })); } - const htmlString = this.#markdownIt.render(value); - this.#unapplyTagClassMap(); - - return unsafeHTML(htmlString); + // TODO: Log once that the markdown renderer is not available. + if (!this.defaultMarkdownWarningLogged) { + console.warn("[MarkdownDirective]", + "can't render markdown because no markdown renderer is configured.\n", + "Use `@a2ui/markdown-it-shared`, or your own markdown renderer."); + this.defaultMarkdownWarningLogged = true; + } + return html`${value}`; } } export const markdown = directive(MarkdownDirective); - -const markdownItStandalone = MarkdownIt(); -export function renderMarkdownToHtmlString(value: string): string { - return markdownItStandalone.render(value); -} diff --git a/renderers/lit/src/0.8/ui/text.ts b/renderers/lit/src/0.8/ui/text.ts index 82ef4aea0..3bbee7cff 100644 --- a/renderers/lit/src/0.8/ui/text.ts +++ b/renderers/lit/src/0.8/ui/text.ts @@ -21,6 +21,8 @@ import { Root } from "./root.js"; import { A2uiMessageProcessor } from "@a2ui/web_core/data/model-processor"; import * as Primitives from "@a2ui/web_core/types/primitives"; import * as Types from "@a2ui/web_core/types/types"; +import * as Context from "./context/theme.js"; +import { consume } from "@lit/context"; import { classMap } from "lit/directives/class-map.js"; import { styleMap } from "lit/directives/style-map.js"; import { structuralStyles } from "./styles.js"; @@ -44,6 +46,11 @@ export class Text extends Root { @property({ reflect: true, attribute: "usage-hint" }) accessor usageHint: Types.ResolvedText["usageHint"] | null = null; + // Allow users to specify their own markdown renderer, + // or the one provided by @a2ui/markdown-it-shared. + @consume({context: Context.markdownRenderer}) + accessor markdownRenderer: Types.MarkdownRenderer | undefined = undefined; + static styles = [ structuralStyles, css` @@ -118,6 +125,7 @@ export class Text extends Root { return html`${markdown( markdownText, + this.markdownRenderer, Styles.appendToAll(this.theme.markdown, ["ol", "ul", "li"], {}) )}`; } From 35699e6b0df22de3a164e88271fd283791af8e93 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 19 Feb 2026 19:21:39 -0800 Subject: [PATCH 03/14] Add the @a2ui/markdown-it-shared package. --- renderers/markdown/markdown-it-shared/.npmrc | 2 + .../markdown/markdown-it-shared/README.md | 34 + .../markdown-it-shared/package-lock.json | 876 ++++++++++++++++++ .../markdown/markdown-it-shared/package.json | 77 ++ .../markdown-it-shared/prepare-publish.mjs | 73 ++ .../markdown-it-shared/src/markdown.test.ts | 42 + .../markdown-it-shared/src/markdown.ts | 34 + .../markdown-it-shared/src/raw-markdown.ts | 97 ++ .../markdown-it-shared/src/sanitizer.ts | 24 + .../markdown/markdown-it-shared/tsconfig.json | 38 + 10 files changed, 1297 insertions(+) create mode 100644 renderers/markdown/markdown-it-shared/.npmrc create mode 100644 renderers/markdown/markdown-it-shared/README.md create mode 100644 renderers/markdown/markdown-it-shared/package-lock.json create mode 100644 renderers/markdown/markdown-it-shared/package.json create mode 100644 renderers/markdown/markdown-it-shared/prepare-publish.mjs create mode 100644 renderers/markdown/markdown-it-shared/src/markdown.test.ts create mode 100644 renderers/markdown/markdown-it-shared/src/markdown.ts create mode 100644 renderers/markdown/markdown-it-shared/src/raw-markdown.ts create mode 100644 renderers/markdown/markdown-it-shared/src/sanitizer.ts create mode 100644 renderers/markdown/markdown-it-shared/tsconfig.json diff --git a/renderers/markdown/markdown-it-shared/.npmrc b/renderers/markdown/markdown-it-shared/.npmrc new file mode 100644 index 000000000..06b0eef7e --- /dev/null +++ b/renderers/markdown/markdown-it-shared/.npmrc @@ -0,0 +1,2 @@ +@a2ui:registry=https://us-npm.pkg.dev/oss-exit-gate-prod/a2ui--npm/ +//us-npm.pkg.dev/oss-exit-gate-prod/a2ui--npm/:always-auth=true diff --git a/renderers/markdown/markdown-it-shared/README.md b/renderers/markdown/markdown-it-shared/README.md new file mode 100644 index 000000000..97b9fe2d5 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/README.md @@ -0,0 +1,34 @@ +Markdown renderer for A2UI using markdown-it and dompurify. + +This is used across all JS renderers, so the configuration is consistent. Each +renderer has a specific facade package that uses this renderer as a dependency. + +End users should use the facade package for their renderer of choice. + +## Development + +### Build + +Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. + +### Running unit tests + +Run `npm test` to execute the unit tests. + +### Code Formatting + +This project uses [Prettier](https://prettier.io/) for code formatting. The configuration is defined in `../.prettierrc`. + +To format all files in the project: + +```bash +npm run format +``` + +To check the format, run: + +```bash +npx prettier --check . +``` + +Most IDEs (like VS Code) can be configured to **Format On Save** using the local Prettier version and configuration. This is the recommended workflow. diff --git a/renderers/markdown/markdown-it-shared/package-lock.json b/renderers/markdown/markdown-it-shared/package-lock.json new file mode 100644 index 000000000..c25c72853 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/package-lock.json @@ -0,0 +1,876 @@ +{ + "name": "@a2ui/markdown-it-shared", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@a2ui/markdown-it-shared", + "version": "0.0.1", + "license": "Apache-2.0", + "dependencies": { + "markdown-it": "^14.1.0" + }, + "devDependencies": { + "@a2ui/web_core": "file:../../web_core", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "prettier": "^3.4.2", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + }, + "peerDependencies": { + "@a2ui/web_core": "file:../../web_core" + } + }, + "../../web_core": { + "name": "@a2ui/web_core", + "version": "0.8.2", + "dev": true, + "license": "Apache-2.0", + "devDependencies": { + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "../../web_core/node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "../../web_core/node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "../../web_core/node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "../../web_core/node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "../../web_core/node_modules/balanced-match": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "../../web_core/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "../../web_core/node_modules/brace-expansion": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "../../web_core/node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../web_core/node_modules/chokidar": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "../../web_core/node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "../../web_core/node_modules/fastq": { + "version": "1.20.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "../../web_core/node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "../../web_core/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "../../web_core/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "../../web_core/node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "../../web_core/node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../web_core/node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "../../web_core/node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "../../web_core/node_modules/jsonc-parser": { + "version": "3.3.1", + "dev": true, + "license": "MIT" + }, + "../../web_core/node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "../../web_core/node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "../../web_core/node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "../../web_core/node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "../../web_core/node_modules/proper-lockfile": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "../../web_core/node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "../../web_core/node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "../../web_core/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "../../web_core/node_modules/reusify": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "../../web_core/node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "../../web_core/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "../../web_core/node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "../../web_core/node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "../../web_core/node_modules/wireit": { + "version": "0.15.0-pre.2", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "vscode-extension", + "website" + ], + "dependencies": { + "brace-expansion": "^4.0.0", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "jsonc-parser": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "bin": { + "wireit": "bin/wireit.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@a2ui/web_core": { + "resolved": "../../web_core", + "link": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.9", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wireit": { + "version": "0.15.0-pre.2", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "vscode-extension", + "website" + ], + "dependencies": { + "brace-expansion": "^4.0.0", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "jsonc-parser": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "bin": { + "wireit": "bin/wireit.js" + }, + "engines": { + "node": ">=18.0.0" + } + } + } +} diff --git a/renderers/markdown/markdown-it-shared/package.json b/renderers/markdown/markdown-it-shared/package.json new file mode 100644 index 000000000..b115c9e30 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/package.json @@ -0,0 +1,77 @@ +{ + "name": "@a2ui/markdown-it-shared", + "version": "0.0.1", + "description": "A Markdown renderer using markdown-it and dompurify.", + "main": "./dist/src/markdown.js", + "types": "./dist/src/markdown.d.ts", + "exports": { + ".": { + "types": "./dist/src/markdown.d.ts", + "default": "./dist/src/markdown.js" + } + }, + "type": "module", + "repository": { + "directory": "renderers/markdown/markdown-it-shared", + "type": "git", + "url": "git+https://github.com/google/A2UI.git" + }, + "files": [ + "dist/src" + ], + "scripts": { + "prepack": "npm run build", + "build": "wireit", + "build:tsc": "wireit", + "test": "wireit", + "format": "prettier --ignore-path ../.gitignore --write .", + "format:check": "prettier --ignore-path ../.gitignore --check ." + }, + "wireit": { + "build": { + "dependencies": [ + "build:tsc" + ] + }, + "build:tsc": { + "command": "tsc -b --pretty", + "files": [ + "src/**/*.ts", + "src/**/*.json", + "tsconfig.json" + ], + "output": [ + "dist/", + "!dist/**/*.min.js{,.map}" + ], + "clean": "if-file-deleted" + }, + "test": { + "command": "node --test --enable-source-maps --test-reporter spec dist/src/*.test.js", + "dependencies": [ + "build" + ] + } + }, + "keywords": [], + "author": "Google", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/google/A2UI/issues" + }, + "homepage": "https://github.com/google/A2UI/tree/main/web#readme", + "peerDependencies": { + "@a2ui/web_core": "file:../../web_core" + }, + "devDependencies": { + "@a2ui/web_core": "file:../../web_core", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "prettier": "^3.4.2", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + }, + "dependencies": { + "markdown-it": "^14.1.0" + } +} diff --git a/renderers/markdown/markdown-it-shared/prepare-publish.mjs b/renderers/markdown/markdown-it-shared/prepare-publish.mjs new file mode 100644 index 000000000..4635c3229 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/prepare-publish.mjs @@ -0,0 +1,73 @@ +import { readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync } from 'fs'; +import { join } from 'path'; + +// TODO: Review this script, it's copied wholesale from the lit package. + +// This script prepares the Lit package for publishing by: +// 1. Copying package.json to dist/ +// 2. Updating @a2ui/web_core dependency from 'file:...' to the actual version +// 3. Adjusting paths in package.json (main, types, exports) to be relative to dist/ + +const dirname = import.meta.dirname; +const corePkgPath = join(dirname, '../core/package.json'); +const litPkgPath = join(dirname, './package.json'); +const distDir = join(dirname, './dist'); + +if (!existsSync(distDir)) { + mkdirSync(distDir, { recursive: true }); +} + +// 1. Get Core Version +const corePkg = JSON.parse(readFileSync(corePkgPath, 'utf8')); +const coreVersion = corePkg.version; +if (!coreVersion) throw new Error('Cannot determine @a2ui/web_core version'); + +// 2. Read Lit Package +const litPkg = JSON.parse(readFileSync(litPkgPath, 'utf8')); + +// 3. Update Dependency +if (litPkg.dependencies && litPkg.dependencies['@a2ui/web_core']) { + litPkg.dependencies['@a2ui/web_core'] = '^' + coreVersion; +} else { + console.warn('Warning: @a2ui/web_core not found in dependencies.'); +} + +// 4. Adjust Paths for Dist +litPkg.main = adjustPath(litPkg.main); +litPkg.types = adjustPath(litPkg.types); + +if (litPkg.exports) { + for (const key in litPkg.exports) { + const exp = litPkg.exports[key]; + if (typeof exp === 'string') { + litPkg.exports[key] = adjustPath(exp); + } else { + if (exp.types) exp.types = adjustPath(exp.types); + if (exp.default) exp.default = adjustPath(exp.default); + if (exp.import) exp.import = adjustPath(exp.import); + if (exp.require) exp.require = adjustPath(exp.require); + } + } +} + +// 5. Write to dist/package.json +writeFileSync(join(distDir, 'package.json'), JSON.stringify(litPkg, null, 2)); + +// 6. Copy README and LICENSE +['README.md', 'LICENSE'].forEach((file) => { + const src = join(dirname, file); + if (!existsSync(src)) { + throw new Error(`Missing required file for publishing: ${file}`); + } + copyFileSync(src, join(distDir, file)); +}); + +console.log(`Prepared dist/package.json with @a2ui/web_core@${coreVersion}`); + +// Utility function to adjustthe paths of the built files (dist/src/*) to (src/*) +function adjustPath(p) { + if (p && p.startsWith('./dist/')) { + return './' + p.substring(7); // Remove ./dist/ + } + return p; +} diff --git a/renderers/markdown/markdown-it-shared/src/markdown.test.ts b/renderers/markdown/markdown-it-shared/src/markdown.test.ts new file mode 100644 index 000000000..9a0d70105 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/src/markdown.test.ts @@ -0,0 +1,42 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { MarkdownItRenderer } from './raw-markdown.js'; + +describe('MarkdownItRenderer', () => { + it('renders basic markdown', () => { + const renderer = new MarkdownItRenderer(); + const result = renderer.render('# Hello'); + assert.match(result, /

Hello<\/h1>/); + }); + + it('applies tag classes via tagClassMap', () => { + const renderer = new MarkdownItRenderer(); + const result = renderer.render('# Hello', { h1: ['custom-class'] }); + assert.match(result, /

Hello<\/h1>/); + }); + + it('applies multiple classes', () => { + const renderer = new MarkdownItRenderer(); + const result = renderer.render('para', { p: ['class1', 'class2'] }); + assert.match(result, /

para<\/p>/); + }); + + it('is stateless (tagClassMap does not persist)', () => { + const renderer = new MarkdownItRenderer(); + + // First render with class + const result1 = renderer.render('# Hello', { h1: ['persistent?'] }); + assert.match(result1, /class="persistent\?"/); + + // Second render without class + const result2 = renderer.render('# Hello'); + assert.doesNotMatch(result2, /class="persistent\?"/); + assert.match(result2, /

Hello<\/h1>/); + }); + + it('handles empty tagClassMap', () => { + const renderer = new MarkdownItRenderer(); + const result = renderer.render('# Hello', {}); + assert.match(result, /

Hello<\/h1>/); + }); +}); diff --git a/renderers/markdown/markdown-it-shared/src/markdown.ts b/renderers/markdown/markdown-it-shared/src/markdown.ts new file mode 100644 index 000000000..b5de73dda --- /dev/null +++ b/renderers/markdown/markdown-it-shared/src/markdown.ts @@ -0,0 +1,34 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { rawMarkdownRenderer } from './raw-markdown.js'; +import { sanitizer } from './sanitizer.js'; +import * as Types from '@a2ui/web_core'; + +// Export the class and type for consumers +export { MarkdownItRenderer } from './raw-markdown.js'; + +/** + * A Markdown to HTML renderer using markdown-it and dompurify. + * @param value The markdown code to render. + * @param options Options for the markdown renderer. + * @returns The rendered HTML as a string. + */ +export function renderMarkdown(value: string, options?: Types.MarkdownRendererOptions): string { + console.log('renderMarkdown', options, value); + const htmlString = rawMarkdownRenderer.render(value, options?.tagClassMap); + return sanitizer.sanitize(htmlString); +} diff --git a/renderers/markdown/markdown-it-shared/src/raw-markdown.ts b/renderers/markdown/markdown-it-shared/src/raw-markdown.ts new file mode 100644 index 000000000..9d4fd1959 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/src/raw-markdown.ts @@ -0,0 +1,97 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import markdownit from 'markdown-it'; +import { sanitizer } from './sanitizer.js'; +import * as Types from '@a2ui/web_core'; + +/** + * A pre-configured instance of markdown-it to render markdown in A2UI web. + * + * This renderer does not perform any sanitization of the outgoing HTML. + */ +export class MarkdownItRenderer { + private markdownIt = markdownit({ + highlight: (str, lang) => { + switch (lang) { + case 'html': { + const iframe = document.createElement('iframe'); + iframe.classList.add('html-view'); + iframe.srcdoc = str; + iframe.sandbox.add(''); + return iframe.innerHTML; + } + + default: + return sanitizer.sanitize(str); + } + }, + }); + + constructor() { + this.registerTagClassMapRules(); + } + + /** + * Registers rules to apply tag class maps from the environment. + */ + private registerTagClassMapRules() { + const rulesToProxy = [ + 'paragraph_open', + 'heading_open', + 'bullet_list_open', + 'ordered_list_open', + 'list_item_open', + 'link_open', + 'strong_open', + 'em_open', + ]; + + for (const ruleName of rulesToProxy) { + this.markdownIt.renderer.rules[ruleName] = (tokens, idx, options, env, self) => { + const token = tokens[idx]; + const tagClassMap = env?.tagClassMap as Types.MarkdownRendererTagClassMap | undefined; + + if (tagClassMap) { + const tokenClasses = tagClassMap[token.tag] ?? []; + for (const clazz of tokenClasses) { + token.attrJoin('class', clazz); + } + } + + return self.renderToken(tokens, idx, options); + }; + } + } + + /** + * Renders the markdown string to HTML using the internal MarkdownIt instance. + * + * @param tagClassMap A map of tag names to classes to apply when rendering a tag. + * + * This method does not perform any sanitization of the outgoing HTML. + */ + render(value: string, tagClassMap?: Types.MarkdownRendererTagClassMap) { + return this.markdownIt.render(value, { tagClassMap }); + } +} + +/** + * A pre-configured instance of markdown-it to render markdown in A2UI web. + * + * This renderer does not perform any sanitization of the outgoing HTML. + */ +export const rawMarkdownRenderer = new MarkdownItRenderer(); diff --git a/renderers/markdown/markdown-it-shared/src/sanitizer.ts b/renderers/markdown/markdown-it-shared/src/sanitizer.ts new file mode 100644 index 000000000..2573d0e6a --- /dev/null +++ b/renderers/markdown/markdown-it-shared/src/sanitizer.ts @@ -0,0 +1,24 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +// TODO: Implement the DOMPurify sanitizer. + +/** + * A sanitizer that sanitizes HTML. + */ +export const sanitizer = { + sanitize: (html: string) => html, +}; diff --git a/renderers/markdown/markdown-it-shared/tsconfig.json b/renderers/markdown/markdown-it-shared/tsconfig.json new file mode 100644 index 000000000..5348e6967 --- /dev/null +++ b/renderers/markdown/markdown-it-shared/tsconfig.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + // "allowJs": true, + "preserveWatchOutput": true, + "sourceMap": true, + "target": "es2022", + "module": "esnext", + "lib": ["es2023", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "useDefineForClassFields": false, + "rootDir": ".", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", + + /* Bundler mode */ + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "paths": { + "@a2ui/web_core": ["../../web_core"] + } + }, + "include": ["src/**/*.ts", "src/**/*.json"] +} From 80c71962e85491c22933eadad78d0b01832e1b90 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 19 Feb 2026 19:23:03 -0800 Subject: [PATCH 04/14] Inject the markdown-it-shared renderer in the lit shell sample. --- samples/client/lit/package.json | 2 +- samples/client/lit/shell/app.ts | 4 ++++ samples/client/lit/shell/package.json | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/client/lit/package.json b/samples/client/lit/package.json index fc5534f1b..ec076f485 100644 --- a/samples/client/lit/package.json +++ b/samples/client/lit/package.json @@ -14,7 +14,7 @@ "serve:agent:rizzcharts": "cd ../../agent/adk/rizzcharts && uv run .", "serve:agent:orchestrator": "cd ../../agent/adk/orchestrator && uv run .", "serve:shell": "cd shell && npm run dev", - "build:renderer": "cd ../../../renderers/web_core && npm install && npm run build && cd ../lit && npm install && npm run build", + "build:renderer": "cd ../../../renderers && for dir in 'web_core' 'markdown/markdown-it-shared' 'lit'; do (cd \"$dir\" && npm install && npm run build); done", "demo:all": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST,CONT1\" -c \"magenta,blue,green\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\" \"npm run serve:agent:contact_lookup\"", "demo:restaurant": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\"", "demo:contact": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,CONT1\" -c \"magenta,green\" \"npm run serve:shell\" \"npm run serve:agent:contact_lookup\"", diff --git a/samples/client/lit/shell/app.ts b/samples/client/lit/shell/app.ts index 33a16cf57..b983207a1 100644 --- a/samples/client/lit/shell/app.ts +++ b/samples/client/lit/shell/app.ts @@ -46,6 +46,7 @@ import { AppConfig } from "./configs/types.js"; import { config as restaurantConfig } from "./configs/restaurant.js"; import { config as contactsConfig } from "./configs/contacts.js"; import { styleMap } from "lit/directives/style-map.js"; +import { renderMarkdown } from "@a2ui/markdown-it-shared"; const configs: Record = { restaurant: restaurantConfig, @@ -57,6 +58,9 @@ export class A2UILayoutEditor extends SignalWatcher(LitElement) { @provide({ context: UI.Context.themeContext }) accessor theme: v0_8.Types.Theme = uiTheme; + @provide({ context: UI.Context.markdownRenderer }) + accessor markdownRenderer: v0_8.Types.MarkdownRenderer = renderMarkdown; + @state() accessor #requesting = false; diff --git a/samples/client/lit/shell/package.json b/samples/client/lit/shell/package.json index 5a8b4d0da..e40f71748 100644 --- a/samples/client/lit/shell/package.json +++ b/samples/client/lit/shell/package.json @@ -79,6 +79,7 @@ "dependencies": { "@a2a-js/sdk": "^0.3.4", "@a2ui/lit": "file:../../../../renderers/lit", + "@a2ui/markdown-it-shared": "file:../../../../renderers/markdown/markdown-it-shared", "@google/genai": "^1.22.0", "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", From f241d39d836b42fb1ff8c037ee2ec1da1a3cc2b7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 14:25:02 -0800 Subject: [PATCH 05/14] Export the markdown context from its own file --- renderers/lit/src/0.8/ui/context/context.ts | 2 ++ renderers/lit/src/0.8/ui/context/markdown.ts | 23 ++++++++++++++++++++ renderers/lit/src/0.8/ui/context/theme.ts | 12 ++++++++-- renderers/lit/src/0.8/ui/ui.ts | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 renderers/lit/src/0.8/ui/context/context.ts create mode 100644 renderers/lit/src/0.8/ui/context/markdown.ts diff --git a/renderers/lit/src/0.8/ui/context/context.ts b/renderers/lit/src/0.8/ui/context/context.ts new file mode 100644 index 000000000..12efa58ba --- /dev/null +++ b/renderers/lit/src/0.8/ui/context/context.ts @@ -0,0 +1,2 @@ +export * from "./theme.js"; +export * from "./markdown.js"; diff --git a/renderers/lit/src/0.8/ui/context/markdown.ts b/renderers/lit/src/0.8/ui/context/markdown.ts new file mode 100644 index 000000000..ff5992a93 --- /dev/null +++ b/renderers/lit/src/0.8/ui/context/markdown.ts @@ -0,0 +1,23 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { createContext } from "@lit/context"; +import * as Types from "@a2ui/web_core/types/types"; + +/** + * The markdown renderer context. + */ +export const markdown = createContext("A2UIMarkdown"); diff --git a/renderers/lit/src/0.8/ui/context/theme.ts b/renderers/lit/src/0.8/ui/context/theme.ts index e3b4ba4c9..c5bd05fc0 100644 --- a/renderers/lit/src/0.8/ui/context/theme.ts +++ b/renderers/lit/src/0.8/ui/context/theme.ts @@ -17,6 +17,14 @@ import { createContext } from "@lit/context"; import * as Types from "@a2ui/web_core/types/types"; -export const themeContext = createContext("A2UITheme"); +/** + * The theme context. + */ +export const theme = createContext("A2UITheme"); -export const markdownRenderer = createContext("A2UIMarkdown"); +/** + * An alias for the theme context, for backwards-compatibility. + * + * @deprecated Use `theme` instead. + */ +export const themeContext = theme; diff --git a/renderers/lit/src/0.8/ui/ui.ts b/renderers/lit/src/0.8/ui/ui.ts index 476d4d567..7038d90a5 100644 --- a/renderers/lit/src/0.8/ui/ui.ts +++ b/renderers/lit/src/0.8/ui/ui.ts @@ -43,7 +43,7 @@ import { TextField } from "./text-field.js"; import { Text } from "./text.js"; import { Video } from "./video.js"; -export * as Context from "./context/theme.js"; +export * as Context from "./context/context.js"; export * as Utils from "./utils/utils.js"; export { ComponentRegistry, componentRegistry } from "./component-registry.js"; export { registerCustomComponents } from "./custom-components/index.js"; From 3a21fc54a643cfbfb8f7d88e33c25cbf4cbd456c Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 14:26:26 -0800 Subject: [PATCH 06/14] Use the new context name --- renderers/lit/src/0.8/ui/text.ts | 4 ++-- samples/client/lit/shell/app.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/renderers/lit/src/0.8/ui/text.ts b/renderers/lit/src/0.8/ui/text.ts index 3bbee7cff..0feff11d2 100644 --- a/renderers/lit/src/0.8/ui/text.ts +++ b/renderers/lit/src/0.8/ui/text.ts @@ -21,7 +21,7 @@ import { Root } from "./root.js"; import { A2uiMessageProcessor } from "@a2ui/web_core/data/model-processor"; import * as Primitives from "@a2ui/web_core/types/primitives"; import * as Types from "@a2ui/web_core/types/types"; -import * as Context from "./context/theme.js"; +import * as Context from "./context/context.js"; import { consume } from "@lit/context"; import { classMap } from "lit/directives/class-map.js"; import { styleMap } from "lit/directives/style-map.js"; @@ -48,7 +48,7 @@ export class Text extends Root { // Allow users to specify their own markdown renderer, // or the one provided by @a2ui/markdown-it-shared. - @consume({context: Context.markdownRenderer}) + @consume({context: Context.markdown}) accessor markdownRenderer: Types.MarkdownRenderer | undefined = undefined; static styles = [ diff --git a/samples/client/lit/shell/app.ts b/samples/client/lit/shell/app.ts index b983207a1..29830433d 100644 --- a/samples/client/lit/shell/app.ts +++ b/samples/client/lit/shell/app.ts @@ -55,10 +55,10 @@ const configs: Record = { @customElement("a2ui-shell") export class A2UILayoutEditor extends SignalWatcher(LitElement) { - @provide({ context: UI.Context.themeContext }) + @provide({ context: UI.Context.theme }) accessor theme: v0_8.Types.Theme = uiTheme; - @provide({ context: UI.Context.markdownRenderer }) + @provide({ context: UI.Context.markdown }) accessor markdownRenderer: v0_8.Types.MarkdownRenderer = renderMarkdown; @state() From 45b22f07179a259922a7181aa2031377b2c1caec Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 14:40:06 -0800 Subject: [PATCH 07/14] Pass the complete MarkdownRendererOptions to the markdown directive from the Text widget. --- renderers/lit/src/0.8/ui/directives/markdown.ts | 9 +++------ renderers/lit/src/0.8/ui/text.ts | 5 +++-- renderers/markdown/markdown-it-shared/src/markdown.ts | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/renderers/lit/src/0.8/ui/directives/markdown.ts b/renderers/lit/src/0.8/ui/directives/markdown.ts index 0e2df787b..02c3aff98 100644 --- a/renderers/lit/src/0.8/ui/directives/markdown.ts +++ b/renderers/lit/src/0.8/ui/directives/markdown.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { html, noChange, TemplateResult } from "lit"; +import { html, noChange } from "lit"; import { Directive, DirectiveParameters, @@ -46,15 +46,12 @@ class MarkdownDirective extends Directive { * Renders the markdown string to HTML using the injected markdown renderer, * if present. Otherwise, it returns the value wrapped in a span. */ - render(value: string, markdownRenderer?: Types.MarkdownRenderer, tagClassMap?: Record) { - console.log('MarkdownDirective', value, tagClassMap); + render(value: string, markdownRenderer?: Types.MarkdownRenderer, markdownOptions?: Types.MarkdownRendererOptions) { if (markdownRenderer) { // The markdown renderer returns a string, which we need to convert to a // template result using unsafeHTML. // It is the responsibilty of the markdown renderer to sanitize the HTML. - return unsafeHTML(markdownRenderer(value, { - tagClassMap: tagClassMap, - })); + return unsafeHTML(markdownRenderer(value, markdownOptions)); } // TODO: Log once that the markdown renderer is not available. if (!this.defaultMarkdownWarningLogged) { diff --git a/renderers/lit/src/0.8/ui/text.ts b/renderers/lit/src/0.8/ui/text.ts index 0feff11d2..45201e27d 100644 --- a/renderers/lit/src/0.8/ui/text.ts +++ b/renderers/lit/src/0.8/ui/text.ts @@ -125,8 +125,9 @@ export class Text extends Root { return html`${markdown( markdownText, - this.markdownRenderer, - Styles.appendToAll(this.theme.markdown, ["ol", "ul", "li"], {}) + this.markdownRenderer, { + tagClassMap: Styles.appendToAll(this.theme.markdown, ["ol", "ul", "li"], {}) + }, )}`; } diff --git a/renderers/markdown/markdown-it-shared/src/markdown.ts b/renderers/markdown/markdown-it-shared/src/markdown.ts index b5de73dda..ad25ca650 100644 --- a/renderers/markdown/markdown-it-shared/src/markdown.ts +++ b/renderers/markdown/markdown-it-shared/src/markdown.ts @@ -28,7 +28,6 @@ export { MarkdownItRenderer } from './raw-markdown.js'; * @returns The rendered HTML as a string. */ export function renderMarkdown(value: string, options?: Types.MarkdownRendererOptions): string { - console.log('renderMarkdown', options, value); const htmlString = rawMarkdownRenderer.render(value, options?.tagClassMap); return sanitizer.sanitize(htmlString); } From 08bce68de8332981ffa53a52494b7fadc1347102 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 15:41:16 -0800 Subject: [PATCH 08/14] Allow injecting a markdownRenderer in the angular renderer. --- renderers/angular/ng-package.json | 2 +- renderers/angular/package.json | 2 - renderers/angular/src/lib/catalog/text.ts | 5 +- renderers/angular/src/lib/data/index.ts | 1 + renderers/angular/src/lib/data/markdown.ts | 118 ++++++--------------- 5 files changed, 37 insertions(+), 91 deletions(-) diff --git a/renderers/angular/ng-package.json b/renderers/angular/ng-package.json index 58252aaf2..d08974fc0 100644 --- a/renderers/angular/ng-package.json +++ b/renderers/angular/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "allowedNonPeerDependencies": ["markdown-it", "@a2ui/web_core"] + "allowedNonPeerDependencies": ["@a2ui/web_core"] } diff --git a/renderers/angular/package.json b/renderers/angular/package.json index 2be6aef48..da572bdfb 100644 --- a/renderers/angular/package.json +++ b/renderers/angular/package.json @@ -6,7 +6,6 @@ }, "dependencies": { "@a2ui/web_core": "file:../web_core", - "markdown-it": "^14.1.0", "tslib": "^2.3.0" }, "peerDependencies": { @@ -22,7 +21,6 @@ "@angular/core": "^21.0.0", "@types/express": "^5.0.1", "@types/jasmine": "~5.1.0", - "@types/markdown-it": "^14.1.2", "@types/node": "^20.17.19", "@types/uuid": "^10.0.0", "@vitest/browser": "^4.0.15", diff --git a/renderers/angular/src/lib/catalog/text.ts b/renderers/angular/src/lib/catalog/text.ts index 7cef363bb..3139732b9 100644 --- a/renderers/angular/src/lib/catalog/text.ts +++ b/renderers/angular/src/lib/catalog/text.ts @@ -95,8 +95,9 @@ export class Text extends DynamicComponent { } return this.markdownRenderer.render( - value, - Styles.appendToAll(this.theme.markdown, ['ol', 'ul', 'li'], {}), + value, { + tagClassMap: Styles.appendToAll(this.theme.markdown, ['ol', 'ul', 'li'], {}), + }, ); }); diff --git a/renderers/angular/src/lib/data/index.ts b/renderers/angular/src/lib/data/index.ts index 59c24ba49..4e0098b37 100644 --- a/renderers/angular/src/lib/data/index.ts +++ b/renderers/angular/src/lib/data/index.ts @@ -16,3 +16,4 @@ export * from './processor'; export * from './types'; +export { provideMarkdownRenderer } from './markdown'; diff --git a/renderers/angular/src/lib/data/markdown.ts b/renderers/angular/src/lib/data/markdown.ts index dac3a77d2..a9b66e25b 100644 --- a/renderers/angular/src/lib/data/markdown.ts +++ b/renderers/angular/src/lib/data/markdown.ts @@ -14,101 +14,47 @@ limitations under the License. */ -import { inject, Injectable, SecurityContext } from '@angular/core'; +import { inject, Injectable, InjectionToken, SecurityContext } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; -import MarkdownIt from 'markdown-it'; +import * as Types from '@a2ui/web_core/types/types'; + +// We need this because Types.MarkdownRenderer is a raw TS type, and can't be used as a token directly. +const MARKDOWN_RENDERER_TOKEN = new InjectionToken('MARKDOWN_RENDERER'); @Injectable({ providedIn: 'root' }) export class MarkdownRenderer { - private originalClassMap = new Map(); - private sanitizer = inject(DomSanitizer); - private markdownIt = MarkdownIt({ - highlight: (str, lang) => { - if (lang === 'html') { - const iframe = document.createElement('iframe'); - iframe.classList.add('html-view'); - iframe.srcdoc = str; - iframe.sandbox = ''; - return iframe.innerHTML; - } - - return str; - }, - }); + private markdownRenderer = inject(MARKDOWN_RENDERER_TOKEN, { optional: true }); + private sanitizer = inject(DomSanitizer); + private static defaultMarkdownWarningLogged = false; - render(value: string, tagClassMap?: Record) { - if (tagClassMap) { - this.applyTagClassMap(tagClassMap); + render(value: string, markdownOptions?: Types.MarkdownRendererOptions) { + if (this.markdownRenderer) { + // The markdownRenderer should return a sanitized string. + return this.markdownRenderer(value, markdownOptions); } - const htmlString = this.markdownIt.render(value); - this.unapplyTagClassMap(); - return this.sanitizer.sanitize(SecurityContext.HTML, htmlString); - } - - private applyTagClassMap(tagClassMap: Record) { - Object.entries(tagClassMap).forEach(([tag, classes]) => { - let tokenName; - switch (tag) { - case 'p': - tokenName = 'paragraph'; - break; - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - tokenName = 'heading'; - break; - case 'ul': - tokenName = 'bullet_list'; - break; - case 'ol': - tokenName = 'ordered_list'; - break; - case 'li': - tokenName = 'list_item'; - break; - case 'a': - tokenName = 'link'; - break; - case 'strong': - tokenName = 'strong'; - break; - case 'em': - tokenName = 'em'; - break; - } - - if (!tokenName) { - return; - } - const key = `${tokenName}_open`; - const original = this.markdownIt.renderer.rules[key]; - this.originalClassMap.set(key, original); - - this.markdownIt.renderer.rules[key] = (tokens, idx, options, env, self) => { - const token = tokens[idx]; - for (const clazz of classes) { - token.attrJoin('class', clazz); - } - - if (original) { - return original.call(this, tokens, idx, options, env, self); - } else { - return self.renderToken(tokens, idx, options); - } - }; - }); - } - - private unapplyTagClassMap() { - for (const [key, original] of this.originalClassMap) { - this.markdownIt.renderer.rules[key] = original; + if (!MarkdownRenderer.defaultMarkdownWarningLogged) { + console.warn("[MarkdownRenderer]", + "can't render markdown because no markdown renderer is configured.\n", + "Use `@a2ui/markdown-it-shared`, or your own markdown renderer."); + MarkdownRenderer.defaultMarkdownWarningLogged = true; } - this.originalClassMap.clear(); + // Return a span with a sanitized version of the input `value`. + const sanitizedValue = this.sanitizer.sanitize(SecurityContext.HTML, value); + return `${sanitizedValue}`; } } + +/** + * Allows the user to provide a markdown renderer function. + * @param {Types.MarkdownRenderer} markdownRenderer a markdown renderer function. + * @returns an Angular provider for the markdown renderer. + */ +export function provideMarkdownRenderer(markdownRenderer: Types.MarkdownRenderer) { + return { + provide: MARKDOWN_RENDERER_TOKEN, + useValue: markdownRenderer, + }; +} From db3d4bcff496e44240c8ad222ca14084ad99dc10 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 15:42:28 -0800 Subject: [PATCH 09/14] Inject the preconfigured markdown-it-shared in restaurant demo app. --- samples/client/angular/package.json | 5 +++-- .../client/angular/projects/restaurant/src/app/app.config.ts | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/samples/client/angular/package.json b/samples/client/angular/package.json index af5c513be..a061f94b2 100644 --- a/samples/client/angular/package.json +++ b/samples/client/angular/package.json @@ -11,9 +11,9 @@ "serve:ssr:restaurant": "node dist/restaurant/server/server.mjs", "serve:ssr:rizzcharts": "node dist/rizzcharts/server/server.mjs", "serve:ssr:contact": "node dist/contact/server/server.mjs", - "build:web_core": "cd ../../../renderers/web_core && npm install && npm run build", + "build:renderer": "cd ../../../renderers && for dir in 'web_core' 'markdown/markdown-it-shared'; do (cd \"$dir\" && npm install && npm run build); done", "serve:agent:restaurant": "cd ../../agent/adk/restaurant_finder && uv run .", - "demo:restaurant": "npm run build:web_core && concurrently -k -n \"AGENT,WEB\" -c \"magenta,blue\" \"npm run serve:agent:restaurant\" \"npm start -- restaurant\"" + "demo:restaurant": "npm run build:renderer && concurrently -k -n \"AGENT,WEB\" -c \"magenta,blue\" \"npm run serve:agent:restaurant\" \"npm start -- restaurant\"" }, "prettier": { "printWidth": 100, @@ -31,6 +31,7 @@ "dependencies": { "@a2a-js/sdk": "^0.3.4", "@a2ui/web_core": "file:../../../renderers/web_core", + "@a2ui/markdown-it-shared": "file:../../../renderers/markdown/markdown-it-shared", "@angular/cdk": "^20.2.10", "@angular/common": "^21.0.3", "@angular/compiler": "^21.0.3", diff --git a/samples/client/angular/projects/restaurant/src/app/app.config.ts b/samples/client/angular/projects/restaurant/src/app/app.config.ts index 773802db1..492a97b21 100644 --- a/samples/client/angular/projects/restaurant/src/app/app.config.ts +++ b/samples/client/angular/projects/restaurant/src/app/app.config.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { DEFAULT_CATALOG, provideA2UI } from '@a2ui/angular'; +import { DEFAULT_CATALOG, provideA2UI, provideMarkdownRenderer } from '@a2ui/angular'; import { IMAGE_CONFIG } from '@angular/common'; import { ApplicationConfig, @@ -23,6 +23,7 @@ import { } from '@angular/core'; import { provideClientHydration, withEventReplay } from '@angular/platform-browser'; import { theme } from './theme'; +import { renderMarkdown } from '@a2ui/markdown-it-shared'; export const appConfig: ApplicationConfig = { providers: [ @@ -33,6 +34,7 @@ export const appConfig: ApplicationConfig = { catalog: DEFAULT_CATALOG, theme: theme, }), + provideMarkdownRenderer(renderMarkdown), { provide: IMAGE_CONFIG, useValue: { From 16f89a439d2d36f6e69a3374afd0d0159f883148 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 15:47:21 -0800 Subject: [PATCH 10/14] Make the lit warning flag a static property --- renderers/lit/src/0.8/ui/directives/markdown.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/renderers/lit/src/0.8/ui/directives/markdown.ts b/renderers/lit/src/0.8/ui/directives/markdown.ts index 02c3aff98..57fdce250 100644 --- a/renderers/lit/src/0.8/ui/directives/markdown.ts +++ b/renderers/lit/src/0.8/ui/directives/markdown.ts @@ -41,7 +41,7 @@ class MarkdownDirective extends Directive { return this.render(value, markdownRenderer, tagClassMap); } - private defaultMarkdownWarningLogged = false; + private static defaultMarkdownWarningLogged = false; /** * Renders the markdown string to HTML using the injected markdown renderer, * if present. Otherwise, it returns the value wrapped in a span. @@ -54,11 +54,11 @@ class MarkdownDirective extends Directive { return unsafeHTML(markdownRenderer(value, markdownOptions)); } // TODO: Log once that the markdown renderer is not available. - if (!this.defaultMarkdownWarningLogged) { + if (!MarkdownDirective.defaultMarkdownWarningLogged) { console.warn("[MarkdownDirective]", "can't render markdown because no markdown renderer is configured.\n", "Use `@a2ui/markdown-it-shared`, or your own markdown renderer."); - this.defaultMarkdownWarningLogged = true; + MarkdownDirective.defaultMarkdownWarningLogged = true; } return html`${value}`; } From aa1249dfb5c9ccaf0e5ea2183231f03eef8fbfeb Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 16:14:14 -0800 Subject: [PATCH 11/14] Update markdown-it-shared README --- renderers/markdown/markdown-it-shared/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/renderers/markdown/markdown-it-shared/README.md b/renderers/markdown/markdown-it-shared/README.md index 97b9fe2d5..3e2da869f 100644 --- a/renderers/markdown/markdown-it-shared/README.md +++ b/renderers/markdown/markdown-it-shared/README.md @@ -1,9 +1,11 @@ Markdown renderer for A2UI using markdown-it and dompurify. -This is used across all JS renderers, so the configuration is consistent. Each -renderer has a specific facade package that uses this renderer as a dependency. +This is used across all JS renderers, so the configuration is consistent. This +package provides a pre-configured `renderMarkdown` function that is injected +into the respective Markdown Renderer service of each renderer. -End users should use the facade package for their renderer of choice. +(See the `samples` directory for examples and the README of each renderer on +how to use this package.) ## Development From e9094e5b2678a1afbbc236a3545614e09711b281 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 16:44:28 -0800 Subject: [PATCH 12/14] Push some package-lock.json to make CI happy --- samples/client/angular/package-lock.json | 68 +++++++++++------------- samples/client/lit/package-lock.json | 27 +++++++++- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/samples/client/angular/package-lock.json b/samples/client/angular/package-lock.json index b4fdab692..18b8efd83 100644 --- a/samples/client/angular/package-lock.json +++ b/samples/client/angular/package-lock.json @@ -13,6 +13,7 @@ ], "dependencies": { "@a2a-js/sdk": "^0.3.4", + "@a2ui/markdown-it-shared": "file:../../../renderers/markdown/markdown-it-shared", "@a2ui/web_core": "file:../../../renderers/web_core", "@angular/cdk": "^20.2.10", "@angular/common": "^21.0.3", @@ -64,9 +65,31 @@ "vitest": "^4.0.15" } }, + "../../../renderers/markdown-it-shared": { + "extraneous": true + }, + "../../../renderers/markdown/markdown-it-shared": { + "name": "@a2ui/markdown-it-shared", + "version": "0.0.1", + "license": "Apache-2.0", + "dependencies": { + "markdown-it": "^14.1.0" + }, + "devDependencies": { + "@a2ui/web_core": "file:../../web_core", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "prettier": "^3.4.2", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + }, + "peerDependencies": { + "@a2ui/web_core": "file:../../web_core" + } + }, "../../../renderers/web_core": { "name": "@a2ui/web_core", - "version": "0.8.0", + "version": "0.8.2", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.8.3", @@ -132,6 +155,10 @@ "resolved": "projects/lib", "link": true }, + "node_modules/@a2ui/markdown-it-shared": { + "resolved": "../../../renderers/markdown/markdown-it-shared", + "link": true + }, "node_modules/@a2ui/web_core": { "resolved": "../../../renderers/web_core", "link": true @@ -568,7 +595,6 @@ "integrity": "sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -664,7 +690,6 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.14.tgz", "integrity": "sha512-7bZxc01URbiPiIBWThQ69XwOxVduqEKN4PhpbF2AAyfMc/W8Hcr4VoIJOwL0O1Nkq5beS8pCAqoOeIgFyXd/kg==", "license": "MIT", - "peer": true, "dependencies": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -716,7 +741,6 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.1.tgz", "integrity": "sha512-Di2I6TooHdKun3SqRr45o4LbWJq/ZdwUt3fg0X3obPYaP/f6TrFQ4TMjcl03EfPufPtoQx6O+d32rcWVLhDxyw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -733,7 +757,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.1.tgz", "integrity": "sha512-Urd3bh0zv0MQ//S7RRTanIkOMAZH/A7vSMXUDJ3aflplNs7JNbVqBwDNj8NoX1V+os+fd8JRJOReCc1EpH4ZKQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -747,7 +770,6 @@ "integrity": "sha512-CCB8SZS0BzqLOdOaMpPpOW256msuatYCFDRTaT+awYIY1vQp/eLXzkMTD2uqyHraQy8cReeH/P6optRP9A077Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -780,7 +802,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.1.tgz", "integrity": "sha512-KFRCEhsi02pY1EqJ5rnze4mzSaacqh14D8goDhtmARiUH0tefaHR+uKyu4bKSrWga2T/ExG0DJX52LhHRs2qSw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -806,7 +827,6 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.1.tgz", "integrity": "sha512-NBbJOynLOeMsPo03+3dfdxE0P7SB7SXRqoFJ7WP2sOgOIxODna/huo2blmRlnZAVPTn1iQEB9Q+UeyP5c4/1+w==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "tslib": "^2.3.0" @@ -858,7 +878,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.1.tgz", "integrity": "sha512-d6liZjPz29GUZ6dhxytFL/W2nMsYwPpc/E/vZpr5yV+u+gI2VjbnLbl8SG+jjj0/Hyq7s4aGhEKsRrCJJMXgNw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -881,7 +900,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-21.1.1.tgz", "integrity": "sha512-n17fVIQuJzihA0aKDsBJMnH6IFfwpqiQ/QZonrS1743tbSnnBq/9GuDwt5GR1hy4Lnu+vbf0WvpVoOBZOd3LXw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0", "xhr2": "^0.2.0" @@ -921,7 +939,6 @@ "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-21.1.1.tgz", "integrity": "sha512-eJEwpCa2j6mly1QXFWZ8BlzoA3ANdGpFEebKbiJRDGjeS1VXgTrdRzatuHhadXRw2mUSzYZQpnX8vtSiqVf21w==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1023,7 +1040,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1384,7 +1400,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1428,7 +1443,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2259,7 +2273,6 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -2693,7 +2706,6 @@ "integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", @@ -5514,7 +5526,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5772,7 +5783,6 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", - "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -5795,7 +5805,6 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -7119,7 +7128,6 @@ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -7449,7 +7457,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -8876,8 +8883,7 @@ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz", "integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jose": { "version": "6.1.3", @@ -9122,7 +9128,6 @@ "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -9498,7 +9503,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -9597,7 +9601,6 @@ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -10443,7 +10446,6 @@ "integrity": "sha512-UlQOhH8DRlaYsBGQMjOYvg70J70hD4i/55NV9vAsYvsxEskmp86xjUtZgEeVKeoLq8tYMjMXDgaYjYde153sZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.3.0", "@rollup/plugin-json": "^6.1.0", @@ -11858,7 +11860,6 @@ "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -11942,7 +11943,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -13021,8 +13021,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tuf-js": { "version": "4.1.0", @@ -13114,7 +13113,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13332,7 +13330,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -13892,7 +13889,6 @@ "integrity": "sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.17", "@vitest/mocker": "4.0.17", @@ -14313,7 +14309,6 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "devOptional": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -14332,8 +14327,7 @@ "version": "0.15.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "projects/a2a-chat-canvas": { "version": "0.0.1", diff --git a/samples/client/lit/package-lock.json b/samples/client/lit/package-lock.json index 94aeccddf..d27008dbe 100644 --- a/samples/client/lit/package-lock.json +++ b/samples/client/lit/package-lock.json @@ -21,11 +21,11 @@ "version": "0.8.1", "license": "Apache-2.0", "dependencies": { + "@a2ui/markdown-it-shared": "file:../markdown-it-shared", "@a2ui/web_core": "file:../web_core", "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", "lit": "^3.3.1", - "markdown-it": "^14.1.0", "signal-utils": "^0.21.1" }, "devDependencies": { @@ -36,6 +36,25 @@ "wireit": "^0.15.0-pre.2" } }, + "../../../renderers/markdown/markdown-it-shared": { + "name": "@a2ui/markdown-it-shared", + "version": "0.0.1", + "license": "Apache-2.0", + "dependencies": { + "markdown-it": "^14.1.0" + }, + "devDependencies": { + "@a2ui/web_core": "file:../../web_core", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "prettier": "^3.4.2", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + }, + "peerDependencies": { + "@a2ui/web_core": "file:../../web_core" + } + }, "contact": { "name": "@a2ui/contact", "version": "0.8.1", @@ -96,6 +115,10 @@ "resolved": "../../../renderers/lit", "link": true }, + "node_modules/@a2ui/markdown-it-shared": { + "resolved": "../../../renderers/markdown/markdown-it-shared", + "link": true + }, "node_modules/@a2ui/shell": { "resolved": "shell", "link": true @@ -2159,7 +2182,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2893,6 +2915,7 @@ "dependencies": { "@a2a-js/sdk": "^0.3.4", "@a2ui/lit": "file:../../../../renderers/lit", + "@a2ui/markdown-it-shared": "file:../../../../renderers/markdown/markdown-it-shared", "@google/genai": "^1.22.0", "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", From f1010a0c0b3c9297d1737427cf5261673e5ebb9a Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 16:56:51 -0800 Subject: [PATCH 13/14] Simplify CI scripts using new targets --- .github/workflows/lit_samples_build.yml | 18 +++------------- .github/workflows/ng_build_and_test.yml | 28 ++++--------------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/.github/workflows/lit_samples_build.yml b/.github/workflows/lit_samples_build.yml index e3679e82d..4dc55bd23 100644 --- a/.github/workflows/lit_samples_build.yml +++ b/.github/workflows/lit_samples_build.yml @@ -35,21 +35,9 @@ jobs: with: node-version: '20' - - name: Install web_core deps - working-directory: ./renderers/web_core - run: npm ci - - - name: Build web_core - working-directory: ./renderers/web_core - run: npm run build - - - name: Install lib's deps - working-directory: ./renderers/lit - run: npm i - - - name: Build lib - working-directory: ./renderers/lit - run: npm run build + - name: Build lit renderer and its dependencies + working-directory: ./samples/client/lit + run: npm run build:renderer - name: Install all lit samples workspaces' dependencies working-directory: ./samples/client/lit diff --git a/.github/workflows/ng_build_and_test.yml b/.github/workflows/ng_build_and_test.yml index 1658b79c4..e0fb336e0 100644 --- a/.github/workflows/ng_build_and_test.yml +++ b/.github/workflows/ng_build_and_test.yml @@ -35,34 +35,14 @@ jobs: with: node-version: '20' - - name: Install web_core dependencies - working-directory: ./renderers/web_core - run: npm i - - - name: Build web_core - working-directory: ./renderers/web_core - run: npm run build - - - name: Install web lib deps - working-directory: ./renderers/lit - run: npm i - - - name: Build web lib - working-directory: ./renderers/lit - run: npm run build - - - name: Install renderer deps - working-directory: ./renderers/angular - run: npm i - - - name: Build Angular renderer - working-directory: ./renderers/angular - run: npm run build - - name: Install top-level deps working-directory: ./samples/client/angular run: npm i + - name: Build Angular renderer and its dependencies + working-directory: ./samples/client/angular + run: npm run build:renderer + - name: Build contact sample working-directory: ./samples/client/angular run: npm run build contact From 38e081212fd6e257a6be14c702c1c079238ff6a6 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 20 Feb 2026 17:05:16 -0800 Subject: [PATCH 14/14] Some more package-locks --- renderers/angular/package-lock.json | 104 ++------------------------- renderers/lit/package-lock.json | 72 +++---------------- renderers/web_core/package-lock.json | 4 +- 3 files changed, 16 insertions(+), 164 deletions(-) diff --git a/renderers/angular/package-lock.json b/renderers/angular/package-lock.json index 97ad71119..934b9b856 100644 --- a/renderers/angular/package-lock.json +++ b/renderers/angular/package-lock.json @@ -9,7 +9,6 @@ "version": "0.8.3", "dependencies": { "@a2ui/web_core": "file:../web_core", - "markdown-it": "^14.1.0", "tslib": "^2.3.0" }, "devDependencies": { @@ -20,7 +19,6 @@ "@angular/core": "^21.0.0", "@types/express": "^5.0.1", "@types/jasmine": "~5.1.0", - "@types/markdown-it": "^14.1.2", "@types/node": "^20.17.19", "@types/uuid": "^10.0.0", "@vitest/browser": "^4.0.15", @@ -49,7 +47,7 @@ }, "../web_core": { "name": "@a2ui/web_core", - "version": "0.8.0", + "version": "0.8.2", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.8.3", @@ -566,7 +564,6 @@ "integrity": "sha512-UprCiJwEU1Ilw1dVk+yLgNiHlbH81obAuh92lRKozUQRgtSCpOfCQUM79q5r+VdmCPxDshr79U2Ke0fRaiJfAQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -580,7 +577,6 @@ "integrity": "sha512-//BTtxJovsF0LaOWQkOVxWX4EVyNJaPus+IlwWxzmdRHpojIl1Zdyy9BJMVJOifvj3XQ16sZwR1PDWDI5DIWrg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -613,7 +609,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.0.tgz", "integrity": "sha512-QTl9s8GYNN0pt1k3GE6UVlfe6zWtfdykhfchinKq2YJywQ6LBM4UcZgoc56YkgscmyrRFYrr4JYUJjlzTF57+A==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1103,7 +1098,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1147,7 +1141,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1946,7 +1939,6 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -4246,38 +4238,12 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "20.19.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4750,6 +4716,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/asn1": { @@ -5027,7 +4994,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5290,7 +5256,6 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -6522,7 +6487,6 @@ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -6551,6 +6515,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -8327,8 +8292,7 @@ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz", "integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jose": { "version": "6.1.3", @@ -8589,7 +8553,6 @@ "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -8948,7 +8911,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -9032,22 +8994,12 @@ "node": ">=0.10.0" } }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, "node_modules/listr2": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -9441,23 +9393,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -9475,12 +9410,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "license": "MIT" - }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -9887,7 +9816,6 @@ "integrity": "sha512-UlQOhH8DRlaYsBGQMjOYvg70J70hD4i/55NV9vAsYvsxEskmp86xjUtZgEeVKeoLq8tYMjMXDgaYjYde153sZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.3.0", "@rollup/plugin-json": "^6.1.0", @@ -10977,15 +10905,6 @@ "dev": true, "license": "MIT" }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", @@ -11210,7 +11129,6 @@ "integrity": "sha512-y9yUpfQvetAjiDLtNMf1hL9NXchIJgWt6zIKeoB+tCd3npX08Eqfzg60V9DhIGVMtQ0AlMkFw5xa+AQ37zxnAA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -11295,7 +11213,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -12343,8 +12260,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tuf-js": { "version": "4.1.0", @@ -12439,7 +12355,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12475,12 +12390,6 @@ "node": "*" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "license": "MIT" - }, "node_modules/undici": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.0.tgz", @@ -12657,7 +12566,6 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -12733,7 +12641,6 @@ "integrity": "sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.17", "@vitest/mocker": "4.0.17", @@ -13121,7 +13028,6 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/renderers/lit/package-lock.json b/renderers/lit/package-lock.json index ced356e5f..bece58d11 100644 --- a/renderers/lit/package-lock.json +++ b/renderers/lit/package-lock.json @@ -9,11 +9,11 @@ "version": "0.8.1", "license": "Apache-2.0", "dependencies": { + "@a2ui/markdown-it-shared": "file:../markdown-it-shared", "@a2ui/web_core": "file:../web_core", "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", "lit": "^3.3.1", - "markdown-it": "^14.1.0", "signal-utils": "^0.21.1" }, "devDependencies": { @@ -24,15 +24,20 @@ "wireit": "^0.15.0-pre.2" } }, + "../markdown-it-shared": {}, "../web_core": { "name": "@a2ui/web_core", - "version": "0.8.0", + "version": "0.8.2", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.8.3", "wireit": "^0.15.0-pre.2" } }, + "node_modules/@a2ui/markdown-it-shared": { + "resolved": "../markdown-it-shared", + "link": true + }, "node_modules/@a2ui/web_core": { "resolved": "../web_core", "link": true @@ -204,6 +209,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/balanced-match": { @@ -388,18 +394,6 @@ "dev": true, "license": "MIT" }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -727,15 +721,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, "node_modules/lit": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.2.tgz", @@ -767,29 +752,6 @@ "@types/trusted-types": "^2.0.2" } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "license": "MIT" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -877,15 +839,6 @@ "signal-exit": "^3.0.2" } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1007,8 +960,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/signal-polyfill/-/signal-polyfill-0.2.2.tgz", "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/signal-utils": { "version": "0.21.1", @@ -1081,12 +1033,6 @@ "node": ">=14.17" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "license": "MIT" - }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", diff --git a/renderers/web_core/package-lock.json b/renderers/web_core/package-lock.json index fdcd63be0..365fea171 100644 --- a/renderers/web_core/package-lock.json +++ b/renderers/web_core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@a2ui/web_core", - "version": "0.8.0", + "version": "0.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@a2ui/web_core", - "version": "0.8.0", + "version": "0.8.2", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.8.3",