humainflow-web/src/app/shared/nodes/generic-node/generic-node.html

199 lines
7.1 KiB
HTML

<div class="llm-node"
[class.llm-node--human]="isHumanNode()">
<div class="llm-header cursor-move">
<div class="llm-icon">
@if (isHumanNode()) {
<i class="bi bi-person-check-fill"></i>
} @else {
<img src="llm_node.png" alt="LLM" class="llm-icon-image" />
}
</div>
<div class="llm-header-content">
<span class="llm-title">{{ nodeTitle() }}</span>
<div class="llm-subtitle-row">
<span class="llm-subtitle">{{ name }}</span>
<button type="button" class="llm-name-edit-btn" title="Edit name" (pointerdown)="$event.stopPropagation()" (click)="openNameEditor($event)">
<i class="bi bi-pencil-square"></i>
</button>
</div>
</div>
<div class="llm-header-actions">
@if (missingRequiredParams.length) {
<div class="llm-warning-wrap">
<div class="llm-warning">
<i class="bi bi-exclamation-triangle-fill"></i>
</div>
<div class="llm-warning-tooltip">
<div class="llm-warning-title">Missing required fields</div>
@for (missing of missingRequiredParams; track missing) {
<div class="llm-warning-item">{{ missing }}</div>
}
</div>
</div>
}
<button type="button" class="llm-delete-btn" title="Delete node" (pointerdown)="$event.stopPropagation()" (click)="confirmDelete($event)">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<div class="llm-body">
<div class="llm-column">
<div class="llm-column-title">Inputs</div>
@for (input of inputs; track input.key) {
<div class="llm-row llm-row-input">
<div
refComponent
class="llm-socket"
[data]="{
type: 'socket',
side: 'input',
key: input.key,
nodeId: data.id,
payload: input.socket
}"
[emit]="emit">
</div>
<span class="llm-pill llm-pill-input">
{{ input.key }}
</span>
</div>
}
</div>
<div class="llm-column">
<div class="llm-column-title llm-column-title-right">Outputs</div>
@for (output of outputs; track output.key) {
<div class="llm-row llm-row-output">
<span class="llm-pill llm-pill-output">{{ output.key }}</span>
<div
refComponent
class="llm-socket llm-socket-right"
[data]="{
type: 'socket',
side: 'output',
key: output.key,
nodeId: data.id,
payload: output.socket
}"
[emit]="emit">
</div>
</div>
}
</div>
</div>
<div class="llm-params">
@if (parameterFieldGroups.length) {
<div class="llm-param-groups">
@for (group of parameterFieldGroups; track group.key) {
<fieldset class="llm-param-fieldset">
<legend class="llm-param-legend">{{ group.legend }}</legend>
<div class="llm-param-grid">
@for (field of group.fields; track field.path) {
<div class="llm-param-chip">
<div class="llm-param-row-head">
<span class="llm-param-key">{{ field.label }}</span>
<button
type="button"
class="llm-edit-btn"
[attr.title]="'Edit ' + field.label.toLowerCase()"
(pointerdown)="$event.stopPropagation()"
(click)="openParameterEditor(field.path, $event)">
<i class="bi bi-pen"></i>
</button>
</div>
<span class="llm-param-value">{{ field.value }}</span>
</div>
}
</div>
</fieldset>
}
</div>
}
@if (parameterFields.length) {
<div class="llm-param-grid">
@for (field of parameterFields; track field.path) {
<div class="llm-param-chip">
<div class="llm-param-row-head">
<span class="llm-param-key">{{ field.label }}</span>
<button
type="button"
class="llm-edit-btn"
[attr.title]="'Edit ' + field.label.toLowerCase()"
(pointerdown)="$event.stopPropagation()"
(click)="openParameterEditor(field.path, $event)">
<i class="bi bi-pen"></i>
</button>
</div>
<span class="llm-param-value">{{ field.value }}</span>
</div>
}
</div>
}
@if (hasMainContent()) {
<div class="llm-param-block">
<div class="llm-param-row-head">
<div class="llm-param-key">{{ mainContentLabel() }}</div>
<button type="button" class="llm-edit-btn" [attr.title]="'Edit ' + mainContentLabel().toLowerCase()" (pointerdown)="$event.stopPropagation()" (click)="openMainContentEditor($event)">
<i class="bi bi-pen"></i>
</button>
</div>
<div class="llm-param-text">
@if (!mainContentParts().length) {
-
} @else {
@for (part of mainContentParts(); track $index) {
@if (part.isDynamicInput) {
<span class="llm-inline-token">{{ formatDynamicInputToken(part.text) }}</span>
} @else {
<span>{{ part.text }}</span>
}
}
}
</div>
</div>
}
</div>
@if (localEditorOpen) {
<div class="llm-modal-backdrop" (pointerdown)="$event.stopPropagation()" (click)="closeSimpleParamEditor($event)">
<div class="llm-modal" (pointerdown)="$event.stopPropagation()" (click)="$event.stopPropagation()">
<div class="llm-modal-title">Edit {{ localEditorLabel }}</div>
<div class="llm-modal-field">
<label>{{ localEditorLabel }}</label>
@if (localEditorLoading) {
<input type="text" [ngModel]="localEditorValue" placeholder="Loading..." disabled (pointerdown)="$event.stopPropagation()" />
} @else if (localEditorType === 'boolean') {
<select [(ngModel)]="localEditorValue" (pointerdown)="$event.stopPropagation()">
<option value="true">true</option>
<option value="false">false</option>
</select>
} @else if (localEditorOptions.length) {
<select [(ngModel)]="localEditorValue" (pointerdown)="$event.stopPropagation()">
<option value="">Select {{ localEditorLabel | lowercase }}...</option>
@for (option of localEditorOptions; track option) {
<option [value]="option">{{ option }}</option>
}
</select>
} @else {
<input
[type]="localEditorType === 'number' || localEditorType === 'integer' ? 'number' : 'text'"
[(ngModel)]="localEditorValue"
[attr.maxlength]="localEditorMaxLength"
[placeholder]="localEditorMaxLength ? 'Max ' + localEditorMaxLength + ' characters' : 'Value...'"
(pointerdown)="$event.stopPropagation()" />
}
</div>
<div class="llm-modal-actions">
<button type="button" class="llm-btn llm-btn-ghost" (pointerdown)="$event.stopPropagation()" (click)="closeSimpleParamEditor($event)">Cancel</button>
<button type="button" class="llm-btn llm-btn-primary" (pointerdown)="$event.stopPropagation()" (click)="saveSimpleParamEditor($event)">Save</button>
</div>
</div>
</div>
}
</div>