feat(macos): add Canvas A2UI renderer
This commit is contained in:
18
vendor/a2ui/renderers/angular/src/lib/data/index.ts
vendored
Normal file
18
vendor/a2ui/renderers/angular/src/lib/data/index.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
export * from './processor';
|
||||
export * from './types';
|
||||
114
vendor/a2ui/renderers/angular/src/lib/data/markdown.ts
vendored
Normal file
114
vendor/a2ui/renderers/angular/src/lib/data/markdown.ts
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
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 { inject, Injectable, SecurityContext } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MarkdownRenderer {
|
||||
private originalClassMap = new Map<string, any>();
|
||||
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;
|
||||
},
|
||||
});
|
||||
|
||||
render(value: string, tagClassMap?: Record<string, string[]>) {
|
||||
if (tagClassMap) {
|
||||
this.applyTagClassMap(tagClassMap);
|
||||
}
|
||||
const htmlString = this.markdownIt.render(value);
|
||||
this.unapplyTagClassMap();
|
||||
return this.sanitizer.sanitize(SecurityContext.HTML, htmlString);
|
||||
}
|
||||
|
||||
private applyTagClassMap(tagClassMap: Record<string, string[]>) {
|
||||
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;
|
||||
}
|
||||
|
||||
this.originalClassMap.clear();
|
||||
}
|
||||
}
|
||||
47
vendor/a2ui/renderers/angular/src/lib/data/processor.ts
vendored
Normal file
47
vendor/a2ui/renderers/angular/src/lib/data/processor.ts
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 { Data, Types } from '@a2ui/lit/0.8';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { firstValueFrom, Subject } from 'rxjs';
|
||||
|
||||
export interface DispatchedEvent {
|
||||
message: Types.A2UIClientEventMessage;
|
||||
completion: Subject<Types.ServerToClientMessage[]>;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MessageProcessor extends Data.A2uiMessageProcessor {
|
||||
readonly events = new Subject<DispatchedEvent>();
|
||||
|
||||
override setData(
|
||||
node: Types.AnyComponentNode,
|
||||
relativePath: string,
|
||||
value: Types.DataValue,
|
||||
surfaceId?: Types.SurfaceID | null,
|
||||
) {
|
||||
// Override setData to convert from optional inputs (which can be null)
|
||||
// to undefined so that this correctly falls back to the default value for
|
||||
// surfaceId.
|
||||
return super.setData(node, relativePath, value, surfaceId ?? undefined);
|
||||
}
|
||||
|
||||
dispatch(message: Types.A2UIClientEventMessage): Promise<Types.ServerToClientMessage[]> {
|
||||
const completion = new Subject<Types.ServerToClientMessage[]>();
|
||||
this.events.next({ message, completion });
|
||||
return firstValueFrom(completion);
|
||||
}
|
||||
}
|
||||
29
vendor/a2ui/renderers/angular/src/lib/data/types.ts
vendored
Normal file
29
vendor/a2ui/renderers/angular/src/lib/data/types.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 { Types } from '@a2ui/lit/0.8';
|
||||
|
||||
export interface A2TextPayload {
|
||||
kind: 'text';
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface A2DataPayload {
|
||||
kind: 'data';
|
||||
data: Types.ServerToClientMessage;
|
||||
}
|
||||
|
||||
export type A2AServerPayload = Array<A2DataPayload | A2TextPayload> | { error: string };
|
||||
Reference in New Issue
Block a user