import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { InterceptorPlugin } from '../.interface'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; // NZ Modules import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzAlertModule } from 'ng-zorro-antd/alert'; import { NzSwitchModule } from 'ng-zorro-antd/switch'; import { NzInputModule } from 'ng-zorro-antd/input'; import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; import { NzSelectModule } from 'ng-zorro-antd/select'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzDividerModule } from 'ng-zorro-antd/divider'; import { NzTagModule } from 'ng-zorro-antd/tag'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; import { NzTabsModule } from 'ng-zorro-antd/tabs'; import { NzCollapseModule } from 'ng-zorro-antd/collapse'; @Component({ selector: 'app-plugin-settings-modal', standalone: true, imports: [ CommonModule, FormsModule, TranslateModule, NzModalModule, NzAlertModule, NzSwitchModule, NzInputModule, NzInputNumberModule, NzSelectModule, NzButtonModule, NzDividerModule, NzTagModule, NzToolTipModule, NzTabsModule, NzCollapseModule ], template: `
{{ getPluginIcon(plugin.name) }}

{{ plugin.name }}

{{ getPluginDescription(plugin.name) }}

Priority: {{ plugin.priority }} {{ plugin.enabled ? 'βœ… Enabled' : '❌ Disabled' }}
{{ 'postProcessor.pluginStatus' | translate }}:
βš™οΈ
{{ 'postProcessor.noConfigurableSettings' | translate }}
{{ getTranslatedLabel(schema) }}

{{ schema.description }}

