{"version":3,"file":"xxmachina-components-organisms-terminal-input-section.mjs","sources":["../../../../../packages/@xxmachina/components/src/lib/organisms/terminal-input-section/terminal-input-section.organism.ts","../../../../../packages/@xxmachina/components/src/lib/organisms/terminal-input-section/terminal-input-section.organism.html","../../../../../packages/@xxmachina/components/src/lib/organisms/terminal-input-section/xxmachina-components-organisms-terminal-input-section.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, ElementRef, inject, input, output, untracked, viewChild } from '@angular/core';\n\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatTooltipModule } from '@angular/material/tooltip';\nimport { Action } from '@ng-atomic/core';\nimport { SpeechService } from '@xxmachina/common/services/speech';\n\nexport enum ActionId {\n  SEND = 'send',\n}\n\n@Component({\n  selector: 'organisms-terminal-input-section',\n  standalone: true,\n  imports: [\n    ReactiveFormsModule,\n    MatButtonModule,\n    MatIconModule,\n    MatTooltipModule\n],\n  templateUrl: './terminal-input-section.organism.html',\n  styleUrls: ['./terminal-input-section.organism.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  host: { class: 'organism terminal-input section' },\n})\nexport class TerminalInputSectionOrganism {\n  static readonly ActionId = ActionId;\n\n  readonly speechService = inject(SpeechService);\n\n  readonly control = input(new FormControl('', { nonNullable: true }));\n  readonly placeholder = input('Enter command...');\n  readonly prefix = input('>');\n  readonly clearOnSend = input(true);\n  readonly enableVoiceInput = input(false);\n  readonly sessionId = input<string | undefined>(undefined);\n\n  readonly action = output<Action<string>>();\n\n  readonly inputField = viewChild<ElementRef<HTMLTextAreaElement>>('inputField');\n\n  private readonly componentId = this.speechService.generateComponentId();\n\n  // このコンポーネントがアクティブかどうか\n  readonly isRecording = computed(() =>\n    this.speechService.isListening() &&\n    this.speechService.isActiveComponent(this.componentId)\n  );\n\n  // IME変換中かどうかのフラグ\n  private isComposing = false;\n  // IME変換中にEnterが押されたかどうか（確定Enterの検出用）\n  private enterPressedDuringComposition = false;\n\n  constructor() {\n    // アクティブな場合のみ、音声認識結果を入力フィールドに反映\n    effect(() => {\n      if (this.enableVoiceInput() && this.speechService.isActiveComponent(this.componentId)) {\n        const transcript = this.speechService.transcript();\n        const text = transcript.finalized + transcript.interim;\n        // clear()時は空文字でsetValueしてフォームもクリア\n        this.control().setValue(text);\n      }\n    });\n\n    // autoMode + selectedSessionId の変化を検知し、書き込み先を登録\n    effect(() => {\n      const autoMode = this.speechService.autoModeEnabled();\n      const selectedId = this.speechService.selectedSessionId();\n      const sid = this.sessionId();\n      if (autoMode && sid && selectedId === sid) {\n        // Speech APIは動き続けている。書き込み先を自分に登録するだけ\n        untracked(() => this.speechService.activateComponent(this.componentId));\n      } else if (autoMode && sid && selectedId !== sid) {\n        // deactivate: 別セッションに切り替わった → 入力欄にテキストがあれば送信\n        untracked(() => this.send());\n      }\n    });\n  }\n\n  onCompositionStart() {\n    this.isComposing = true;\n    this.enterPressedDuringComposition = false;\n  }\n\n  onCompositionEnd() {\n    this.isComposing = false;\n    // Chromeではkeydownがcompositionendより先に発火するため、\n    // IME確定のEnterを検出した場合はcompositionend後に送信する\n    if (this.enterPressedDuringComposition) {\n      this.enterPressedDuringComposition = false;\n      setTimeout(() => this.send(), 0);\n    }\n  }\n\n  handleKeydown(event: KeyboardEvent) {\n    if (this.isComposing) {\n      // IME変換中のEnterを記録（確定Enter = 送信トリガー）\n      if (event.key === 'Enter' && !event.shiftKey) {\n        this.enterPressedDuringComposition = true;\n      }\n      return;\n    }\n    if (event.key === 'Enter' && !event.shiftKey) {\n      event.preventDefault();\n      this.send();\n    }\n  }\n\n  onInput() {\n    this.adjustTextareaHeight();\n  }\n\n  send() {\n    const value = this.control().value.trim();\n    if (value) {\n      this.action.emit({ id: ActionId.SEND, payload: value });\n      if (this.clearOnSend()) {\n        this.control().reset();\n        this.resetTextareaHeight();\n        // 自動モード時: stopしない。clear()でtranscriptだけリセット\n        if (this.isRecording()) {\n          this.speechService.clear();\n        }\n      }\n    }\n  }\n\n  private adjustTextareaHeight() {\n    const textarea = this.inputField()?.nativeElement;\n    if (textarea) {\n      // overflowを一時的にhiddenにしてscrollHeightを正確に取得\n      textarea.style.overflow = 'hidden';\n      // 一旦高さを最小に設定してscrollHeightを正確に取得\n      textarea.style.height = '0px';\n      // scrollHeightに合わせて高さを設定（最大200px）\n      const newHeight = Math.min(textarea.scrollHeight, 200);\n      textarea.style.height = `${newHeight}px`;\n      // overflowを元に戻す\n      textarea.style.overflow = 'auto';\n    }\n  }\n\n  private resetTextareaHeight() {\n    const textarea = this.inputField()?.nativeElement;\n    if (textarea) {\n      textarea.style.height = 'auto';\n    }\n  }\n\n  focus() {\n    this.inputField()?.nativeElement?.focus();\n  }\n\n  toggleVoiceInput() {\n    this.speechService.toggle(this.componentId);\n  }\n}\n","<div class=\"input-line\">\n  <span class=\"input-prefix\">{{ prefix() }}</span>\n  <div class=\"input-container\">\n    <textarea\n      #inputField\n      class=\"terminal-input\"\n      [formControl]=\"control()\"\n      [placeholder]=\"placeholder()\"\n      (keydown)=\"handleKeydown($event)\"\n      (input)=\"onInput()\"\n      (compositionstart)=\"onCompositionStart()\"\n      (compositionend)=\"onCompositionEnd()\"\n      rows=\"1\"\n    ></textarea>\n  </div>\n  @if (enableVoiceInput() && speechService.isSupported) {\n    <button\n      mat-icon-button\n      (click)=\"toggleVoiceInput()\"\n      [matTooltip]=\"isRecording() ? '音声入力停止' : '音声入力'\"\n      [class.recording]=\"isRecording()\">\n      <mat-icon>{{ isRecording() ? 'mic' : 'mic_none' }}</mat-icon>\n    </button>\n  }\n  <button mat-icon-button (click)=\"send()\" matTooltip=\"送信\">\n    <mat-icon>send</mat-icon>\n  </button>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}