@mcp-elements/angular 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/index.d.ts +506 -0
  2. package/dist/index.js +2714 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +28 -11
  5. package/src/accordion.component.ts +0 -74
  6. package/src/ai-badge.component.ts +0 -26
  7. package/src/alert.component.ts +0 -25
  8. package/src/avatar.component.ts +0 -24
  9. package/src/badge.component.ts +0 -17
  10. package/src/button.component.ts +0 -27
  11. package/src/card.component.ts +0 -46
  12. package/src/chat-bubble.component.ts +0 -53
  13. package/src/chips.component.ts +0 -33
  14. package/src/counter.component.ts +0 -48
  15. package/src/dialog.component.ts +0 -42
  16. package/src/drawer.component.ts +0 -48
  17. package/src/dropdown-menu.component.ts +0 -62
  18. package/src/feedback.component.ts +0 -71
  19. package/src/index.ts +0 -86
  20. package/src/input.component.ts +0 -46
  21. package/src/loader.component.ts +0 -12
  22. package/src/mcp/index.ts +0 -9
  23. package/src/mcp/mcp-app-frame.component.ts +0 -60
  24. package/src/mcp/mcp-consent-dialog.component.ts +0 -63
  25. package/src/mcp/mcp-resource-browser.component.ts +0 -86
  26. package/src/mcp/mcp-scope-inspector.component.ts +0 -81
  27. package/src/mcp/mcp-server-status.component.ts +0 -44
  28. package/src/mcp/mcp-tool-call.component.ts +0 -105
  29. package/src/mcp/mcp-tool-form.component.ts +0 -127
  30. package/src/password-input.component.ts +0 -35
  31. package/src/popover.component.ts +0 -40
  32. package/src/progress.component.ts +0 -20
  33. package/src/prompt-input.component.ts +0 -70
  34. package/src/select.component.ts +0 -106
  35. package/src/separator.component.ts +0 -15
  36. package/src/skeleton.component.ts +0 -11
  37. package/src/source-card.component.ts +0 -34
  38. package/src/streaming-text.component.ts +0 -43
  39. package/src/suggestion-chips.component.ts +0 -23
  40. package/src/switch.component.ts +0 -32
  41. package/src/tabs.component.ts +0 -95
  42. package/src/textarea.component.ts +0 -22
  43. package/src/toast.component.ts +0 -62
  44. package/src/tooltip.directive.ts +0 -63