{{ schema.description }}
{{ schema.description }}
{{ schema.description }}
{{ schema.description }}
Template
#123;ctx.currentTool}, Line=
Template
#123;ctx.lineNumber})" [nzAutosize]="{ minRows: 3, maxRows: 6 }" style="width: 100%; font-family: 'Consolas', 'Monaco', monospace; font-size: 13px; background: #f6f8fa; border: 2px solid #0969da; padding: 12px;">
Template
πŸ’‘ Template Syntax
πŸ’‘ Template Syntax
\$\{ctx.currentTool\} β†’ Current tool number
\$\{ctx.lineNumber\} β†’ Line number
\$\{cmd.type\} β†’ Command type
Pro tip: Use any JavaScript expression inside \$\{\}
#123;ctx.currentTool}
β†’ Current tool number
πŸ’‘ Template Syntax
\$\{ctx.currentTool\} β†’ Current tool number
\$\{ctx.lineNumber\} β†’ Line number
\$\{cmd.type\} β†’ Command type
Pro tip: Use any JavaScript expression inside \$\{\}
#123;ctx.lineNumber}
β†’ Line number
πŸ’‘ Template Syntax
\$\{ctx.currentTool\} β†’ Current tool number
\$\{ctx.lineNumber\} β†’ Line number
\$\{cmd.type\} β†’ Command type
Pro tip: Use any JavaScript expression inside \$\{\}
#123;cmd.type}
β†’ Command type
Pro tip: Use any JavaScript expression inside
πŸ’‘ Template Syntax
\$\{ctx.currentTool\} β†’ Current tool number
\$\{ctx.lineNumber\} β†’ Line number
\$\{cmd.type\} β†’ Command type
Pro tip: Use any JavaScript expression inside \$\{\}
#123;}
TypeScript
πŸš€ Quick Snippets:
ctx.currentTool - Current tool number
ctx.currentFeedRate - Current feed rate
ctx.currentSpindleSpeed - Spindle speed
ctx.lineNumber - Current line number
ctx.currentPosition - {x, y, z}
ctx.lastPosition - Previous position
ctx.isRapidMode - Boolean
ctx.currentAptFile - Current file info
ctx.profile - Machine profile
ctx.outputSettings - Output settings
cmd.type - Command type (GOTO, LOADTL, etc.)
cmd.rawLine - Original APT line
cmd.position - {x, y, z}
cmd.toolNumber - Tool number
cmd.diameter - Tool diameter
cmd.cutterParams - Cutter parameters
cmd.spindleSpeed - Spindle speed
cmd.feedRate - Feed rate
ctx.formatNumber(val, decimals?)
ctx.formatPosition(pos)
ctx.formatComment(text)
ctx.calculateDistance(from, to)
ctx.getAllAptFiles()
ctx.getAllToolNumbers()
ctx.getToolInfo(toolNum)
ctx.getCommandsByType(type)
ctx.findCommand(predicate)
ctx.getPlugin(name)
ctx.getPluginSettings(name)
ctx.setPluginSettings(name, settings)
Basic Header:
const lines = [];
lines.push(ctx.formatComment('Tool: T' + ctx.currentTool));
lines.push(ctx.formatComment('Feed: ' + ctx.currentFeedRate));
return lines;
File Info:
const file = ctx.currentAptFile;
const lines = [];
if (file) {
  lines.push(ctx.formatComment('File: ' + file.name));
  lines.push(ctx.formatComment('Offset: ' + file.workOffset));
}
return lines;
Tool List:
const tools = ctx.getAllToolNumbers();
const lines = [];
tools.forEach(t => {
  const info = ctx.getToolInfo(t);
  lines.push(ctx.formatComment('T' + t + ': D' + info.diameter));
});
return lines;
{{ schema.description }}
`, styles: [` .setting-card { padding: 16px; background: white; border: 1px solid #e8e8e8; border-radius: 8px; transition: all 0.3s ease; } .setting-card:hover { border-color: #1890ff; box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1); } code { padding: 2px 6px; background: #f5f5f5; border-radius: 3px; font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; } pre { overflow-x: auto; font-size: 11px; } `], changeDetection: ChangeDetectionStrategy.OnPush }) export class PluginSettingsModalComponent { @Input() visible = false; @Input() plugin: InterceptorPlugin | null = null; @Output() visibleChange = new EventEmitter(); @Output() save = new EventEmitter(); public customCodes: Map = new Map(); constructor( private cdr: ChangeDetectorRef, private translate: TranslateService ) { } ngOnChanges() { if (this.plugin?.settings?.customCode) { this.customCodes.set('customOutputTemplate', this.plugin.settings.customCode); } } get modalTitle(): string { return this.plugin ? `βš™οΈ ${this.plugin.name} Settings` : 'Plugin Settings'; } getEvaluationMode(schemaKey: string): 'template' | 'code' { return this.plugin?.settings?.evaluationMode || 'template'; } setEvaluationMode(schemaKey: string, mode: 'template' | 'code'): void { if (this.plugin?.settings) { this.plugin.settings.evaluationMode = mode; } this.cdr.markForCheck(); } getCustomCode(schemaKey: string): string { if (this.plugin?.settings?.customCode) { return this.plugin.settings.customCode; } if (!this.customCodes.has(schemaKey)) { this.customCodes.set(schemaKey, this.getDefaultCode()); } return this.customCodes.get(schemaKey)!; } onCustomCodeChange(schemaKey: string, code: string): void { this.customCodes.set(schemaKey, code); if (this.plugin?.settings) { this.plugin.settings.customCode = code; } } getDefaultCode(): string { return `// ctx and cmd are available const lines = []; lines.push(ctx.formatComment('Custom Header')); lines.push(ctx.formatComment(\`Tool: T\${ctx.currentTool || 0}\`)); lines.push(ctx.formatComment(\`Feed: \${ctx.currentFeedRate || 0}\`)); return lines;`; } getCodePlaceholder(): string { return 'Write TypeScript code here. Must return string or string[]'; } getQuickVariables(): Array<{label: string, code: string, description: string}> { return [ { label: 'ctx.currentTool', code: '${ctx.currentTool}', description: 'Current tool number' }, { label: 'ctx.lineNumber', code: '${ctx.lineNumber}', description: 'Current line number' }, { label: 'ctx.currentFeedRate', code: '${ctx.currentFeedRate}', description: 'Current feed rate' }, { label: 'ctx.currentSpindleSpeed', code: '${ctx.currentSpindleSpeed}', description: 'Current spindle speed' }, { label: 'cmd.type', code: '${cmd.type}', description: 'Command type' }, { label: 'cmd.position', code: '${cmd.position?.x}', description: 'Position X coordinate' }, { label: 'file.name', code: '${ctx.currentAptFile?.name}', description: 'Current file name' }, { label: 'file.workOffset', code: '${ctx.currentAptFile?.workOffset}', description: 'Work offset' } ]; } insertVariable(schemaKey: string, variableCode: string): void { if (!this.plugin?.settings) return; // Template mode'da direkt schema.key'e ekle const currentValue = this.plugin.settings[schemaKey] || ''; const newValue = currentValue + (currentValue ? ' ' : '') + variableCode; this.plugin.settings[schemaKey] = newValue; // Force UI update this.cdr.markForCheck(); // Trigger change detection setTimeout(() => { this.cdr.detectChanges(); }, 0); } insertVariableAtCursor(schemaKey: string, variableCode: string): void { if (!this.plugin?.settings) return; // Basit append yΓΆntemi (cursor pozisyonu yok) const currentValue = this.plugin.settings[schemaKey] || ''; this.plugin.settings[schemaKey] = currentValue + (currentValue ? ' ' : '') + variableCode; this.cdr.detectChanges(); } getCodeSnippets(): Array<{label: string, code: string, description: string}> { return [ { label: 'πŸ“ Basic Header', code: `const lines = []; lines.push(ctx.formatComment('=== HEADER ===')); lines.push(ctx.formatComment(\`Tool: T\${ctx.currentTool || 0}\`)); lines.push(ctx.formatComment(\`Feed: \${ctx.currentFeedRate || 0}\`)); return lines;`, description: 'Simple header with tool and feed' }, { label: 'πŸ“ File Info', code: `const file = ctx.currentAptFile; const lines = []; if (file) { lines.push(ctx.formatComment(\`File: \${file.name}\`)); lines.push(ctx.formatComment(\`Offset: \${file.workOffset}\`)); } return lines;`, description: 'Display current file information' }, { label: 'πŸ”§ Tool List', code: `const tools = ctx.getAllToolNumbers(); const lines = []; tools.forEach(t => { const info = ctx.getToolInfo(t); if (info) { lines.push(ctx.formatComment(\`T\${t}: D\${ctx.formatNumber(info.diameter)}\`)); } }); return lines;`, description: 'List all tools with diameters' }, { label: 'πŸ“Š Statistics', code: `const gotoCount = ctx.getCommandsByType('GOTO').length; const toolCount = ctx.getAllToolNumbers().length; const lines = []; lines.push(ctx.formatComment(\`GOTO Commands: \${gotoCount}\`)); lines.push(ctx.formatComment(\`Tools Used: \${toolCount}\`)); return lines;`, description: 'Show command statistics' }, { label: 'πŸ“ Bounds', code: `const bounds = ctx.calculateBounds(); const lines = []; if (bounds.xMin !== null) { lines.push(ctx.formatComment(\`X: \${ctx.formatNumber(bounds.xMin)} to \${ctx.formatNumber(bounds.xMax)}\`)); lines.push(ctx.formatComment(\`Y: \${ctx.formatNumber(bounds.yMin)} to \${ctx.formatNumber(bounds.yMax)}\`)); lines.push(ctx.formatComment(\`Z: \${ctx.formatNumber(bounds.zMin)} to \${ctx.formatNumber(bounds.zMax)}\`)); } return lines;`, description: 'Calculate and display part boundaries' }, { label: '🎯 Current Position', code: `const pos = ctx.currentPosition; const lines = []; lines.push(ctx.formatComment(\`Position: X\${ctx.formatNumber(pos.x)} Y\${ctx.formatNumber(pos.y)} Z\${ctx.formatNumber(pos.z)}\`)); return lines;`, description: 'Display current position' } ]; } insertSnippet(schemaKey: string, code: string): void { this.customCodes.set(schemaKey, code); if (this.plugin?.settings) { this.plugin.settings.customCode = code; } this.cdr.markForCheck(); } getTranslatedLabel(schema: any): string { const translationKey = `postProcessor.settings.${schema.key}`; const translated = this.translate.instant(translationKey); return translated !== translationKey ? translated : schema.label; } getTranslatedOptionLabel(option: any, schemaKey: string): string { const translationKey = `postProcessor.settings.${schemaKey}.${option.value}`; const translated = this.translate.instant(translationKey); return translated !== translationKey ? translated : option.label; } getPluginIcon(pluginName: string): string { const icons: Record = { 'HeaderSummary': 'πŸ“‹', 'HeaderSummaryFull': 'πŸ“‹', 'MotionPlugin': '🎯', 'ToolChangePlugin': 'πŸ”§', 'SpindlePlugin': 'βš™οΈ', 'CoolantPlugin': 'πŸ’§', 'CommentPlugin': 'πŸ’¬', 'FooterPlugin': 'πŸ“„', 'OperationSeparator': 'πŸ“‚', 'SafetyPlugin': 'πŸ›‘οΈ', 'OptimizationPlugin': '⚑', 'ValidationPlugin': 'βœ…' }; return icons[pluginName] || 'πŸ”Œ'; } getPluginDescription(pluginName: string): string { const descriptions: Record = { 'HeaderSummary': 'Generate program header with file and tool information', 'HeaderSummaryFull': 'Advanced header with TypeScript evaluation support', 'MotionPlugin': 'Handle motion commands (GOTO, RAPID)', 'ToolChangePlugin': 'Process tool change operations', 'SpindlePlugin': 'Control spindle speed and rotation', 'CoolantPlugin': 'Manage coolant on/off commands', 'CommentPlugin': 'Add comments and annotations', 'FooterPlugin': 'Generate program footer', 'OperationSeparator': 'Add separators between operations', 'SafetyPlugin': 'Safety checks and validations', 'OptimizationPlugin': 'Optimize G-code output', 'ValidationPlugin': 'Validate commands and parameters' }; return descriptions[pluginName] || 'Plugin for post-processing'; } getPriorityColor(priority: number): string { if (priority >= 1000) return 'red'; if (priority >= 500) return 'orange'; if (priority >= 100) return 'blue'; return 'default'; } resetToDefaults(): void { if (!this.plugin?.settingsSchema) return; this.plugin.settingsSchema.forEach(schema => { if (this.plugin!.settings) { this.plugin!.settings[schema.key] = schema.defaultValue; } }); this.customCodes.clear(); if (this.plugin.settings) { this.plugin.settings.evaluationMode = 'template'; } this.cdr.markForCheck(); } handleCancel(): void { this.visible = false; this.visibleChange.emit(false); } handleSave(): void { if (this.plugin) { if (this.customCodes.has('customOutputTemplate')) { if (!this.plugin.settings) { this.plugin.settings = {}; } this.plugin.settings.customCode = this.customCodes.get('customOutputTemplate'); } this.save.emit(this.plugin); } this.visible = false; this.visibleChange.emit(false); } }