feat(macos): add Canvas A2UI renderer
This commit is contained in:
2
vendor/a2ui/renderers/angular/.npmrc
vendored
Normal file
2
vendor/a2ui/renderers/angular/.npmrc
vendored
Normal file
@@ -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
|
||||
9
vendor/a2ui/renderers/angular/README.md
vendored
Normal file
9
vendor/a2ui/renderers/angular/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Angular implementation of A2UI.
|
||||
|
||||
Important: The sample code provided is for demonstration purposes and illustrates the mechanics of A2UI and the Agent-to-Agent (A2A) protocol. When building production applications, it is critical to treat any agent operating outside of your direct control as a potentially untrusted entity.
|
||||
|
||||
All operational data received from an external agent—including its AgentCard, messages, artifacts, and task statuses—should be handled as untrusted input. For example, a malicious agent could provide crafted data in its fields (e.g., name, skills.description) that, if used without sanitization to construct prompts for a Large Language Model (LLM), could expose your application to prompt injection attacks.
|
||||
|
||||
Similarly, any UI definition or data stream received must be treated as untrusted. Malicious agents could attempt to spoof legitimate interfaces to deceive users (phishing), inject malicious scripts via property values (XSS), or generate excessive layout complexity to degrade client performance (DoS). If your application supports optional embedded content (such as iframes or web views), additional care must be taken to prevent exposure to malicious external sites.
|
||||
|
||||
Developer Responsibility: Failure to properly validate data and strictly sandbox rendered content can introduce severe vulnerabilities. Developers are responsible for implementing appropriate security measures—such as input sanitization, Content Security Policies (CSP), strict isolation for optional embedded content, and secure credential handling—to protect their systems and users.
|
||||
35
vendor/a2ui/renderers/angular/angular.json
vendored
Normal file
35
vendor/a2ui/renderers/angular/angular.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"projects": {
|
||||
"lib": {
|
||||
"projectType": "library",
|
||||
"root": ".",
|
||||
"sourceRoot": "./src",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:ng-packagr",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "./tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "./tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"tsConfig": "./tsconfig.spec.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
8
vendor/a2ui/renderers/angular/ng-package.json
vendored
Normal file
8
vendor/a2ui/renderers/angular/ng-package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "./dist",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
},
|
||||
"allowedNonPeerDependencies": ["markdown-it", "@a2ui/lit"]
|
||||
}
|
||||
14264
vendor/a2ui/renderers/angular/package-lock.json
generated
vendored
Normal file
14264
vendor/a2ui/renderers/angular/package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
59
vendor/a2ui/renderers/angular/package.json
vendored
Normal file
59
vendor/a2ui/renderers/angular/package.json
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@a2ui/angular",
|
||||
"version": "0.8.1",
|
||||
"scripts": {
|
||||
"build": "ng build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@a2ui/lit": "file:../lit",
|
||||
"markdown-it": "^14.1.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^21.0.0",
|
||||
"@angular/core": "^21.0.0",
|
||||
"@angular/platform-browser": "^21.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^21.0.2",
|
||||
"@angular/cli": "^21.0.2",
|
||||
"@angular/compiler": "^21.0.0",
|
||||
"@angular/compiler-cli": "^21.0.3",
|
||||
"@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",
|
||||
"cypress": "^15.6.0",
|
||||
"google-artifactregistry-auth": "^3.5.0",
|
||||
"jasmine-core": "~5.9.0",
|
||||
"jsdom": "^27.2.0",
|
||||
"karma": "^6.4.4",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-coverage": "^2.2.1",
|
||||
"karma-jasmine": "^5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.1.0",
|
||||
"ng-packagr": "^21.0.0",
|
||||
"playwright": "^1.56.1",
|
||||
"prettier": "^3.6.2",
|
||||
"sass": "^1.93.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "~5.9.2",
|
||||
"vitest": "^4.0.15"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"prettier": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
50
vendor/a2ui/renderers/angular/src/lib/catalog/audio.ts
vendored
Normal file
50
vendor/a2ui/renderers/angular/src/lib/catalog/audio.ts
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-audio',
|
||||
template: `
|
||||
@let resolvedUrl = this.resolvedUrl();
|
||||
|
||||
@if (resolvedUrl) {
|
||||
<section [class]="theme.components.AudioPlayer" [style]="theme.additionalStyles?.AudioPlayer">
|
||||
<audio controls [src]="resolvedUrl"></audio>
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
audio {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`
|
||||
})
|
||||
export class Audio extends DynamicComponent {
|
||||
readonly url = input.required<Primitives.StringValue | null>();
|
||||
protected readonly resolvedUrl = computed(() => this.resolvePrimitive(this.url()));
|
||||
}
|
||||
56
vendor/a2ui/renderers/angular/src/lib/catalog/button.ts
vendored
Normal file
56
vendor/a2ui/renderers/angular/src/lib/catalog/button.ts
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 { Component, input } from '@angular/core';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-button',
|
||||
imports: [Renderer],
|
||||
template: `
|
||||
<button
|
||||
[class]="theme.components.Button"
|
||||
[style]="theme.additionalStyles?.Button"
|
||||
(click)="handleClick()"
|
||||
>
|
||||
<ng-container
|
||||
a2ui-renderer
|
||||
[surfaceId]="surfaceId()!"
|
||||
[component]="component().properties.child"
|
||||
/>
|
||||
</button>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Button extends DynamicComponent<Types.ButtonNode> {
|
||||
readonly action = input.required<Types.Action | null>();
|
||||
|
||||
protected handleClick() {
|
||||
const action = this.action();
|
||||
|
||||
if (action) {
|
||||
super.sendAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
vendor/a2ui/renderers/angular/src/lib/catalog/card.ts
vendored
Normal file
57
vendor/a2ui/renderers/angular/src/lib/catalog/card.ts
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-card',
|
||||
imports: [Renderer],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styles: `
|
||||
a2ui-card {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a2ui-card > section {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a2ui-card > section > * {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
@let properties = component().properties;
|
||||
@let children = properties.children || [properties.child];
|
||||
|
||||
<section [class]="theme.components.Card" [style]="theme.additionalStyles?.Card">
|
||||
@for (child of children; track child) {
|
||||
<ng-container a2ui-renderer [surfaceId]="surfaceId()!" [component]="child" />
|
||||
}
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class Card extends DynamicComponent<Types.CardNode> { }
|
||||
73
vendor/a2ui/renderers/angular/src/lib/catalog/checkbox.ts
vendored
Normal file
73
vendor/a2ui/renderers/angular/src/lib/catalog/checkbox.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-checkbox',
|
||||
template: `
|
||||
<section
|
||||
[class]="theme.components.CheckBox.container"
|
||||
[style]="theme.additionalStyles?.CheckBox"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
type="checkbox"
|
||||
[id]="inputId"
|
||||
[checked]="inputChecked()"
|
||||
[class]="theme.components.CheckBox.element"
|
||||
(change)="handleChange($event)"
|
||||
/>
|
||||
|
||||
<label [htmlFor]="inputId" [class]="theme.components.CheckBox.label">{{
|
||||
resolvedLabel()
|
||||
}}</label>
|
||||
</section>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Checkbox extends DynamicComponent {
|
||||
readonly value = input.required<Primitives.BooleanValue | null>();
|
||||
readonly label = input.required<Primitives.StringValue | null>();
|
||||
|
||||
protected inputChecked = computed(() => super.resolvePrimitive(this.value()) ?? false);
|
||||
protected resolvedLabel = computed(() => super.resolvePrimitive(this.label()));
|
||||
protected inputId = super.getUniqueId('a2ui-checkbox');
|
||||
|
||||
protected handleChange(event: Event) {
|
||||
const path = this.value()?.path;
|
||||
|
||||
if (!(event.target instanceof HTMLInputElement) || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processor.setData(this.component(), path, event.target.checked, this.surfaceId());
|
||||
}
|
||||
}
|
||||
96
vendor/a2ui/renderers/angular/src/lib/catalog/column.ts
vendored
Normal file
96
vendor/a2ui/renderers/angular/src/lib/catalog/column.ts
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-column',
|
||||
imports: [Renderer],
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
flex: var(--weight);
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.align-end {
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.align-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.distribute-start {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.distribute-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.distribute-end {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.distribute-spaceBetween {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.distribute-spaceAround {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.distribute-spaceEvenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
<section [class]="classes()" [style]="theme.additionalStyles?.Column">
|
||||
@for (child of component().properties.children; track child) {
|
||||
<ng-container a2ui-renderer [surfaceId]="surfaceId()!" [component]="child" />
|
||||
}
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class Column extends DynamicComponent<Types.ColumnNode> {
|
||||
readonly alignment = input<Types.ResolvedColumn['alignment']>('stretch');
|
||||
readonly distribution = input<Types.ResolvedColumn['distribution']>('start');
|
||||
|
||||
protected readonly classes = computed(() => ({
|
||||
...this.theme.components.Column,
|
||||
[`align-${this.alignment()}`]: true,
|
||||
[`distribute-${this.distribution()}`]: true,
|
||||
}));
|
||||
}
|
||||
127
vendor/a2ui/renderers/angular/src/lib/catalog/datetime-input.ts
vendored
Normal file
127
vendor/a2ui/renderers/angular/src/lib/catalog/datetime-input.ts
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
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 { computed, Component, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-datetime-input',
|
||||
template: `
|
||||
<section [class]="theme.components.DateTimeInput.container">
|
||||
<label [for]="inputId" [class]="theme.components.DateTimeInput.label">{{ label() }}</label>
|
||||
|
||||
<input
|
||||
autocomplete="off"
|
||||
[attr.type]="inputType()"
|
||||
[id]="inputId"
|
||||
[class]="theme.components.DateTimeInput.element"
|
||||
[style]="theme.additionalStyles?.DateTimeInput"
|
||||
[value]="inputValue()"
|
||||
(input)="handleInput($event)"
|
||||
/>
|
||||
</section>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class DatetimeInput extends DynamicComponent {
|
||||
readonly value = input.required<Primitives.StringValue | null>();
|
||||
readonly enableDate = input.required<boolean>();
|
||||
readonly enableTime = input.required<boolean>();
|
||||
protected readonly inputId = super.getUniqueId('a2ui-datetime-input');
|
||||
|
||||
protected inputType = computed(() => {
|
||||
const enableDate = this.enableDate();
|
||||
const enableTime = this.enableTime();
|
||||
|
||||
if (enableDate && enableTime) {
|
||||
return 'datetime-local';
|
||||
} else if (enableDate) {
|
||||
return 'date';
|
||||
} else if (enableTime) {
|
||||
return 'time';
|
||||
}
|
||||
|
||||
return 'datetime-local';
|
||||
});
|
||||
|
||||
protected label = computed(() => {
|
||||
// TODO: this should likely be passed from the model.
|
||||
const inputType = this.inputType();
|
||||
|
||||
if (inputType === 'date') {
|
||||
return 'Date';
|
||||
} else if (inputType === 'time') {
|
||||
return 'Time';
|
||||
}
|
||||
|
||||
return 'Date & Time';
|
||||
});
|
||||
|
||||
protected inputValue = computed(() => {
|
||||
const inputType = this.inputType();
|
||||
const parsed = super.resolvePrimitive(this.value()) || '';
|
||||
const date = parsed ? new Date(parsed) : null;
|
||||
|
||||
if (!date || isNaN(date.getTime())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const year = this.padNumber(date.getFullYear());
|
||||
const month = this.padNumber(date.getMonth());
|
||||
const day = this.padNumber(date.getDate());
|
||||
const hours = this.padNumber(date.getHours());
|
||||
const minutes = this.padNumber(date.getMinutes());
|
||||
|
||||
// Browsers are picky with what format they allow for the `value` attribute of date/time inputs.
|
||||
// We need to parse it out of the provided value. Note that we don't use `toISOString`,
|
||||
// because the resulting value is relative to UTC.
|
||||
if (inputType === 'date') {
|
||||
return `${year}-${month}-${day}`;
|
||||
} else if (inputType === 'time') {
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
});
|
||||
|
||||
protected handleInput(event: Event) {
|
||||
const path = this.value()?.path;
|
||||
|
||||
if (!(event.target instanceof HTMLInputElement) || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processor.setData(this.component(), path, event.target.value, this.surfaceId());
|
||||
}
|
||||
|
||||
private padNumber(value: number) {
|
||||
return value.toString().padStart(2, '0');
|
||||
}
|
||||
}
|
||||
185
vendor/a2ui/renderers/angular/src/lib/catalog/default.ts
vendored
Normal file
185
vendor/a2ui/renderers/angular/src/lib/catalog/default.ts
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
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 { inputBinding } from '@angular/core';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { Catalog } from '../rendering/catalog';
|
||||
import { Row } from './row';
|
||||
import { Column } from './column';
|
||||
import { Text } from './text';
|
||||
|
||||
export const DEFAULT_CATALOG: Catalog = {
|
||||
Row: {
|
||||
type: () => Row,
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.RowNode).properties;
|
||||
return [
|
||||
inputBinding('alignment', () => properties.alignment ?? 'stretch'),
|
||||
inputBinding('distribution', () => properties.distribution ?? 'start'),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
Column: {
|
||||
type: () => Column,
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.ColumnNode).properties;
|
||||
return [
|
||||
inputBinding('alignment', () => properties.alignment ?? 'stretch'),
|
||||
inputBinding('distribution', () => properties.distribution ?? 'start'),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
List: {
|
||||
type: () => import('./list').then((r) => r.List),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.ListNode).properties;
|
||||
return [inputBinding('direction', () => properties.direction ?? 'vertical')];
|
||||
},
|
||||
},
|
||||
|
||||
Card: () => import('./card').then((r) => r.Card),
|
||||
|
||||
Image: {
|
||||
type: () => import('./image').then((r) => r.Image),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.ImageNode).properties;
|
||||
return [
|
||||
inputBinding('url', () => properties.url),
|
||||
inputBinding('usageHint', () => properties.usageHint),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
Icon: {
|
||||
type: () => import('./icon').then((r) => r.Icon),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.IconNode).properties;
|
||||
return [inputBinding('name', () => properties.name)];
|
||||
},
|
||||
},
|
||||
|
||||
Video: {
|
||||
type: () => import('./video').then((r) => r.Video),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.VideoNode).properties;
|
||||
return [inputBinding('url', () => properties.url)];
|
||||
},
|
||||
},
|
||||
|
||||
AudioPlayer: {
|
||||
type: () => import('./audio').then((r) => r.Audio),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.AudioPlayerNode).properties;
|
||||
return [inputBinding('url', () => properties.url)];
|
||||
},
|
||||
},
|
||||
|
||||
Text: {
|
||||
type: () => Text,
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.TextNode).properties;
|
||||
return [
|
||||
inputBinding('text', () => properties.text),
|
||||
inputBinding('usageHint', () => properties.usageHint || null),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
Button: {
|
||||
type: () => import('./button').then((r) => r.Button),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.ButtonNode).properties;
|
||||
return [inputBinding('action', () => properties.action)];
|
||||
},
|
||||
},
|
||||
|
||||
Divider: () => import('./divider').then((r) => r.Divider),
|
||||
|
||||
MultipleChoice: {
|
||||
type: () => import('./multiple-choice').then((r) => r.MultipleChoice),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.MultipleChoiceNode).properties;
|
||||
return [
|
||||
inputBinding('options', () => properties.options || []),
|
||||
inputBinding('value', () => properties.selections),
|
||||
inputBinding('description', () => 'Select an item'), // TODO: this should be defined in the properties
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
TextField: {
|
||||
type: () => import('./text-field').then((r) => r.TextField),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.TextFieldNode).properties;
|
||||
return [
|
||||
inputBinding('text', () => properties.text ?? null),
|
||||
inputBinding('label', () => properties.label),
|
||||
inputBinding('inputType', () => properties.type),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
DateTimeInput: {
|
||||
type: () => import('./datetime-input').then((r) => r.DatetimeInput),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.DateTimeInputNode).properties;
|
||||
return [
|
||||
inputBinding('enableDate', () => properties.enableDate),
|
||||
inputBinding('enableTime', () => properties.enableTime),
|
||||
inputBinding('value', () => properties.value),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
CheckBox: {
|
||||
type: () => import('./checkbox').then((r) => r.Checkbox),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.CheckboxNode).properties;
|
||||
return [
|
||||
inputBinding('label', () => properties.label),
|
||||
inputBinding('value', () => properties.value),
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
Slider: {
|
||||
type: () => import('./slider').then((r) => r.Slider),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.SliderNode).properties;
|
||||
return [
|
||||
inputBinding('value', () => properties.value),
|
||||
inputBinding('minValue', () => properties.minValue),
|
||||
inputBinding('maxValue', () => properties.maxValue),
|
||||
inputBinding('label', () => ''), // TODO: this should be defined in the properties
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
Tabs: {
|
||||
type: () => import('./tabs').then((r) => r.Tabs),
|
||||
bindings: (node) => {
|
||||
const properties = (node as Types.TabsNode).properties;
|
||||
return [inputBinding('tabs', () => properties.tabItems)];
|
||||
},
|
||||
},
|
||||
|
||||
Modal: {
|
||||
type: () => import('./modal').then((r) => r.Modal),
|
||||
bindings: () => [],
|
||||
},
|
||||
};
|
||||
37
vendor/a2ui/renderers/angular/src/lib/catalog/divider.ts
vendored
Normal file
37
vendor/a2ui/renderers/angular/src/lib/catalog/divider.ts
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 { Component } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-divider',
|
||||
template: '<hr [class]="theme.components.Divider" [style]="theme.additionalStyles?.Divider"/>',
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background: #ccc;
|
||||
border: none;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Divider extends DynamicComponent {}
|
||||
44
vendor/a2ui/renderers/angular/src/lib/catalog/icon.ts
vendored
Normal file
44
vendor/a2ui/renderers/angular/src/lib/catalog/icon.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-icon',
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
@let resolvedName = this.resolvedName();
|
||||
|
||||
@if (resolvedName) {
|
||||
<section [class]="theme.components.Icon" [style]="theme.additionalStyles?.Icon">
|
||||
<span class="g-icon">{{ resolvedName }}</span>
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Icon extends DynamicComponent {
|
||||
readonly name = input.required<Primitives.StringValue | null>();
|
||||
protected readonly resolvedName = computed(() => this.resolvePrimitive(this.name()));
|
||||
}
|
||||
62
vendor/a2ui/renderers/angular/src/lib/catalog/image.ts
vendored
Normal file
62
vendor/a2ui/renderers/angular/src/lib/catalog/image.ts
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { Primitives, Styles, Types } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-image',
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
@let resolvedUrl = this.resolvedUrl();
|
||||
|
||||
@if (resolvedUrl) {
|
||||
<section [class]="classes()" [style]="theme.additionalStyles?.Image">
|
||||
<img [src]="resolvedUrl" />
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Image extends DynamicComponent {
|
||||
readonly url = input.required<Primitives.StringValue | null>();
|
||||
readonly usageHint = input.required<Types.ResolvedImage['usageHint'] | null>();
|
||||
|
||||
protected readonly resolvedUrl = computed(() => this.resolvePrimitive(this.url()));
|
||||
|
||||
protected classes = computed(() => {
|
||||
const usageHint = this.usageHint();
|
||||
|
||||
return Styles.merge(
|
||||
this.theme.components.Image.all,
|
||||
usageHint ? this.theme.components.Image[usageHint] : {},
|
||||
);
|
||||
});
|
||||
}
|
||||
63
vendor/a2ui/renderers/angular/src/lib/catalog/list.ts
vendored
Normal file
63
vendor/a2ui/renderers/angular/src/lib/catalog/list.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 { Component, input } from '@angular/core';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-list',
|
||||
imports: [Renderer],
|
||||
host: {
|
||||
'[attr.direction]': 'direction()',
|
||||
},
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:host([direction='vertical']) section {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
:host([direction='horizontal']) section {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
|
||||
> ::slotted(*) {
|
||||
flex: 1 0 fit-content;
|
||||
max-width: min(80%, 400px);
|
||||
}
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
<section [class]="theme.components.List" [style]="theme.additionalStyles?.List">
|
||||
@for (child of component().properties.children; track child) {
|
||||
<ng-container a2ui-renderer [surfaceId]="surfaceId()!" [component]="child" />
|
||||
}
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class List extends DynamicComponent<Types.ListNode> {
|
||||
readonly direction = input<'vertical' | 'horizontal'>('vertical');
|
||||
}
|
||||
113
vendor/a2ui/renderers/angular/src/lib/catalog/modal.ts
vendored
Normal file
113
vendor/a2ui/renderers/angular/src/lib/catalog/modal.ts
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 { Component, signal, viewChild, ElementRef, effect } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { Renderer } from '../rendering';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-modal',
|
||||
imports: [Renderer],
|
||||
template: `
|
||||
@if (showDialog()) {
|
||||
<dialog #dialog [class]="theme.components.Modal.backdrop" (click)="handleDialogClick($event)">
|
||||
<section [class]="theme.components.Modal.element" [style]="theme.additionalStyles?.Modal">
|
||||
<div class="controls">
|
||||
<button (click)="closeDialog()">
|
||||
<span class="g-icon">close</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-container
|
||||
a2ui-renderer
|
||||
[surfaceId]="surfaceId()!"
|
||||
[component]="component().properties.contentChild"
|
||||
/>
|
||||
</section>
|
||||
</dialog>
|
||||
} @else {
|
||||
<section (click)="showDialog.set(true)">
|
||||
<ng-container
|
||||
a2ui-renderer
|
||||
[surfaceId]="surfaceId()!"
|
||||
[component]="component().properties.entryPointChild"
|
||||
/>
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
dialog {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
|
||||
& section {
|
||||
& .controls {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-bottom: 4px;
|
||||
|
||||
& button {
|
||||
padding: 0;
|
||||
background: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
pointer: cursor;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Modal extends DynamicComponent<Types.ModalNode> {
|
||||
protected readonly showDialog = signal(false);
|
||||
protected readonly dialog = viewChild<ElementRef<HTMLDialogElement>>('dialog');
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
effect(() => {
|
||||
const dialog = this.dialog();
|
||||
|
||||
if (dialog && !dialog.nativeElement.open) {
|
||||
dialog.nativeElement.showModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected handleDialogClick(event: MouseEvent) {
|
||||
if (event.target instanceof HTMLDialogElement) {
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
protected closeDialog() {
|
||||
const dialog = this.dialog();
|
||||
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dialog.nativeElement.open) {
|
||||
dialog.nativeElement.close();
|
||||
}
|
||||
|
||||
this.showDialog.set(false);
|
||||
}
|
||||
}
|
||||
77
vendor/a2ui/renderers/angular/src/lib/catalog/multiple-choice.ts
vendored
Normal file
77
vendor/a2ui/renderers/angular/src/lib/catalog/multiple-choice.ts
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-multiple-choice',
|
||||
template: `
|
||||
<section [class]="theme.components.MultipleChoice.container">
|
||||
<label [class]="theme.components.MultipleChoice.label" [for]="selectId">{{
|
||||
description()
|
||||
}}</label>
|
||||
|
||||
<select
|
||||
(change)="handleChange($event)"
|
||||
[id]="selectId"
|
||||
[value]="selectValue()"
|
||||
[class]="theme.components.MultipleChoice.element"
|
||||
[style]="theme.additionalStyles?.MultipleChoice"
|
||||
>
|
||||
@for (option of options(); track option.value) {
|
||||
<option [value]="option.value">{{ resolvePrimitive(option.label) }}</option>
|
||||
}
|
||||
</select>
|
||||
</section>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class MultipleChoice extends DynamicComponent {
|
||||
readonly options = input.required<{ label: Primitives.StringValue; value: string }[]>();
|
||||
readonly value = input.required<Primitives.StringValue | null>();
|
||||
readonly description = input.required<string>();
|
||||
|
||||
protected readonly selectId = super.getUniqueId('a2ui-multiple-choice');
|
||||
protected selectValue = computed(() => super.resolvePrimitive(this.value()));
|
||||
|
||||
protected handleChange(event: Event) {
|
||||
const path = this.value()?.path;
|
||||
|
||||
if (!(event.target instanceof HTMLSelectElement) || !event.target.value || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processor.setData(
|
||||
this.component(),
|
||||
this.processor.resolvePath(path, this.component().dataContextPath),
|
||||
event.target.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
100
vendor/a2ui/renderers/angular/src/lib/catalog/row.ts
vendored
Normal file
100
vendor/a2ui/renderers/angular/src/lib/catalog/row.ts
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-row',
|
||||
imports: [Renderer],
|
||||
host: {
|
||||
'[attr.alignment]': 'alignment()',
|
||||
'[attr.distribution]': 'distribution()',
|
||||
},
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
flex: var(--weight);
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.align-start {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.align-end {
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.align-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.distribute-start {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.distribute-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.distribute-end {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.distribute-spaceBetween {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.distribute-spaceAround {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.distribute-spaceEvenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
<section [class]="classes()" [style]="theme.additionalStyles?.Row">
|
||||
@for (child of component().properties.children; track child) {
|
||||
<ng-container a2ui-renderer [surfaceId]="surfaceId()!" [component]="child" />
|
||||
}
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class Row extends DynamicComponent<Types.RowNode> {
|
||||
readonly alignment = input<Types.ResolvedRow['alignment']>('stretch');
|
||||
readonly distribution = input<Types.ResolvedRow['distribution']>('start');
|
||||
|
||||
protected readonly classes = computed(() => ({
|
||||
...this.theme.components.Row,
|
||||
[`align-${this.alignment()}`]: true,
|
||||
[`distribute-${this.distribution()}`]: true,
|
||||
}));
|
||||
}
|
||||
73
vendor/a2ui/renderers/angular/src/lib/catalog/slider.ts
vendored
Normal file
73
vendor/a2ui/renderers/angular/src/lib/catalog/slider.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
|
||||
@Component({
|
||||
selector: '[a2ui-slider]',
|
||||
template: `
|
||||
<section [class]="theme.components.Slider.container">
|
||||
<label [class]="theme.components.Slider.label" [for]="inputId">
|
||||
{{ label() }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
autocomplete="off"
|
||||
type="range"
|
||||
[value]="resolvedValue()"
|
||||
[min]="minValue()"
|
||||
[max]="maxValue()"
|
||||
[id]="inputId"
|
||||
(input)="handleInput($event)"
|
||||
[class]="theme.components.Slider.element"
|
||||
[style]="theme.additionalStyles?.Slider"
|
||||
/>
|
||||
</section>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Slider extends DynamicComponent {
|
||||
readonly value = input.required<Primitives.NumberValue | null>();
|
||||
readonly label = input('');
|
||||
readonly minValue = input.required<number | undefined>();
|
||||
readonly maxValue = input.required<number | undefined>();
|
||||
|
||||
protected readonly inputId = super.getUniqueId('a2ui-slider');
|
||||
protected resolvedValue = computed(() => super.resolvePrimitive(this.value()) ?? 0);
|
||||
|
||||
protected handleInput(event: Event) {
|
||||
const path = this.value()?.path;
|
||||
|
||||
if (!(event.target instanceof HTMLInputElement) || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processor.setData(this.component(), path, event.target.valueAsNumber, this.surfaceId());
|
||||
}
|
||||
}
|
||||
99
vendor/a2ui/renderers/angular/src/lib/catalog/surface.ts
vendored
Normal file
99
vendor/a2ui/renderers/angular/src/lib/catalog/surface.ts
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-surface',
|
||||
imports: [Renderer],
|
||||
template: `
|
||||
@let surfaceId = this.surfaceId();
|
||||
@let surface = this.surface();
|
||||
|
||||
@if (surfaceId && surface) {
|
||||
<ng-container a2ui-renderer [surfaceId]="surfaceId" [component]="surface.componentTree!" />
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
`,
|
||||
host: {
|
||||
'[style]': 'styles()',
|
||||
},
|
||||
})
|
||||
export class Surface {
|
||||
readonly surfaceId = input.required<Types.SurfaceID | null>();
|
||||
readonly surface = input.required<Types.Surface | null>();
|
||||
|
||||
protected readonly styles = computed(() => {
|
||||
const surface = this.surface();
|
||||
const styles: Record<string, string> = {};
|
||||
|
||||
if (surface?.styles) {
|
||||
for (const [key, value] of Object.entries(surface.styles)) {
|
||||
switch (key) {
|
||||
// Here we generate a palette from the singular primary color received
|
||||
// from the surface data. We will want the values to range from
|
||||
// 0 <= x <= 100, where 0 = back, 100 = white, and 50 = the primary
|
||||
// color itself. As such we use a color-mix to create the intermediate
|
||||
// values.
|
||||
//
|
||||
// Note: since we use half the range for black to the primary color,
|
||||
// and half the range for primary color to white the mixed values have
|
||||
// to go up double the amount, i.e., a range from black to primary
|
||||
// color needs to fit in 0 -> 50 rather than 0 -> 100.
|
||||
case 'primaryColor': {
|
||||
styles['--p-100'] = '#ffffff';
|
||||
styles['--p-99'] = `color-mix(in srgb, ${value} 2%, white 98%)`;
|
||||
styles['--p-98'] = `color-mix(in srgb, ${value} 4%, white 96%)`;
|
||||
styles['--p-95'] = `color-mix(in srgb, ${value} 10%, white 90%)`;
|
||||
styles['--p-90'] = `color-mix(in srgb, ${value} 20%, white 80%)`;
|
||||
styles['--p-80'] = `color-mix(in srgb, ${value} 40%, white 60%)`;
|
||||
styles['--p-70'] = `color-mix(in srgb, ${value} 60%, white 40%)`;
|
||||
styles['--p-60'] = `color-mix(in srgb, ${value} 80%, white 20%)`;
|
||||
styles['--p-50'] = value;
|
||||
styles['--p-40'] = `color-mix(in srgb, ${value} 80%, black 20%)`;
|
||||
styles['--p-35'] = `color-mix(in srgb, ${value} 70%, black 30%)`;
|
||||
styles['--p-30'] = `color-mix(in srgb, ${value} 60%, black 40%)`;
|
||||
styles['--p-25'] = `color-mix(in srgb, ${value} 50%, black 50%)`;
|
||||
styles['--p-20'] = `color-mix(in srgb, ${value} 40%, black 60%)`;
|
||||
styles['--p-15'] = `color-mix(in srgb, ${value} 30%, black 70%)`;
|
||||
styles['--p-10'] = `color-mix(in srgb, ${value} 20%, black 80%)`;
|
||||
styles['--p-5'] = `color-mix(in srgb, ${value} 10%, black 90%)`;
|
||||
styles['--0'] = '#00000';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'font': {
|
||||
styles['--font-family'] = value;
|
||||
styles['--font-family-flex'] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
});
|
||||
}
|
||||
72
vendor/a2ui/renderers/angular/src/lib/catalog/tabs.ts
vendored
Normal file
72
vendor/a2ui/renderers/angular/src/lib/catalog/tabs.ts
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 { Component, computed, input, signal } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Renderer } from '../rendering/renderer';
|
||||
import { Styles, Types } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-tabs',
|
||||
imports: [Renderer],
|
||||
template: `
|
||||
@let tabs = this.tabs();
|
||||
@let selectedIndex = this.selectedIndex();
|
||||
|
||||
<section [class]="theme.components.Tabs.container" [style]="theme.additionalStyles?.Tabs">
|
||||
<div [class]="theme.components.Tabs.element">
|
||||
@for (tab of tabs; track tab) {
|
||||
<button
|
||||
(click)="this.selectedIndex.set($index)"
|
||||
[disabled]="selectedIndex === $index"
|
||||
[class]="buttonClasses()[selectedIndex]"
|
||||
>
|
||||
{{ resolvePrimitive(tab.title) }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<ng-container
|
||||
a2ui-renderer
|
||||
[surfaceId]="surfaceId()!"
|
||||
[component]="tabs[selectedIndex].child"
|
||||
/>
|
||||
</section>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Tabs extends DynamicComponent {
|
||||
protected selectedIndex = signal(0);
|
||||
readonly tabs = input.required<Types.ResolvedTabItem[]>();
|
||||
|
||||
protected readonly buttonClasses = computed(() => {
|
||||
const selectedIndex = this.selectedIndex();
|
||||
|
||||
return this.tabs().map((_, index) => {
|
||||
return index === selectedIndex
|
||||
? Styles.merge(
|
||||
this.theme.components.Tabs.controls.all,
|
||||
this.theme.components.Tabs.controls.selected,
|
||||
)
|
||||
: this.theme.components.Tabs.controls.all;
|
||||
});
|
||||
});
|
||||
}
|
||||
86
vendor/a2ui/renderers/angular/src/lib/catalog/text-field.ts
vendored
Normal file
86
vendor/a2ui/renderers/angular/src/lib/catalog/text-field.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 { computed, Component, input } from '@angular/core';
|
||||
import { Primitives, Types } from '@a2ui/lit/0.8';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-text-field',
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
flex: var(--weight);
|
||||
}
|
||||
|
||||
section,
|
||||
input,
|
||||
label {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
`,
|
||||
template: `
|
||||
@let resolvedLabel = this.resolvedLabel();
|
||||
|
||||
<section [class]="theme.components.TextField.container">
|
||||
@if (resolvedLabel) {
|
||||
<label [for]="inputId" [class]="theme.components.TextField.label">{{
|
||||
resolvedLabel
|
||||
}}</label>
|
||||
}
|
||||
|
||||
<input
|
||||
autocomplete="off"
|
||||
[class]="theme.components.TextField.element"
|
||||
[style]="theme.additionalStyles?.TextField"
|
||||
(input)="handleInput($event)"
|
||||
[id]="inputId"
|
||||
[value]="inputValue()"
|
||||
placeholder="Please enter a value"
|
||||
[type]="inputType() === 'number' ? 'number' : 'text'"
|
||||
/>
|
||||
</section>
|
||||
`,
|
||||
})
|
||||
export class TextField extends DynamicComponent {
|
||||
readonly text = input.required<Primitives.StringValue | null>();
|
||||
readonly label = input.required<Primitives.StringValue | null>();
|
||||
readonly inputType = input.required<Types.ResolvedTextField['type'] | null>();
|
||||
|
||||
protected inputValue = computed(() => super.resolvePrimitive(this.text()) || '');
|
||||
protected resolvedLabel = computed(() => super.resolvePrimitive(this.label()));
|
||||
protected inputId = super.getUniqueId('a2ui-input');
|
||||
|
||||
protected handleInput(event: Event) {
|
||||
const path = this.text()?.path;
|
||||
|
||||
if (!(event.target instanceof HTMLInputElement) || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processor.setData(this.component(), path, event.target.value, this.surfaceId());
|
||||
}
|
||||
}
|
||||
137
vendor/a2ui/renderers/angular/src/lib/catalog/text.ts
vendored
Normal file
137
vendor/a2ui/renderers/angular/src/lib/catalog/text.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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 { Component, computed, inject, input, ViewEncapsulation } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives, Styles, Types } from '@a2ui/lit/0.8';
|
||||
import { MarkdownRenderer } from '../data/markdown';
|
||||
|
||||
interface HintedStyles {
|
||||
h1: Record<string, string>;
|
||||
h2: Record<string, string>;
|
||||
h3: Record<string, string>;
|
||||
h4: Record<string, string>;
|
||||
h5: Record<string, string>;
|
||||
body: Record<string, string>;
|
||||
caption: Record<string, string>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-text',
|
||||
template: `
|
||||
<section
|
||||
[class]="classes()"
|
||||
[style]="additionalStyles()"
|
||||
[innerHTML]="resolvedText()"
|
||||
></section>
|
||||
`,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styles: `
|
||||
a2ui-text {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
}
|
||||
|
||||
a2ui-text h1,
|
||||
a2ui-text h2,
|
||||
a2ui-text h3,
|
||||
a2ui-text h4,
|
||||
a2ui-text h5 {
|
||||
line-height: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Text extends DynamicComponent {
|
||||
private markdownRenderer = inject(MarkdownRenderer);
|
||||
readonly text = input.required<Primitives.StringValue | null>();
|
||||
readonly usageHint = input.required<Types.ResolvedText['usageHint'] | null>();
|
||||
|
||||
protected resolvedText = computed(() => {
|
||||
const usageHint = this.usageHint();
|
||||
let value = super.resolvePrimitive(this.text());
|
||||
|
||||
if (value == null) {
|
||||
return '(empty)';
|
||||
}
|
||||
|
||||
switch (usageHint) {
|
||||
case 'h1':
|
||||
value = `# ${value}`;
|
||||
break;
|
||||
case 'h2':
|
||||
value = `## ${value}`;
|
||||
break;
|
||||
case 'h3':
|
||||
value = `### ${value}`;
|
||||
break;
|
||||
case 'h4':
|
||||
value = `#### ${value}`;
|
||||
break;
|
||||
case 'h5':
|
||||
value = `##### ${value}`;
|
||||
break;
|
||||
case 'caption':
|
||||
value = `*${value}*`;
|
||||
break;
|
||||
default:
|
||||
value = String(value);
|
||||
break;
|
||||
}
|
||||
|
||||
return this.markdownRenderer.render(
|
||||
value,
|
||||
Styles.appendToAll(this.theme.markdown, ['ol', 'ul', 'li'], {}),
|
||||
);
|
||||
});
|
||||
|
||||
protected classes = computed(() => {
|
||||
const usageHint = this.usageHint();
|
||||
|
||||
return Styles.merge(
|
||||
this.theme.components.Text.all,
|
||||
usageHint ? this.theme.components.Text[usageHint] : {},
|
||||
);
|
||||
});
|
||||
|
||||
protected additionalStyles = computed(() => {
|
||||
const usageHint = this.usageHint();
|
||||
const styles = this.theme.additionalStyles?.Text;
|
||||
|
||||
if (!styles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let additionalStyles: Record<string, string> = {};
|
||||
|
||||
if (this.areHintedStyles(styles)) {
|
||||
additionalStyles = styles[usageHint ?? 'body'];
|
||||
} else {
|
||||
additionalStyles = styles;
|
||||
}
|
||||
|
||||
return additionalStyles;
|
||||
});
|
||||
|
||||
private areHintedStyles(styles: unknown): styles is HintedStyles {
|
||||
if (typeof styles !== 'object' || !styles || Array.isArray(styles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expected = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'caption', 'body'];
|
||||
return expected.every((v) => v in styles);
|
||||
}
|
||||
}
|
||||
50
vendor/a2ui/renderers/angular/src/lib/catalog/video.ts
vendored
Normal file
50
vendor/a2ui/renderers/angular/src/lib/catalog/video.ts
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 { Component, computed, input } from '@angular/core';
|
||||
import { DynamicComponent } from '../rendering/dynamic-component';
|
||||
import { Primitives } from '@a2ui/lit/0.8';
|
||||
|
||||
@Component({
|
||||
selector: 'a2ui-video',
|
||||
template: `
|
||||
@let resolvedUrl = this.resolvedUrl();
|
||||
|
||||
@if (resolvedUrl) {
|
||||
<section [class]="theme.components.Video" [style]="theme.additionalStyles?.Video">
|
||||
<video controls [src]="resolvedUrl"></video>
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: block;
|
||||
flex: var(--weight);
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Video extends DynamicComponent {
|
||||
readonly url = input.required<Primitives.StringValue | null>();
|
||||
protected readonly resolvedUrl = computed(() => this.resolvePrimitive(this.url()));
|
||||
}
|
||||
25
vendor/a2ui/renderers/angular/src/lib/config.ts
vendored
Normal file
25
vendor/a2ui/renderers/angular/src/lib/config.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
|
||||
import { Catalog, Theme } from './rendering';
|
||||
|
||||
export function provideA2UI(config: { catalog: Catalog; theme: Theme }): EnvironmentProviders {
|
||||
return makeEnvironmentProviders([
|
||||
{ provide: Catalog, useValue: config.catalog },
|
||||
{ provide: Theme, useValue: config.theme },
|
||||
]);
|
||||
}
|
||||
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 };
|
||||
36
vendor/a2ui/renderers/angular/src/lib/rendering/catalog.ts
vendored
Normal file
36
vendor/a2ui/renderers/angular/src/lib/rendering/catalog.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 { Binding, InjectionToken, Type } from '@angular/core';
|
||||
import { DynamicComponent } from './dynamic-component';
|
||||
import { Types } from '@a2ui/lit/0.8';
|
||||
|
||||
export type CatalogLoader = () =>
|
||||
| Promise<Type<DynamicComponent<any>>>
|
||||
| Type<DynamicComponent<any>>;
|
||||
|
||||
export type CatalogEntry<T extends Types.AnyComponentNode> =
|
||||
| CatalogLoader
|
||||
| {
|
||||
type: CatalogLoader;
|
||||
bindings: (data: T) => Binding[];
|
||||
};
|
||||
|
||||
export interface Catalog {
|
||||
[key: string]: CatalogEntry<Types.AnyComponentNode>;
|
||||
}
|
||||
|
||||
export const Catalog = new InjectionToken<Catalog>('Catalog');
|
||||
100
vendor/a2ui/renderers/angular/src/lib/rendering/dynamic-component.ts
vendored
Normal file
100
vendor/a2ui/renderers/angular/src/lib/rendering/dynamic-component.ts
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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, Primitives } from '@a2ui/lit/0.8';
|
||||
import { Directive, inject, input } from '@angular/core';
|
||||
import { MessageProcessor } from '../data';
|
||||
import { Theme } from './theming';
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
@Directive({
|
||||
host: {
|
||||
'[style.--weight]': 'weight()',
|
||||
},
|
||||
})
|
||||
export abstract class DynamicComponent<T extends Types.AnyComponentNode = Types.AnyComponentNode> {
|
||||
protected readonly processor = inject(MessageProcessor);
|
||||
protected readonly theme = inject(Theme);
|
||||
|
||||
readonly surfaceId = input.required<Types.SurfaceID | null>();
|
||||
readonly component = input.required<T>();
|
||||
readonly weight = input.required<string | number>();
|
||||
|
||||
protected sendAction(action: Types.Action): Promise<Types.ServerToClientMessage[]> {
|
||||
const component = this.component();
|
||||
const surfaceId = this.surfaceId() ?? undefined;
|
||||
const context: Record<string, unknown> = {};
|
||||
|
||||
if (action.context) {
|
||||
for (const item of action.context) {
|
||||
if (item.value.literalBoolean) {
|
||||
context[item.key] = item.value.literalBoolean;
|
||||
} else if (item.value.literalNumber) {
|
||||
context[item.key] = item.value.literalNumber;
|
||||
} else if (item.value.literalString) {
|
||||
context[item.key] = item.value.literalString;
|
||||
} else if (item.value.path) {
|
||||
const path = this.processor.resolvePath(item.value.path, component.dataContextPath);
|
||||
const value = this.processor.getData(component, path, surfaceId);
|
||||
context[item.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const message: Types.A2UIClientEventMessage = {
|
||||
userAction: {
|
||||
name: action.name,
|
||||
sourceComponentId: component.id,
|
||||
surfaceId: surfaceId!,
|
||||
timestamp: new Date().toISOString(),
|
||||
context,
|
||||
},
|
||||
};
|
||||
|
||||
return this.processor.dispatch(message);
|
||||
}
|
||||
|
||||
protected resolvePrimitive(value: Primitives.StringValue | null): string | null;
|
||||
protected resolvePrimitive(value: Primitives.BooleanValue | null): boolean | null;
|
||||
protected resolvePrimitive(value: Primitives.NumberValue | null): number | null;
|
||||
protected resolvePrimitive(
|
||||
value: Primitives.StringValue | Primitives.BooleanValue | Primitives.NumberValue | null,
|
||||
) {
|
||||
const component = this.component();
|
||||
const surfaceId = this.surfaceId();
|
||||
|
||||
if (!value || typeof value !== 'object') {
|
||||
return null;
|
||||
} else if (value.literal != null) {
|
||||
return value.literal;
|
||||
} else if (value.path) {
|
||||
return this.processor.getData(component, value.path, surfaceId ?? undefined);
|
||||
} else if ('literalString' in value) {
|
||||
return value.literalString;
|
||||
} else if ('literalNumber' in value) {
|
||||
return value.literalNumber;
|
||||
} else if ('literalBoolean' in value) {
|
||||
return value.literalBoolean;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected getUniqueId(prefix: string) {
|
||||
return `${prefix}-${idCounter++}`;
|
||||
}
|
||||
}
|
||||
20
vendor/a2ui/renderers/angular/src/lib/rendering/index.ts
vendored
Normal file
20
vendor/a2ui/renderers/angular/src/lib/rendering/index.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 './catalog';
|
||||
export * from './dynamic-component';
|
||||
export * from './renderer';
|
||||
export * from './theming';
|
||||
109
vendor/a2ui/renderers/angular/src/lib/rendering/renderer.ts
vendored
Normal file
109
vendor/a2ui/renderers/angular/src/lib/rendering/renderer.ts
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 {
|
||||
Binding,
|
||||
ComponentRef,
|
||||
Directive,
|
||||
DOCUMENT,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
inputBinding,
|
||||
OnDestroy,
|
||||
PLATFORM_ID,
|
||||
Type,
|
||||
untracked,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Types, Styles } from '@a2ui/lit/0.8';
|
||||
import { Catalog } from './catalog';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
|
||||
@Directive({
|
||||
selector: 'ng-container[a2ui-renderer]',
|
||||
})
|
||||
export class Renderer implements OnDestroy {
|
||||
private viewContainerRef = inject(ViewContainerRef);
|
||||
private catalog = inject(Catalog);
|
||||
private static hasInsertedStyles = false;
|
||||
|
||||
private currentRef: ComponentRef<unknown> | null = null;
|
||||
private isDestroyed = false;
|
||||
|
||||
readonly surfaceId = input.required<Types.SurfaceID>();
|
||||
readonly component = input.required<Types.AnyComponentNode>();
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const surfaceId = this.surfaceId();
|
||||
const component = this.component();
|
||||
untracked(() => this.render(surfaceId, component));
|
||||
});
|
||||
|
||||
const platformId = inject(PLATFORM_ID);
|
||||
const document = inject(DOCUMENT);
|
||||
|
||||
if (!Renderer.hasInsertedStyles && isPlatformBrowser(platformId)) {
|
||||
const styles = document.createElement('style');
|
||||
styles.textContent = Styles.structuralStyles;
|
||||
document.head.appendChild(styles);
|
||||
Renderer.hasInsertedStyles = true;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
private async render(surfaceId: Types.SurfaceID, component: Types.AnyComponentNode) {
|
||||
const config = this.catalog[component.type];
|
||||
let newComponent: Type<unknown> | null = null;
|
||||
let componentBindings: Binding[] | null = null;
|
||||
|
||||
if (typeof config === 'function') {
|
||||
newComponent = await config();
|
||||
} else if (typeof config === 'object') {
|
||||
newComponent = await config.type();
|
||||
componentBindings = config.bindings(component as any);
|
||||
}
|
||||
|
||||
this.clear();
|
||||
|
||||
if (newComponent && !this.isDestroyed) {
|
||||
const bindings = [
|
||||
inputBinding('surfaceId', () => surfaceId),
|
||||
inputBinding('component', () => component),
|
||||
inputBinding('weight', () => component.weight ?? 'initial'),
|
||||
];
|
||||
|
||||
if (componentBindings) {
|
||||
bindings.push(...componentBindings);
|
||||
}
|
||||
|
||||
this.currentRef = this.viewContainerRef.createComponent(newComponent, {
|
||||
bindings,
|
||||
injector: this.viewContainerRef.injector,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private clear() {
|
||||
this.currentRef?.destroy();
|
||||
this.currentRef = null;
|
||||
}
|
||||
}
|
||||
22
vendor/a2ui/renderers/angular/src/lib/rendering/theming.ts
vendored
Normal file
22
vendor/a2ui/renderers/angular/src/lib/rendering/theming.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
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';
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const Theme = new InjectionToken<Theme>('Theme');
|
||||
|
||||
export type Theme = Types.Theme;
|
||||
21
vendor/a2ui/renderers/angular/src/public-api.ts
vendored
Normal file
21
vendor/a2ui/renderers/angular/src/public-api.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
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 './lib/rendering/index';
|
||||
export * from './lib/data/index';
|
||||
export * from './lib/config';
|
||||
export * from './lib/catalog/default';
|
||||
export { Surface } from './lib/catalog/surface';
|
||||
23
vendor/a2ui/renderers/angular/tsconfig.json
vendored
Normal file
23
vendor/a2ui/renderers/angular/tsconfig.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
16
vendor/a2ui/renderers/angular/tsconfig.lib.json
vendored
Normal file
16
vendor/a2ui/renderers/angular/tsconfig.lib.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
9
vendor/a2ui/renderers/angular/tsconfig.lib.prod.json
vendored
Normal file
9
vendor/a2ui/renderers/angular/tsconfig.lib.prod.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
12
vendor/a2ui/renderers/angular/tsconfig.spec.json
vendored
Normal file
12
vendor/a2ui/renderers/angular/tsconfig.spec.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user