@@ -1,53 +0,0 @@
1
- import { Component, input, computed } from '@angular/core'
2
-
3
- type ChatBubbleVariant = 'user' | 'ai'
4
-
5
- @Component({
6
- selector: 'mcpe-chat-bubble',
7
- standalone: true,
8
- template: `<div [class]="classes()"><ng-content /></div>`,
9
- })
10
- export class SnxChatBubbleComponent {
11
- variant = input<ChatBubbleVariant>('ai')
12
- class = input('')
13
- classes = computed(() =>
14
- ['mcpe-chat-bubble', `mcpe-chat-bubble-${this.variant()}`, this.class()].filter(Boolean).join(' ')
15
- )
16
- }
17
-
18
- @Component({
19
- selector: 'mcpe-chat-bubble-avatar',
20
- standalone: true,
21
- template: `<img class="mcpe-chat-bubble-avatar" [src]="src()" [alt]="alt()" />`,
22
- })
23
- export class SnxChatBubbleAvatarComponent {
24
- src = input.required<string>()
25
- alt = input('')
26
- }
27
-
28
- @Component({
29
- selector: 'mcpe-chat-bubble-content',
30
- standalone: true,
31
- template: `<div class="mcpe-chat-bubble-content"><ng-content /></div>`,
32
- })
33
- export class SnxChatBubbleContentComponent {}
34
-
35
- @Component({
36
- selector: 'mcpe-chat-bubble-timestamp',
37
- standalone: true,
38
- template: `<span class="mcpe-chat-bubble-timestamp"><ng-content /></span>`,
39
- })
40
- export class SnxChatBubbleTimestampComponent {}
41
-
42
- @Component({
43
- selector: 'mcpe-chat-bubble-typing',
44
- standalone: true,
45
- template: `
46
- <div class="mcpe-chat-bubble-typing">
47
- <span class="mcpe-chat-bubble-typing-dot"></span>
48
- <span class="mcpe-chat-bubble-typing-dot"></span>
49
- <span class="mcpe-chat-bubble-typing-dot"></span>
50
- </div>
51
- `,
52
- })
53
- export class SnxChatBubbleTypingComponent {}
@@ -1,33 +0,0 @@
1
- import { Component, input, output, computed } from '@angular/core'
2
-
3
- @Component({
4
- selector: 'mcpe-chip',
5
- standalone: true,
6
- template: `
7
- <span [class]="classes()">
8
- <ng-content />
9
- @if (removable()) {
10
- <button type="button" class="mcpe-chip-remove" (click)="remove.emit()" aria-label="Remove">
11
- <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
12
- <path d="M18 6 6 18" /><path d="m6 6 12 12" />
13
- </svg>
14
- </button>
15
- }
16
- </span>
17
- `,
18
- })
19
- export class SnxChipComponent {
20
- variant = input<'default' | 'primary' | 'outline' | 'destructive'>('default')
21
- removable = input(false)
22
- remove = output<void>()
23
- classes = computed(() =>
24
- ['mcpe-chip', `mcpe-chip-${this.variant()}`, this.removable() ? 'mcpe-chip-removable' : ''].filter(Boolean).join(' ')
25
- )
26
- }
27
-
28
- @Component({
29
- selector: 'mcpe-chips',
30
- standalone: true,
31
- template: `<div class="mcpe-chips"><ng-content /></div>`,
32
- })
33
- export class SnxChipsComponent {}
@@ -1,48 +0,0 @@
1
- import { Component, input, output, signal } from '@angular/core'
2
-
3
- @Component({
4
- selector: 'mcpe-counter',
5
- standalone: true,
6
- template: `
7
- <div class="mcpe-counter">
8
- <button type="button" class="mcpe-counter-button" (click)="decrement()"
9
- [disabled]="disabled() || value() <= min()" aria-label="Decrease">
10
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
11
- <path d="M5 12h14" />
12
- </svg>
13
- </button>
14
- <input type="text" inputmode="numeric" class="mcpe-counter-input"
15
- [value]="value()" (change)="onInput($event)" [disabled]="disabled()" aria-label="Count" />
16
- <button type="button" class="mcpe-counter-button" (click)="increment()"
17
- [disabled]="disabled() || value() >= max()" aria-label="Increase">
18
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
19
- <path d="M5 12h14" /><path d="M12 5v14" />
20
- </svg>
21
- </button>
22
- </div>
23
- `,
24
- })
25
- export class SnxCounterComponent {
26
- value = signal(0)
27
- min = input(0)
28
- max = input(99)
29
- step = input(1)
30
- disabled = input(false)
31
- valueChange = output<number>()
32
-
33
- decrement() {
34
- this.value.update(v => Math.max(this.min(), v - this.step()))
35
- this.valueChange.emit(this.value())
36
- }
37
- increment() {
38
- this.value.update(v => Math.min(this.max(), v + this.step()))
39
- this.valueChange.emit(this.value())
40
- }
41
- onInput(event: Event) {
42
- const num = parseInt((event.target as HTMLInputElement).value, 10)
43
- if (!isNaN(num)) {
44
- this.value.set(Math.max(this.min(), Math.min(this.max(), num)))
45
- this.valueChange.emit(this.value())
46
- }
47
- }
48
- }
@@ -1,42 +0,0 @@
1
- import { Component, input, output, signal, computed, effect, ElementRef, viewChild } from '@angular/core'
2
- import { createDialog } from '@mcp-elements/core'
3
-
4
- @Component({
5
- selector: 'mcpe-dialog',
6
- standalone: true,
7
- template: `
8
- @if (open()) {
9
- <div class="mcpe-dialog-overlay" (click)="close()"></div>
10
- <div
11
- #content
12
- class="mcpe-dialog-content"
13
- role="dialog"
14
- [attr.aria-modal]="modal()"
15
- (keydown.escape)="close()"
16
- >
17
- <ng-content />
18
- <button class="mcpe-dialog-close" (click)="close()" aria-label="Close">&#x2715;</button>
19
- </div>
20
- }
21
- `,
22
- })
23
- export class SnxDialogComponent {
24
- modal = input(true)
25
- open = signal(false)
26
- openChange = output<boolean>()
27
-
28
- toggle() {
29
- this.open.update(v => !v)
30
- this.openChange.emit(this.open())
31
- }
32
-
33
- close() {
34
- this.open.set(false)
35
- this.openChange.emit(false)
36
- }
37
-
38
- show() {
39
- this.open.set(true)
40
- this.openChange.emit(true)
41
- }
42
- }
@@ -1,48 +0,0 @@
1
- import { Component, input, output, signal } from '@angular/core'
2
-
3
- @Component({
4
- selector: 'mcpe-drawer',
5
- standalone: true,
6
- template: `
7
- @if (open()) {
8
- <div class="mcpe-drawer-overlay" (click)="close()"></div>
9
- <div
10
- [class]="'mcpe-drawer-content mcpe-drawer-content-' + side()"
11
- role="dialog"
12
- aria-modal="true"
13
- (keydown.escape)="close()"
14
- >
15
- <ng-content />
16
- <button class="mcpe-drawer-close" (click)="close()" aria-label="Close">
17
- <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
18
- <path d="M18 6 6 18" /><path d="m6 6 12 12" />
19
- </svg>
20
- </button>
21
- </div>
22
- }
23
- `,
24
- })
25
- export class SnxDrawerComponent {
26
- side = input<'left' | 'right' | 'top' | 'bottom'>('right')
27
- open = signal(false)
28
- openChange = output<boolean>()
29
-
30
- show() { this.open.set(true); this.openChange.emit(true) }
31
- close() { this.open.set(false); this.openChange.emit(false) }
32
- toggle() { this.open.update(v => !v); this.openChange.emit(this.open()) }
33
- }
34
-
35
- @Component({ selector: 'mcpe-drawer-header', standalone: true, template: `<div class="mcpe-drawer-header"><ng-content /></div>` })
36
- export class SnxDrawerHeaderComponent {}
37
-
38
- @Component({ selector: 'mcpe-drawer-footer', standalone: true, template: `<div class="mcpe-drawer-footer"><ng-content /></div>` })
39
- export class SnxDrawerFooterComponent {}
40
-
41
- @Component({ selector: 'mcpe-drawer-title', standalone: true, template: `<h2 class="mcpe-drawer-title"><ng-content /></h2>` })
42
- export class SnxDrawerTitleComponent {}
43
-
44
- @Component({ selector: 'mcpe-drawer-description', standalone: true, template: `<p class="mcpe-drawer-description"><ng-content /></p>` })
45
- export class SnxDrawerDescriptionComponent {}
46
-
47
- @Component({ selector: 'mcpe-drawer-body', standalone: true, template: `<div class="mcpe-drawer-body"><ng-content /></div>` })
48
- export class SnxDrawerBodyComponent {}
@@ -1,62 +0,0 @@
1
- import { Component, input, signal, ElementRef, viewChild } from '@angular/core'
2
-
3
- export interface DropdownItem {
4
- id: string
5
- label: string
6
- disabled?: boolean
7
- type?: 'item' | 'separator' | 'label'
8
- shortcut?: string
9
- }
10
-
11
- @Component({
12
- selector: 'mcpe-dropdown-menu',
13
- standalone: true,
14
- template: `
15
- <div class="relative inline-block" #container>
16
- <div (click)="toggle()">
17
- <ng-content select="[trigger]" />
18
- </div>
19
- @if (isOpen()) {
20
- <div class="mcpe-dropdown-menu-content absolute top-full mt-1 right-0" role="menu">
21
- @for (item of items(); track item.id) {
22
- @if (item.type === 'separator') {
23
- <div class="mcpe-dropdown-menu-separator" role="separator"></div>
24
- } @else if (item.type === 'label') {
25
- <div class="mcpe-dropdown-menu-label">{{ item.label }}</div>
26
- } @else {
27
- <div
28
- role="menuitem"
29
- [class]="'mcpe-dropdown-menu-item' + (item.disabled ? ' opacity-50 pointer-events-none' : '')"
30
- (click)="selectItem(item)"
31
- [attr.aria-disabled]="item.disabled"
32
- >
33
- <span class="flex-1">{{ item.label }}</span>
34
- @if (item.shortcut) {
35
- <span class="mcpe-dropdown-menu-shortcut">{{ item.shortcut }}</span>
36
- }
37
- </div>
38
- }
39
- }
40
- </div>
41
- }
42
- </div>
43
- `,
44
- host: { '(document:click)': 'onDocumentClick($event)' },
45
- })
46
- export class SnxDropdownMenuComponent {
47
- items = input<DropdownItem[]>([])
48
- isOpen = signal(false)
49
- container = viewChild<ElementRef>('container')
50
-
51
- toggle() { this.isOpen.update(v => !v) }
52
- close() { this.isOpen.set(false) }
53
-
54
- selectItem(item: DropdownItem) {
55
- if (!item.disabled) this.isOpen.set(false)
56
- }
57
-
58
- onDocumentClick(event: MouseEvent) {
59
- const el = this.container()?.nativeElement
60
- if (el && !el.contains(event.target as Node)) this.isOpen.set(false)
61
- }
62
- }
@@ -1,71 +0,0 @@
1
- import { Component, input, computed } from '@angular/core'
2
-
3
- @Component({
4
- selector: 'mcpe-feedback',
5
- standalone: true,
6
- template: `<div class="mcpe-feedback"><ng-content /></div>`,
7
- })
8
- export class SnxFeedbackComponent {}
9
-
10
- type FeedbackType = 'up' | 'down'
11
-
12
- @Component({
13
- selector: 'mcpe-feedback-btn',
14
- standalone: true,
15
- template: `
16
- <button [class]="classes()" type="button">
17
- @if (feedbackType() === 'up') {
18
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
19
- <path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/>
20
- </svg>
21
- } @else {
22
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
23
- <path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/>
24
- </svg>
25
- }
26
- </button>
27
- `,
28
- })
29
- export class SnxFeedbackButtonComponent {
30
- feedbackType = input.required<FeedbackType>({ alias: 'type' })
31
- selected = input(false)
32
- class = input('')
33
- classes = computed(() =>
34
- [
35
- 'mcpe-feedback-btn',
36
- `mcpe-feedback-btn-${this.feedbackType()}`,
37
- this.selected() ? 'mcpe-feedback-btn-selected' : '',
38
- this.class(),
39
- ].filter(Boolean).join(' ')
40
- )
41
- }
42
-
43
- @Component({
44
- selector: 'mcpe-feedback-separator',
45
- standalone: true,
46
- template: `<span class="mcpe-feedback-separator"></span>`,
47
- })
48
- export class SnxFeedbackSeparatorComponent {}
49
-
50
- @Component({
51
- selector: 'mcpe-feedback-form',
52
- standalone: true,
53
- template: `<div class="mcpe-feedback-form"><ng-content /></div>`,
54
- })
55
- export class SnxFeedbackFormComponent {}
56
-
57
- @Component({
58
- selector: 'mcpe-feedback-input',
59
- standalone: true,
60
- template: `<input class="mcpe-feedback-input" [placeholder]="placeholder()" />`,
61
- })
62
- export class SnxFeedbackInputComponent {
63
- placeholder = input('Add a comment...')
64
- }
65
-
66
- @Component({
67
- selector: 'mcpe-feedback-submit',
68
- standalone: true,
69
- template: `<button class="mcpe-feedback-submit" type="button"><ng-content /></button>`,
70
- })
71
- export class SnxFeedbackSubmitComponent {}
package/src/index.ts DELETED
@@ -1,86 +0,0 @@
1
- // CSS-only components
2
- export { SnxButtonComponent } from './button.component'
3
- export { SnxBadgeComponent } from './badge.component'
4
- export {
5
- SnxCardComponent,
6
- SnxCardHeaderComponent,
7
- SnxCardTitleComponent,
8
- SnxCardDescriptionComponent,
9
- SnxCardContentComponent,
10
- SnxCardFooterComponent,
11
- } from './card.component'
12
- export { SnxInputComponent } from './input.component'
13
- export { SnxTextareaComponent } from './textarea.component'
14
- export { SnxAvatarComponent } from './avatar.component'
15
- export { SnxSeparatorComponent } from './separator.component'
16
- export { SnxSkeletonComponent } from './skeleton.component'
17
-
18
- // Interactive components
19
- export { SnxDialogComponent } from './dialog.component'
20
- export {
21
- SnxTabsComponent,
22
- SnxTabsListComponent,
23
- SnxTabsTriggerComponent,
24
- SnxTabsContentComponent,
25
- } from './tabs.component'
26
- export {
27
- SnxAccordionComponent,
28
- SnxAccordionItemComponent,
29
- SnxAccordionTriggerComponent,
30
- SnxAccordionContentComponent,
31
- } from './accordion.component'
32
- export { SnxSelectComponent } from './select.component'
33
- export { SnxTooltipDirective } from './tooltip.directive'
34
- export { SnxPopoverComponent } from './popover.component'
35
-
36
- // New components
37
- export { SnxToasterComponent, SnxToastService } from './toast.component'
38
- export { SnxDrawerComponent, SnxDrawerHeaderComponent, SnxDrawerFooterComponent, SnxDrawerTitleComponent, SnxDrawerDescriptionComponent, SnxDrawerBodyComponent } from './drawer.component'
39
- export { SnxDropdownMenuComponent } from './dropdown-menu.component'
40
- export { SnxSwitchComponent } from './switch.component'
41
- export { SnxProgressComponent } from './progress.component'
42
- export { SnxLoaderComponent } from './loader.component'
43
- export { SnxChipComponent, SnxChipsComponent } from './chips.component'
44
- export { SnxPasswordInputComponent } from './password-input.component'
45
- export { SnxCounterComponent } from './counter.component'
46
- export { SnxAlertComponent, SnxAlertTitleComponent, SnxAlertDescriptionComponent } from './alert.component'
47
-
48
- // AI components
49
- export {
50
- SnxPromptInputComponent,
51
- SnxPromptInputTextareaComponent,
52
- SnxPromptInputFooterComponent,
53
- SnxPromptInputActionsComponent,
54
- SnxPromptInputCharCountComponent,
55
- SnxPromptInputAttachmentsComponent,
56
- SnxPromptInputAttachmentComponent,
57
- } from './prompt-input.component'
58
- export {
59
- SnxChatBubbleComponent,
60
- SnxChatBubbleAvatarComponent,
61
- SnxChatBubbleContentComponent,
62
- SnxChatBubbleTimestampComponent,
63
- SnxChatBubbleTypingComponent,
64
- } from './chat-bubble.component'
65
- export { SnxAiBadgeComponent } from './ai-badge.component'
66
- export { SnxSuggestionChipsComponent, SnxSuggestionChipComponent } from './suggestion-chips.component'
67
- export { SnxSourceCardsComponent, SnxSourceCardComponent } from './source-card.component'
68
- export {
69
- SnxStreamingTextComponent,
70
- SnxStreamingTextFadeInComponent,
71
- SnxStreamingTextWordComponent,
72
- SnxStreamingTextLineComponent,
73
- SnxStreamingTextSkeletonComponent,
74
- SnxStreamingTextSkeletonLineComponent,
75
- } from './streaming-text.component'
76
- export {
77
- SnxFeedbackComponent,
78
- SnxFeedbackButtonComponent,
79
- SnxFeedbackSeparatorComponent,
80
- SnxFeedbackFormComponent,
81
- SnxFeedbackInputComponent,
82
- SnxFeedbackSubmitComponent,
83
- } from './feedback.component'
84
-
85
- // MCP components
86
- export * from './mcp'
@@ -1,46 +0,0 @@
1
- import { Component, input, computed, output, forwardRef } from '@angular/core'
2
- import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3
-
4
- @Component({
5
- selector: 'mcpe-input',
6
- standalone: true,
7
- template: `
8
- <input
9
- [class]="classes()"
10
- [type]="type()"
11
- [placeholder]="placeholder()"
12
- [disabled]="disabled()"
13
- [value]="value()"
14
- (input)="onInput($event)"
15
- (blur)="onTouched()"
16
- />
17
- `,
18
- providers: [
19
- {
20
- provide: NG_VALUE_ACCESSOR,
21
- useExisting: forwardRef(() => SnxInputComponent),
22
- multi: true,
23
- },
24
- ],
25
- })
26
- export class SnxInputComponent implements ControlValueAccessor {
27
- type = input<string>('text')
28
- placeholder = input('')
29
- disabled = input(false)
30
- value = input('')
31
- class = input('')
32
-
33
- classes = computed(() => ['mcpe-input', this.class()].filter(Boolean).join(' '))
34
-
35
- private onChange: (value: string) => void = () => {}
36
- onTouched: () => void = () => {}
37
-
38
- onInput(event: Event) {
39
- const value = (event.target as HTMLInputElement).value
40
- this.onChange(value)
41
- }
42
-
43
- writeValue(value: string): void {}
44
- registerOnChange(fn: (value: string) => void): void { this.onChange = fn }
45
- registerOnTouched(fn: () => void): void { this.onTouched = fn }
46
- }
@@ -1,12 +0,0 @@
1
- import { Component, input, computed } from '@angular/core'
2
-
3
- @Component({
4
- selector: 'mcpe-loader',
5
- standalone: true,
6
- template: `<div [class]="classes()" role="status" aria-label="Loading"><span class="sr-only">Loading...</span></div>`,
7
- })
8
- export class SnxLoaderComponent {
9
- size = input<'sm' | 'md' | 'lg' | 'xl'>('md')
10
- variant = input<'primary' | 'muted'>('primary')
11
- classes = computed(() => `mcpe-loader mcpe-loader-${this.size()} mcpe-loader-${this.variant()}`)
12
- }
package/src/mcp/index.ts DELETED
@@ -1,9 +0,0 @@
1
- export { McpeMcpServerStatusComponent } from './mcp-server-status.component'
2
- export type { McpConnectionStatus } from './mcp-server-status.component'
3
- export { McpeMcpToolCallComponent } from './mcp-tool-call.component'
4
- export { McpeMcpToolFormComponent } from './mcp-tool-form.component'
5
- export { McpeMcpConsentDialogComponent } from './mcp-consent-dialog.component'
6
- export { McpeMcpScopeInspectorComponent } from './mcp-scope-inspector.component'
7
- export { McpeMcpResourceBrowserComponent } from './mcp-resource-browser.component'
8
- export type { McpResource } from './mcp-resource-browser.component'
9
- export { McpeMcpAppFrameComponent } from './mcp-app-frame.component'
@@ -1,60 +0,0 @@
1
- import { Component, input, output, effect, computed, ElementRef, viewChild, OnDestroy } from '@angular/core'
2
- import { cn, createAppBridge } from '@mcp-elements/core'
3
- import type { AppMessageEnvelope } from '@mcp-elements/core'
4
-
5
- @Component({
6
- selector: 'mcpe-mcp-app-frame',
7
- standalone: true,
8
- template: `
9
- <div [class]="classes()">
10
- <iframe
11
- #frame
12
- [src]="src()"
13
- [sandbox]="sandbox()"
14
- [style.height.px]="height()"
15
- title="MCP App"
16
- aria-label="MCP App frame"
17
- style="display:block;width:100%;border:none"
18
- ></iframe>
19
- </div>
20
- `,
21
- })
22
- export class McpeMcpAppFrameComponent implements OnDestroy {
23
- src = input.required<string>()
24
- height = input(480)
25
- sandbox = input('allow-scripts allow-same-origin')
26
- class = input('')
27
- onMessage = output<AppMessageEnvelope>()
28
-
29
- frame = viewChild<ElementRef<HTMLIFrameElement>>('frame')
30
-
31
- private _unsub: (() => void) | undefined
32
- private _removeListener: (() => void) | undefined
33
-
34
- constructor() {
35
- effect(() => {
36
- this._cleanup()
37
- const bridge = createAppBridge({
38
- postMessage: (env: AppMessageEnvelope) => {
39
- this.frame()?.nativeElement?.contentWindow?.postMessage(env, '*')
40
- },
41
- })
42
- const unsub = bridge.onMessage((env: AppMessageEnvelope) => this.onMessage.emit(env))
43
- const handler = (e: MessageEvent) => bridge.receive(e.data)
44
- window.addEventListener('message', handler)
45
- this._unsub = unsub
46
- this._removeListener = () => window.removeEventListener('message', handler)
47
- })
48
- }
49
-
50
- private _cleanup() {
51
- this._unsub?.()
52
- this._removeListener?.()
53
- this._unsub = undefined
54
- this._removeListener = undefined
55
- }
56
-
57
- ngOnDestroy() { this._cleanup() }
58
-
59
- classes = computed(() => cn('mcpe-mcp-app-frame', this.class()))
60
- }
@@ -1,63 +0,0 @@
1
- import { Component, input, output, computed } from '@angular/core'
2
- import { CommonModule } from '@angular/common'
3
- import { parseScopes } from '@mcp-elements/core'
4
-
5
- @Component({
6
- selector: 'mcpe-mcp-consent-dialog',
7
- standalone: true,
8
- imports: [CommonModule],
9
- template: `
10
- @if (open()) {
11
- <div class="mcpe-dialog-overlay" (click)="deny()"></div>
12
- <div class="mcpe-dialog-content" role="dialog" aria-modal="true" [attr.aria-label]="'Allow ' + serverName() + '?'">
13
- <!-- Server info -->
14
- <div class="mcpe-mcp-consent-dialog-server">
15
- <div class="mcpe-mcp-consent-dialog-icon" aria-hidden="true">
16
- @if (serverIcon()) {
17
- <img [src]="serverIcon()" alt="" class="h-full w-full object-cover" />
18
- } @else {
19
- {{ serverName()[0]?.toUpperCase() ?? '?' }}
20
- }
21
- </div>
22
- <div>
23
- <p class="mcpe-mcp-consent-dialog-server-name">{{ serverName() }}</p>
24
- <p class="mcpe-mcp-consent-dialog-server-meta">is requesting access to</p>
25
- </div>
26
- </div>
27
- <!-- Scopes -->
28
- <div class="mcpe-mcp-consent-dialog-scopes" role="list" aria-label="Requested permissions">
29
- @for (s of parsedScopes(); track s.raw) {
30
- <div class="mcpe-mcp-consent-dialog-scope-item" role="listitem">
31
- <div class="flex-1 min-w-0">
32
- <p class="mcpe-mcp-consent-dialog-scope-resource">{{ s.resource }}</p>
33
- <div class="mcpe-mcp-consent-dialog-scope-perms">
34
- @for (p of s.permissions; track p) {
35
- <span class="mcpe-mcp-consent-dialog-scope-perm">{{ p }}</span>
36
- }
37
- </div>
38
- </div>
39
- </div>
40
- }
41
- </div>
42
- <!-- Actions -->
43
- <div class="mcpe-mcp-consent-dialog-actions">
44
- <button class="mcpe-btn mcpe-btn-outline flex-1" (click)="deny()">Deny</button>
45
- <button class="mcpe-btn mcpe-btn-primary flex-1" (click)="approve()">Allow</button>
46
- </div>
47
- </div>
48
- }
49
- `,
50
- })
51
- export class McpeMcpConsentDialogComponent {
52
- open = input.required<boolean>()
53
- serverName = input.required<string>()
54
- serverIcon = input<string>()
55
- scopes = input<string[]>([])
56
- onApprove = output<void>()
57
- onDeny = output<void>()
58
-
59
- parsedScopes = computed(() => parseScopes(this.scopes().join(' ')))
60
-
61
- approve() { this.onApprove.emit() }
62
- deny() { this.onDeny.emit() }
63
- }