@mcp-elements/angular 0.1.0 → 0.1.2
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.
- package/dist/index.d.ts +506 -0
- package/dist/index.js +2738 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -11
- package/src/accordion.component.ts +0 -74
- package/src/ai-badge.component.ts +0 -26
- package/src/alert.component.ts +0 -25
- package/src/avatar.component.ts +0 -24
- package/src/badge.component.ts +0 -17
- package/src/button.component.ts +0 -27
- package/src/card.component.ts +0 -46
- package/src/chat-bubble.component.ts +0 -53
- package/src/chips.component.ts +0 -33
- package/src/counter.component.ts +0 -48
- package/src/dialog.component.ts +0 -42
- package/src/drawer.component.ts +0 -48
- package/src/dropdown-menu.component.ts +0 -62
- package/src/feedback.component.ts +0 -71
- package/src/index.ts +0 -86
- package/src/input.component.ts +0 -46
- package/src/loader.component.ts +0 -12
- package/src/mcp/index.ts +0 -9
- package/src/mcp/mcp-app-frame.component.ts +0 -60
- package/src/mcp/mcp-consent-dialog.component.ts +0 -63
- package/src/mcp/mcp-resource-browser.component.ts +0 -86
- package/src/mcp/mcp-scope-inspector.component.ts +0 -81
- package/src/mcp/mcp-server-status.component.ts +0 -44
- package/src/mcp/mcp-tool-call.component.ts +0 -105
- package/src/mcp/mcp-tool-form.component.ts +0 -127
- package/src/password-input.component.ts +0 -35
- package/src/popover.component.ts +0 -40
- package/src/progress.component.ts +0 -20
- package/src/prompt-input.component.ts +0 -70
- package/src/select.component.ts +0 -106
- package/src/separator.component.ts +0 -15
- package/src/skeleton.component.ts +0 -11
- package/src/source-card.component.ts +0 -34
- package/src/streaming-text.component.ts +0 -43
- package/src/suggestion-chips.component.ts +0 -23
- package/src/switch.component.ts +0 -32
- package/src/tabs.component.ts +0 -95
- package/src/textarea.component.ts +0 -22
- package/src/toast.component.ts +0 -62
- package/src/tooltip.directive.ts +0 -63
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { Component, input, output, computed } from '@angular/core'
|
|
2
|
-
import { CommonModule } from '@angular/common'
|
|
3
|
-
import { cn } from '@mcp-elements/core'
|
|
4
|
-
|
|
5
|
-
export interface McpResource {
|
|
6
|
-
uri: string
|
|
7
|
-
name: string
|
|
8
|
-
mimeType?: string
|
|
9
|
-
description?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function mimeTypeLabel(mimeType?: string): string {
|
|
13
|
-
if (!mimeType) return 'res'
|
|
14
|
-
if (mimeType.includes('json')) return 'json'
|
|
15
|
-
if (mimeType.includes('text')) return 'txt'
|
|
16
|
-
if (mimeType.includes('image')) return 'img'
|
|
17
|
-
if (mimeType.includes('pdf')) return 'pdf'
|
|
18
|
-
return mimeType.split('/')[1]?.slice(0, 4) ?? 'res'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@Component({
|
|
22
|
-
selector: 'mcpe-mcp-resource-browser',
|
|
23
|
-
standalone: true,
|
|
24
|
-
imports: [CommonModule],
|
|
25
|
-
template: `
|
|
26
|
-
@if (loading()) {
|
|
27
|
-
<div [class]="classes()">
|
|
28
|
-
@for (n of skeletonItems; track n) {
|
|
29
|
-
<div class="flex items-center gap-3 px-3 py-2.5">
|
|
30
|
-
<div class="h-8 w-8 rounded-md animate-pulse bg-muted"></div>
|
|
31
|
-
<div class="h-4 flex-1 rounded animate-pulse bg-muted"></div>
|
|
32
|
-
</div>
|
|
33
|
-
}
|
|
34
|
-
</div>
|
|
35
|
-
} @else if (resources().length === 0) {
|
|
36
|
-
<div [class]="classes()">
|
|
37
|
-
<p class="mcpe-mcp-resource-browser-empty">No resources available</p>
|
|
38
|
-
</div>
|
|
39
|
-
} @else {
|
|
40
|
-
<div [class]="classes()" role="list">
|
|
41
|
-
@for (r of resources(); track r.uri) {
|
|
42
|
-
<button
|
|
43
|
-
type="button"
|
|
44
|
-
role="listitem"
|
|
45
|
-
[class]="itemClass(r.uri)"
|
|
46
|
-
[attr.aria-selected]="selectedUri() === r.uri"
|
|
47
|
-
[attr.aria-label]="r.name"
|
|
48
|
-
(click)="select(r)"
|
|
49
|
-
>
|
|
50
|
-
<span class="mcpe-mcp-resource-browser-icon" aria-hidden="true">{{ mimeLabel(r.mimeType) }}</span>
|
|
51
|
-
<span class="mcpe-mcp-resource-browser-name">{{ r.name }}</span>
|
|
52
|
-
@if (r.mimeType) {
|
|
53
|
-
<span class="mcpe-mcp-resource-browser-type">{{ r.mimeType.split('/')[0] }}</span>
|
|
54
|
-
}
|
|
55
|
-
</button>
|
|
56
|
-
}
|
|
57
|
-
</div>
|
|
58
|
-
}
|
|
59
|
-
`,
|
|
60
|
-
})
|
|
61
|
-
export class McpeMcpResourceBrowserComponent {
|
|
62
|
-
resources = input<McpResource[]>([])
|
|
63
|
-
selectedUri = input<string>()
|
|
64
|
-
loading = input(false)
|
|
65
|
-
class = input('')
|
|
66
|
-
onSelect = output<McpResource>()
|
|
67
|
-
|
|
68
|
-
readonly skeletonItems = [1, 2, 3, 4]
|
|
69
|
-
|
|
70
|
-
classes = computed(() => cn('mcpe-mcp-resource-browser', this.class()))
|
|
71
|
-
|
|
72
|
-
itemClass(uri: string): string {
|
|
73
|
-
return cn(
|
|
74
|
-
'mcpe-mcp-resource-browser-item w-full text-left',
|
|
75
|
-
this.selectedUri() === uri ? 'mcpe-mcp-resource-browser-item-selected' : ''
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
mimeLabel(mimeType?: string): string {
|
|
80
|
-
return mimeTypeLabel(mimeType)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
select(r: McpResource) {
|
|
84
|
-
this.onSelect.emit(r)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { Component, input, signal, computed } from '@angular/core'
|
|
2
|
-
import { CommonModule } from '@angular/common'
|
|
3
|
-
import { cn, parseScopes } from '@mcp-elements/core'
|
|
4
|
-
import type { ScopeDescriptor } from '@mcp-elements/core'
|
|
5
|
-
|
|
6
|
-
@Component({
|
|
7
|
-
selector: 'mcpe-mcp-scope-inspector',
|
|
8
|
-
standalone: true,
|
|
9
|
-
imports: [CommonModule],
|
|
10
|
-
template: `
|
|
11
|
-
<div [class]="classes()" role="list">
|
|
12
|
-
@for (s of parsedScopes(); track s.raw) {
|
|
13
|
-
<div class="mcpe-mcp-scope-inspector-item" role="listitem">
|
|
14
|
-
<button
|
|
15
|
-
type="button"
|
|
16
|
-
class="mcpe-mcp-scope-inspector-trigger"
|
|
17
|
-
[attr.aria-expanded]="isOpen(s.raw)"
|
|
18
|
-
(click)="toggle(s.raw)"
|
|
19
|
-
>
|
|
20
|
-
<div class="flex items-center gap-3">
|
|
21
|
-
<span class="mcpe-mcp-scope-inspector-resource">{{ s.resource }}</span>
|
|
22
|
-
<div class="mcpe-mcp-scope-inspector-perms">
|
|
23
|
-
@for (p of s.permissions; track p) {
|
|
24
|
-
<span class="mcpe-mcp-scope-inspector-perm">{{ p }}</span>
|
|
25
|
-
}
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
<svg
|
|
29
|
-
[class]="chevronClass(s.raw)"
|
|
30
|
-
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
|
31
|
-
fill="none" stroke="currentColor" stroke-width="2"
|
|
32
|
-
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
33
|
-
<path d="m6 9 6 6 6-6"/>
|
|
34
|
-
</svg>
|
|
35
|
-
</button>
|
|
36
|
-
@if (isOpen(s.raw) && getDescription(s)) {
|
|
37
|
-
<div role="region" class="mcpe-mcp-scope-inspector-body">
|
|
38
|
-
{{ getDescription(s) }}
|
|
39
|
-
</div>
|
|
40
|
-
}
|
|
41
|
-
</div>
|
|
42
|
-
}
|
|
43
|
-
</div>
|
|
44
|
-
`,
|
|
45
|
-
})
|
|
46
|
-
export class McpeMcpScopeInspectorComponent {
|
|
47
|
-
scopes = input<string | ScopeDescriptor[]>('')
|
|
48
|
-
descriptions = input<Record<string, string>>({})
|
|
49
|
-
class = input('')
|
|
50
|
-
|
|
51
|
-
openKeys = signal<Set<string>>(new Set())
|
|
52
|
-
|
|
53
|
-
parsedScopes = computed((): ScopeDescriptor[] => {
|
|
54
|
-
const s = this.scopes()
|
|
55
|
-
return typeof s === 'string' ? parseScopes(s) : s
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
classes = computed(() => cn('mcpe-mcp-scope-inspector', this.class()))
|
|
59
|
-
|
|
60
|
-
toggle(key: string) {
|
|
61
|
-
this.openKeys.update((prev: Set<string>) => {
|
|
62
|
-
const next = new Set(prev)
|
|
63
|
-
if (next.has(key)) next.delete(key)
|
|
64
|
-
else next.add(key)
|
|
65
|
-
return next
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
isOpen(key: string): boolean {
|
|
70
|
-
return this.openKeys().has(key)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
chevronClass(key: string): string {
|
|
74
|
-
return cn('mcpe-mcp-scope-inspector-chevron', this.isOpen(key) ? 'mcpe-mcp-scope-inspector-chevron-open' : '')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
getDescription(s: ScopeDescriptor): string | undefined {
|
|
78
|
-
const d = this.descriptions()
|
|
79
|
-
return d[s.raw] ?? d[s.resource] ?? s.description
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Component, input, computed } from '@angular/core'
|
|
2
|
-
import { cn } from '@mcp-elements/core'
|
|
3
|
-
|
|
4
|
-
export type McpConnectionStatus = 'connected' | 'connecting' | 'disconnected' | 'error'
|
|
5
|
-
|
|
6
|
-
const STATUS_LABELS: Record<McpConnectionStatus, string> = {
|
|
7
|
-
connected: 'Connected',
|
|
8
|
-
connecting: 'Connecting',
|
|
9
|
-
disconnected: 'Disconnected',
|
|
10
|
-
error: 'Error',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@Component({
|
|
14
|
-
selector: 'mcpe-mcp-server-status',
|
|
15
|
-
standalone: true,
|
|
16
|
-
template: `
|
|
17
|
-
<span
|
|
18
|
-
[class]="classes()"
|
|
19
|
-
role="status"
|
|
20
|
-
aria-live="polite"
|
|
21
|
-
[attr.aria-label]="ariaLabel()"
|
|
22
|
-
>
|
|
23
|
-
<span class="mcpe-mcp-server-status-dot" aria-hidden="true"></span>
|
|
24
|
-
{{ label() }}
|
|
25
|
-
</span>
|
|
26
|
-
`,
|
|
27
|
-
})
|
|
28
|
-
export class McpeMcpServerStatusComponent {
|
|
29
|
-
status = input.required<McpConnectionStatus>()
|
|
30
|
-
serverName = input<string>()
|
|
31
|
-
class = input('')
|
|
32
|
-
|
|
33
|
-
classes = computed(() => cn('mcpe-mcp-server-status', `mcpe-mcp-server-status-${this.status()}`, this.class()))
|
|
34
|
-
label = computed(() => {
|
|
35
|
-
const s = this.serverName()
|
|
36
|
-
const statusLabel = STATUS_LABELS[this.status() as McpConnectionStatus]
|
|
37
|
-
return s ? `${s} · ${statusLabel}` : statusLabel
|
|
38
|
-
})
|
|
39
|
-
ariaLabel = computed(() => {
|
|
40
|
-
const s = this.serverName()
|
|
41
|
-
const statusLabel = STATUS_LABELS[this.status() as McpConnectionStatus]
|
|
42
|
-
return s ? `${s}: ${statusLabel}` : statusLabel
|
|
43
|
-
})
|
|
44
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Component, input, output, signal, effect, computed, OnDestroy } from '@angular/core'
|
|
2
|
-
import { CommonModule } from '@angular/common'
|
|
3
|
-
import { cn } from '@mcp-elements/core'
|
|
4
|
-
import type { ToolStateApi, ToolStateSnapshot } from '@mcp-elements/core'
|
|
5
|
-
|
|
6
|
-
const STATUS_LABELS: Record<string, string> = {
|
|
7
|
-
idle: 'idle',
|
|
8
|
-
pending: 'pending',
|
|
9
|
-
running: 'running',
|
|
10
|
-
done: 'done',
|
|
11
|
-
error: 'error',
|
|
12
|
-
cancelled: 'cancelled',
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@Component({
|
|
16
|
-
selector: 'mcpe-mcp-tool-call',
|
|
17
|
-
standalone: true,
|
|
18
|
-
imports: [CommonModule],
|
|
19
|
-
template: `
|
|
20
|
-
<div [class]="classes()">
|
|
21
|
-
<!-- Header -->
|
|
22
|
-
<div class="mcpe-mcp-tool-call-header">
|
|
23
|
-
<div class="mcpe-mcp-tool-call-name">
|
|
24
|
-
<span class="mcpe-mcp-tool-call-icon" aria-hidden="true">fn</span>
|
|
25
|
-
<span class="mcpe-mcp-tool-call-title">{{ displayName() }}</span>
|
|
26
|
-
</div>
|
|
27
|
-
<span [class]="badgeClass()">
|
|
28
|
-
@if (snap().status === 'running') {
|
|
29
|
-
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
|
30
|
-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
|
|
31
|
-
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
|
|
32
|
-
</svg>
|
|
33
|
-
}
|
|
34
|
-
{{ statusLabels[snap().status] }}
|
|
35
|
-
</span>
|
|
36
|
-
</div>
|
|
37
|
-
<!-- Args -->
|
|
38
|
-
@if (displayArgs()) {
|
|
39
|
-
<pre class="mcpe-mcp-tool-call-args">{{ displayArgs() | json }}</pre>
|
|
40
|
-
}
|
|
41
|
-
<!-- Progress bar -->
|
|
42
|
-
@if (snap().status === 'running') {
|
|
43
|
-
<div class="mcpe-mcp-tool-call-progress" role="progressbar" aria-label="Tool running">
|
|
44
|
-
<div class="mcpe-mcp-tool-call-progress-bar" style="width: 60%"></div>
|
|
45
|
-
</div>
|
|
46
|
-
}
|
|
47
|
-
<!-- Result -->
|
|
48
|
-
@if (snap().status === 'done' && snap().result) {
|
|
49
|
-
<div class="mcpe-mcp-tool-call-result mcpe-mcp-tool-call-result-done">
|
|
50
|
-
@for (block of textBlocks(); track $index) {
|
|
51
|
-
<p class="whitespace-pre-wrap text-sm">{{ block }}</p>
|
|
52
|
-
}
|
|
53
|
-
</div>
|
|
54
|
-
}
|
|
55
|
-
<!-- Error -->
|
|
56
|
-
@if (snap().status === 'error' && snap().error) {
|
|
57
|
-
<div class="mcpe-mcp-tool-call-result mcpe-mcp-tool-call-result-error">
|
|
58
|
-
<p class="text-sm">{{ snap().error?.message }}</p>
|
|
59
|
-
<button (click)="onRetry.emit()" class="text-xs underline underline-offset-2">Retry</button>
|
|
60
|
-
</div>
|
|
61
|
-
}
|
|
62
|
-
</div>
|
|
63
|
-
`,
|
|
64
|
-
})
|
|
65
|
-
export class McpeMcpToolCallComponent implements OnDestroy {
|
|
66
|
-
state = input.required<ToolStateApi>()
|
|
67
|
-
toolName = input<string>()
|
|
68
|
-
args = input<Record<string, unknown>>()
|
|
69
|
-
onRetry = output<void>()
|
|
70
|
-
class = input('')
|
|
71
|
-
|
|
72
|
-
readonly statusLabels = STATUS_LABELS
|
|
73
|
-
|
|
74
|
-
snap = signal<ToolStateSnapshot>({ status: 'idle' })
|
|
75
|
-
private _unsub: (() => void) | undefined
|
|
76
|
-
|
|
77
|
-
constructor() {
|
|
78
|
-
effect(() => {
|
|
79
|
-
this._unsub?.()
|
|
80
|
-
const s = this.state()
|
|
81
|
-
this.snap.set({
|
|
82
|
-
status: s.status,
|
|
83
|
-
tool: s.tool,
|
|
84
|
-
args: s.args,
|
|
85
|
-
result: s.result,
|
|
86
|
-
error: s.error,
|
|
87
|
-
startedAt: s.startedAt,
|
|
88
|
-
endedAt: s.endedAt,
|
|
89
|
-
})
|
|
90
|
-
this._unsub = s.subscribe((snapshot: ToolStateSnapshot) => this.snap.set({ ...snapshot }))
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
ngOnDestroy() { this._unsub?.() }
|
|
95
|
-
|
|
96
|
-
classes = computed(() => cn('mcpe-mcp-tool-call', this.class()))
|
|
97
|
-
badgeClass = computed(() => cn('mcpe-mcp-tool-call-badge', `mcpe-mcp-tool-call-badge-${this.snap().status}`))
|
|
98
|
-
displayName = computed(() => this.snap().tool ?? this.toolName() ?? 'unknown')
|
|
99
|
-
displayArgs = computed(() => this.snap().args ?? this.args())
|
|
100
|
-
textBlocks = computed(() =>
|
|
101
|
-
this.snap().result?.content
|
|
102
|
-
.filter((c: { type: string }) => c.type === 'text')
|
|
103
|
-
.map((c: { type: string; text?: string }) => (c as { type: 'text'; text: string }).text) ?? []
|
|
104
|
-
)
|
|
105
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { Component, input, output, signal, computed, OnInit } from '@angular/core'
|
|
2
|
-
import { CommonModule } from '@angular/common'
|
|
3
|
-
import { cn, schemaToFields } from '@mcp-elements/core'
|
|
4
|
-
import type { JsonSchema, FieldDescriptor } from '@mcp-elements/core'
|
|
5
|
-
|
|
6
|
-
@Component({
|
|
7
|
-
selector: 'mcpe-mcp-tool-form',
|
|
8
|
-
standalone: true,
|
|
9
|
-
imports: [CommonModule],
|
|
10
|
-
template: `
|
|
11
|
-
<form [class]="classes()" (ngSubmit)="handleSubmit()">
|
|
12
|
-
@if (fields().length === 0) {
|
|
13
|
-
<p class="text-sm text-muted-foreground">This tool takes no inputs.</p>
|
|
14
|
-
}
|
|
15
|
-
@for (field of fields(); track field.key) {
|
|
16
|
-
<div class="mcpe-mcp-tool-form-field">
|
|
17
|
-
<label
|
|
18
|
-
[for]="field.key"
|
|
19
|
-
[class]="labelClass(field)"
|
|
20
|
-
>{{ field.label }}</label>
|
|
21
|
-
@switch (field.kind) {
|
|
22
|
-
@case ('switch') {
|
|
23
|
-
<input type="checkbox" [id]="field.key" class="mcpe-switch"
|
|
24
|
-
[checked]="getBool(field.key)"
|
|
25
|
-
(change)="onCheckChange(field.key, $event)" />
|
|
26
|
-
}
|
|
27
|
-
@case ('select') {
|
|
28
|
-
<select [id]="field.key" class="mcpe-select"
|
|
29
|
-
[value]="getStr(field.key)"
|
|
30
|
-
(change)="onInputChange(field.key, $event)">
|
|
31
|
-
<option value="">Select…</option>
|
|
32
|
-
@for (opt of field.options ?? []; track opt.value) {
|
|
33
|
-
<option [value]="opt.value">{{ opt.label }}</option>
|
|
34
|
-
}
|
|
35
|
-
</select>
|
|
36
|
-
}
|
|
37
|
-
@case ('textarea') {
|
|
38
|
-
<textarea [id]="field.key" class="mcpe-textarea" rows="4"
|
|
39
|
-
[value]="getStr(field.key)"
|
|
40
|
-
(input)="onInputChange(field.key, $event)"></textarea>
|
|
41
|
-
}
|
|
42
|
-
@case ('number') {
|
|
43
|
-
<input type="number" [id]="field.key" class="mcpe-input"
|
|
44
|
-
[value]="getStr(field.key)"
|
|
45
|
-
(input)="onNumberChange(field.key, $event)" />
|
|
46
|
-
}
|
|
47
|
-
@default {
|
|
48
|
-
<input [type]="inputType(field)" [id]="field.key" class="mcpe-input"
|
|
49
|
-
[value]="getStr(field.key)"
|
|
50
|
-
(input)="onInputChange(field.key, $event)" />
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
@if (field.help) {
|
|
54
|
-
<p class="mcpe-mcp-tool-form-help">{{ field.help }}</p>
|
|
55
|
-
}
|
|
56
|
-
</div>
|
|
57
|
-
}
|
|
58
|
-
<div class="mcpe-mcp-tool-form-submit">
|
|
59
|
-
<button type="submit" class="mcpe-btn mcpe-btn-primary mcpe-btn-sm" [disabled]="loading()">
|
|
60
|
-
{{ loading() ? 'Running…' : submitLabel() }}
|
|
61
|
-
</button>
|
|
62
|
-
</div>
|
|
63
|
-
</form>
|
|
64
|
-
`,
|
|
65
|
-
})
|
|
66
|
-
export class McpeMcpToolFormComponent implements OnInit {
|
|
67
|
-
schema = input.required<JsonSchema>()
|
|
68
|
-
loading = input(false)
|
|
69
|
-
submitLabel = input('Run')
|
|
70
|
-
class = input('')
|
|
71
|
-
onSubmit = output<Record<string, unknown>>()
|
|
72
|
-
|
|
73
|
-
fields = computed(() => schemaToFields(this.schema()))
|
|
74
|
-
values = signal<Record<string, unknown>>({})
|
|
75
|
-
|
|
76
|
-
ngOnInit() {
|
|
77
|
-
const defaults: Record<string, unknown> = {}
|
|
78
|
-
for (const f of this.fields()) {
|
|
79
|
-
if (f.defaultValue !== undefined) defaults[f.key] = f.defaultValue
|
|
80
|
-
}
|
|
81
|
-
this.values.set(defaults)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
classes = computed(() => cn('mcpe-mcp-tool-form', this.class()))
|
|
85
|
-
|
|
86
|
-
setValue(key: string, value: unknown) {
|
|
87
|
-
this.values.update((v: Record<string, unknown>) => ({ ...v, [key]: value }))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
onInputChange(key: string, event: Event) {
|
|
91
|
-
this.setValue(key, (event.target as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement).value)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
onNumberChange(key: string, event: Event) {
|
|
95
|
-
this.setValue(key, (event.target as HTMLInputElement).valueAsNumber)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
onCheckChange(key: string, event: Event) {
|
|
99
|
-
this.setValue(key, (event.target as HTMLInputElement).checked)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
getStr(key: string): string {
|
|
103
|
-
const v = this.values()[key]
|
|
104
|
-
return v == null ? '' : String(v)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
getBool(key: string): boolean {
|
|
108
|
-
return Boolean(this.values()[key])
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
labelClass(field: FieldDescriptor): string {
|
|
112
|
-
return cn('mcpe-mcp-tool-form-label', field.required ? 'mcpe-mcp-tool-form-label-required' : '')
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
inputType(field: FieldDescriptor): string {
|
|
116
|
-
switch (field.kind) {
|
|
117
|
-
case 'email': return 'email'
|
|
118
|
-
case 'url': return 'url'
|
|
119
|
-
case 'date': return 'date'
|
|
120
|
-
default: return 'text'
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
handleSubmit() {
|
|
125
|
-
this.onSubmit.emit(this.values())
|
|
126
|
-
}
|
|
127
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { Component, input, signal, computed } from '@angular/core'
|
|
2
|
-
|
|
3
|
-
@Component({
|
|
4
|
-
selector: 'mcpe-password-input',
|
|
5
|
-
standalone: true,
|
|
6
|
-
template: `
|
|
7
|
-
<div class="mcpe-password-input-wrapper">
|
|
8
|
-
<input
|
|
9
|
-
[type]="showPassword() ? 'text' : 'password'"
|
|
10
|
-
class="mcpe-password-input"
|
|
11
|
-
[placeholder]="placeholder()"
|
|
12
|
-
[disabled]="disabled()"
|
|
13
|
-
/>
|
|
14
|
-
<button type="button" class="mcpe-password-toggle" (click)="showPassword.update(v => !v)"
|
|
15
|
-
[attr.aria-label]="showPassword() ? 'Hide password' : 'Show password'" tabindex="-1">
|
|
16
|
-
@if (showPassword()) {
|
|
17
|
-
<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">
|
|
18
|
-
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
|
|
19
|
-
<line x1="1" y1="1" x2="23" y2="23" />
|
|
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="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
24
|
-
<circle cx="12" cy="12" r="3" />
|
|
25
|
-
</svg>
|
|
26
|
-
}
|
|
27
|
-
</button>
|
|
28
|
-
</div>
|
|
29
|
-
`,
|
|
30
|
-
})
|
|
31
|
-
export class SnxPasswordInputComponent {
|
|
32
|
-
placeholder = input('')
|
|
33
|
-
disabled = input(false)
|
|
34
|
-
showPassword = signal(false)
|
|
35
|
-
}
|
package/src/popover.component.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Component, input, signal, ElementRef, viewChild } from '@angular/core'
|
|
2
|
-
|
|
3
|
-
@Component({
|
|
4
|
-
selector: 'mcpe-popover',
|
|
5
|
-
standalone: true,
|
|
6
|
-
template: `
|
|
7
|
-
<div class="relative inline-block" #container>
|
|
8
|
-
<div (click)="toggle()">
|
|
9
|
-
<ng-content select="[trigger]" />
|
|
10
|
-
</div>
|
|
11
|
-
@if (isOpen()) {
|
|
12
|
-
<div
|
|
13
|
-
class="mcpe-popover-content absolute top-full mt-2"
|
|
14
|
-
role="dialog"
|
|
15
|
-
(keydown.escape)="close()"
|
|
16
|
-
>
|
|
17
|
-
<ng-content />
|
|
18
|
-
</div>
|
|
19
|
-
}
|
|
20
|
-
</div>
|
|
21
|
-
`,
|
|
22
|
-
host: {
|
|
23
|
-
'(document:click)': 'onDocumentClick($event)',
|
|
24
|
-
},
|
|
25
|
-
})
|
|
26
|
-
export class SnxPopoverComponent {
|
|
27
|
-
isOpen = signal(false)
|
|
28
|
-
container = viewChild<ElementRef>('container')
|
|
29
|
-
|
|
30
|
-
toggle() { this.isOpen.update(v => !v) }
|
|
31
|
-
close() { this.isOpen.set(false) }
|
|
32
|
-
show() { this.isOpen.set(true) }
|
|
33
|
-
|
|
34
|
-
onDocumentClick(event: MouseEvent) {
|
|
35
|
-
const el = this.container()?.nativeElement
|
|
36
|
-
if (el && !el.contains(event.target as Node)) {
|
|
37
|
-
this.isOpen.set(false)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Component, input, computed } from '@angular/core'
|
|
2
|
-
|
|
3
|
-
@Component({
|
|
4
|
-
selector: 'mcpe-progress',
|
|
5
|
-
standalone: true,
|
|
6
|
-
template: `
|
|
7
|
-
<div class="mcpe-progress" role="progressbar"
|
|
8
|
-
[attr.aria-valuenow]="value()" [attr.aria-valuemin]="0" [attr.aria-valuemax]="max()">
|
|
9
|
-
<div class="mcpe-progress-indicator" [style.transform]="transform()"></div>
|
|
10
|
-
</div>
|
|
11
|
-
`,
|
|
12
|
-
})
|
|
13
|
-
export class SnxProgressComponent {
|
|
14
|
-
value = input(0)
|
|
15
|
-
max = input(100)
|
|
16
|
-
transform = computed(() => {
|
|
17
|
-
const pct = Math.min(Math.max((this.value() / this.max()) * 100, 0), 100)
|
|
18
|
-
return `translateX(-${100 - pct}%)`
|
|
19
|
-
})
|
|
20
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { Component, input, output, computed } from '@angular/core'
|
|
2
|
-
|
|
3
|
-
@Component({
|
|
4
|
-
selector: 'mcpe-prompt-input',
|
|
5
|
-
standalone: true,
|
|
6
|
-
template: `<div [class]="classes()"><ng-content /></div>`,
|
|
7
|
-
})
|
|
8
|
-
export class SnxPromptInputComponent {
|
|
9
|
-
class = input('')
|
|
10
|
-
classes = computed(() => ['mcpe-prompt-input', this.class()].filter(Boolean).join(' '))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@Component({
|
|
14
|
-
selector: 'mcpe-prompt-input-textarea',
|
|
15
|
-
standalone: true,
|
|
16
|
-
template: `<textarea class="mcpe-prompt-input-textarea" [placeholder]="placeholder()" [rows]="rows()"></textarea>`,
|
|
17
|
-
})
|
|
18
|
-
export class SnxPromptInputTextareaComponent {
|
|
19
|
-
placeholder = input('')
|
|
20
|
-
rows = input(3)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@Component({
|
|
24
|
-
selector: 'mcpe-prompt-input-footer',
|
|
25
|
-
standalone: true,
|
|
26
|
-
template: `<div class="mcpe-prompt-input-footer"><ng-content /></div>`,
|
|
27
|
-
})
|
|
28
|
-
export class SnxPromptInputFooterComponent {}
|
|
29
|
-
|
|
30
|
-
@Component({
|
|
31
|
-
selector: 'mcpe-prompt-input-actions',
|
|
32
|
-
standalone: true,
|
|
33
|
-
template: `<div class="mcpe-prompt-input-actions"><ng-content /></div>`,
|
|
34
|
-
})
|
|
35
|
-
export class SnxPromptInputActionsComponent {}
|
|
36
|
-
|
|
37
|
-
@Component({
|
|
38
|
-
selector: 'mcpe-prompt-input-char-count',
|
|
39
|
-
standalone: true,
|
|
40
|
-
template: `<span class="mcpe-prompt-input-char-count">{{ count() }}@if (max()) { / {{ max() }} }</span>`,
|
|
41
|
-
})
|
|
42
|
-
export class SnxPromptInputCharCountComponent {
|
|
43
|
-
count = input(0)
|
|
44
|
-
max = input<number | undefined>(undefined)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@Component({
|
|
48
|
-
selector: 'mcpe-prompt-input-attachments',
|
|
49
|
-
standalone: true,
|
|
50
|
-
template: `<div class="mcpe-prompt-input-attachments"><ng-content /></div>`,
|
|
51
|
-
})
|
|
52
|
-
export class SnxPromptInputAttachmentsComponent {}
|
|
53
|
-
|
|
54
|
-
@Component({
|
|
55
|
-
selector: 'mcpe-prompt-input-attachment',
|
|
56
|
-
standalone: true,
|
|
57
|
-
template: `
|
|
58
|
-
<span class="mcpe-prompt-input-attachment">
|
|
59
|
-
<ng-content />
|
|
60
|
-
<button type="button" class="mcpe-prompt-input-attachment-remove" (click)="remove.emit()" aria-label="Remove">
|
|
61
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
62
|
-
<path d="M18 6 6 18" /><path d="m6 6 12 12" />
|
|
63
|
-
</svg>
|
|
64
|
-
</button>
|
|
65
|
-
</span>
|
|
66
|
-
`,
|
|
67
|
-
})
|
|
68
|
-
export class SnxPromptInputAttachmentComponent {
|
|
69
|
-
remove = output()
|
|
70
|
-
}
|