{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "pomodoro-focus-timer",
  "title": "Pomodoro Focus Timer",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/pomodoro-focus-timer/pomodoro-focus-timer.component.ts",
      "content": "// Pomodoro productivity workspace: focus / break interval timers with an SVG circular\n// progress countdown, active task queue with pomodoro estimate pills, ambient sound\n// generator switcher, and daily focus analytics. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  BarChart3,\n  Check,\n  Flame,\n  Headphones,\n  ListTodo,\n  LucideAngularModule,\n  Music2,\n  Pause,\n  Play,\n  Plus,\n  RotateCcw,\n  SkipForward,\n  Target,\n  Trash2,\n  Trees,\n  Volume2,\n  VolumeX,\n  Waves,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport interface FocusTask {\n  id: string\n  title: string\n  tag: string\n  done: boolean\n  currentPomodoros: number\n  targetPomodoros: number\n}\n\nexport type TimerMode = 'focus' | 'shortBreak' | 'longBreak'\n\nexport const MODE_DURATIONS: Record<TimerMode, number> = {\n  focus: 25 * 60,\n  shortBreak: 5 * 60,\n  longBreak: 15 * 60,\n}\n\nconst defaultTasks: FocusTask[] = [\n  {\n    id: 'task-1',\n    title: 'Refactor OKLCH color-mix utilities in Tailwind v4',\n    tag: 'Design System',\n    done: false,\n    currentPomodoros: 2,\n    targetPomodoros: 3,\n  },\n  {\n    id: 'task-2',\n    title: 'Audit ARIA accessibility roles on interactive primitives',\n    tag: 'Accessibility',\n    done: true,\n    currentPomodoros: 2,\n    targetPomodoros: 2,\n  },\n  {\n    id: 'task-3',\n    title: 'Draft technical RFC for component variant registry',\n    tag: 'Architecture',\n    done: false,\n    currentPomodoros: 0,\n    targetPomodoros: 2,\n  },\n  {\n    id: 'task-4',\n    title: 'Benchmark Astro island hydration bundle sizes',\n    tag: 'Performance',\n    done: false,\n    currentPomodoros: 1,\n    targetPomodoros: 4,\n  },\n]\n\ninterface AmbientSound {\n  id: string\n  name: string\n  label: string\n}\n\nconst ambientSounds: AmbientSound[] = [\n  { id: 'off', name: 'Off / Mute', label: 'Silent' },\n  { id: 'whitenoise', name: 'Deep Focus White Noise', label: 'White Noise' },\n  { id: 'lofi', name: 'Lofi Beats', label: 'Lofi Beats' },\n  { id: 'rainforest', name: 'Rainforest', label: 'Rainforest' },\n]\n\n// SVG geometry: radius 104, circumference 2 * PI * 104 ≈ 653.451\nconst RADIUS = 104\nconst CIRCUMFERENCE = 2 * Math.PI * RADIUS\n\nexport function formatTimerTime(seconds: number): string {\n  const mins = Math.floor(seconds / 60)\n  const secs = seconds % 60\n  return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-pomodoro-focus-timer, [ui-pomodoro-focus-timer]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCheckboxComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"pomodoro-focus-timer\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <header class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex items-center gap-2\">\n            <h2 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">\n              Focus Space &amp; Pomodoro Timer\n            </h2>\n            <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\"> v2.4 </span>\n          </div>\n          <p class=\"text-muted-foreground text-sm\">\n            High-yield interval productivity engine with ambient soundscapes and active task execution.\n          </p>\n        </div>\n\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <span\n            ui-badge\n            wrap\n            variant=\"outline\"\n            class=\"border-warning/30 bg-warning/10 text-warning gap-1.5 px-3 py-1.5 text-xs font-semibold\"\n          >\n            <lucide-icon [img]=\"Flame\" class=\"fill-warning text-warning size-4\" />\n            <span>🔥 6-Day Focus Streak · {{ completedSessionsToday() }} Sessions Complete Today</span>\n          </span>\n        </div>\n      </header>\n\n      <!-- Ambient Sound Preset Bar -->\n      <div ui-card>\n        <div ui-card-content class=\"flex flex-wrap items-center justify-between gap-3 p-3 sm:p-4\">\n          <div class=\"text-muted-foreground flex items-center gap-2 text-xs font-medium\">\n            <lucide-icon [img]=\"Headphones\" class=\"text-primary size-4\" />\n            <span class=\"text-foreground font-semibold\">Ambient Sound:</span>\n            <span class=\"hidden sm:inline\">Choose your background audio focus generator</span>\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-1.5\">\n            @for (sound of ambientSounds; track sound.id) {\n              <button\n                ui-button\n                [variant]=\"selectedSound() === sound.id && !isSoundMuted() ? 'default' : 'outline'\"\n                size=\"sm\"\n                class=\"h-8 gap-1.5 text-xs\"\n                (click)=\"selectAmbientSound(sound.id)\"\n              >\n                <lucide-icon [img]=\"soundIcon(sound.id)\" class=\"size-3.5\" />\n                <span>{{ sound.name }}</span>\n                @if (selectedSound() === sound.id && !isSoundMuted() && sound.id !== 'off') {\n                  <span class=\"flex items-center gap-0.5\">\n                    <span class=\"bg-primary-foreground h-2 w-0.5 animate-pulse\"></span>\n                    <span class=\"bg-primary-foreground h-3.5 w-0.5 animate-pulse delay-75\"></span>\n                    <span class=\"bg-primary-foreground h-1.5 w-0.5 animate-pulse delay-150\"></span>\n                  </span>\n                }\n              </button>\n            }\n\n            <button\n              ui-button\n              variant=\"ghost\"\n              size=\"icon-sm\"\n              class=\"text-muted-foreground hover:text-foreground size-8\"\n              [attr.title]=\"isSoundMuted() ? 'Unmute Ambient Sound' : 'Mute Ambient Sound'\"\n              (click)=\"toggleSoundMute()\"\n            >\n              @if (isSoundMuted()) {\n                <lucide-icon [img]=\"VolumeX\" class=\"text-destructive size-4\" />\n              } @else {\n                <lucide-icon [img]=\"Volume2\" class=\"size-4\" />\n              }\n            </button>\n          </div>\n        </div>\n      </div>\n\n      <!-- Main Grid: Timer Centerpiece (Left) and Tasks + Analytics (Right) -->\n      <div class=\"grid grid-cols-1 items-start gap-6 lg:grid-cols-12\">\n        <!-- Left Column: Centerpiece Timer -->\n        <div class=\"space-y-6 lg:col-span-6\">\n          <div ui-card class=\"border-border relative overflow-hidden shadow-xs\">\n            <div ui-card-header class=\"pb-2 text-center\">\n              <div class=\"bg-muted/70 mx-auto flex w-full max-w-sm items-center justify-center rounded-xl p-1\">\n                <button\n                  ui-button\n                  [variant]=\"currentMode() === 'focus' ? 'default' : 'ghost'\"\n                  size=\"sm\"\n                  class=\"flex-1 rounded-lg text-xs font-semibold\"\n                  (click)=\"setMode('focus')\"\n                >\n                  Focus 25m\n                </button>\n                <button\n                  ui-button\n                  [variant]=\"currentMode() === 'shortBreak' ? 'default' : 'ghost'\"\n                  size=\"sm\"\n                  class=\"flex-1 rounded-lg text-xs font-semibold\"\n                  (click)=\"setMode('shortBreak')\"\n                >\n                  Short Break 5m\n                </button>\n                <button\n                  ui-button\n                  [variant]=\"currentMode() === 'longBreak' ? 'default' : 'ghost'\"\n                  size=\"sm\"\n                  class=\"flex-1 rounded-lg text-xs font-semibold\"\n                  (click)=\"setMode('longBreak')\"\n                >\n                  Long Break 15m\n                </button>\n              </div>\n            </div>\n\n            <div ui-card-content class=\"flex flex-col items-center justify-center pt-4 pb-2\">\n              <div class=\"relative flex size-64 items-center justify-center sm:size-72\">\n                <svg class=\"size-full -rotate-90 transform\" viewBox=\"0 0 256 256\">\n                  <circle\n                    cx=\"128\"\n                    cy=\"128\"\n                    [attr.r]=\"radius\"\n                    fill=\"transparent\"\n                    stroke=\"currentColor\"\n                    stroke-width=\"10\"\n                    class=\"text-muted/40\"\n                  />\n                  <circle\n                    cx=\"128\"\n                    cy=\"128\"\n                    [attr.r]=\"radius\"\n                    fill=\"transparent\"\n                    stroke=\"currentColor\"\n                    stroke-width=\"10\"\n                    stroke-linecap=\"round\"\n                    [attr.stroke-dasharray]=\"circumference\"\n                    [attr.stroke-dashoffset]=\"strokeDashoffset()\"\n                    [class]=\"progressRingClass\"\n                  />\n                </svg>\n\n                <div class=\"absolute inset-0 flex flex-col items-center justify-center text-center\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <span [class]=\"statusDotClass\"></span>\n                    <span [class]=\"modeLabelClass\">\n                      {{\n                        currentMode() === 'focus'\n                          ? 'Deep Focus'\n                          : currentMode() === 'shortBreak'\n                            ? 'Short Break'\n                            : 'Rest Phase'\n                      }}\n                    </span>\n                  </div>\n\n                  <div\n                    class=\"text-foreground my-1 font-mono text-5xl font-bold tracking-tight tabular-nums sm:text-6xl\"\n                  >\n                    {{ formatTime(remainingSeconds()) }}\n                  </div>\n\n                  <div class=\"text-muted-foreground text-xs font-medium\">\n                    @if (isRunning()) {\n                      <span>Session {{ currentSession() }} of 4 active</span>\n                    } @else {\n                      <span>Timer Paused</span>\n                    }\n                  </div>\n                </div>\n              </div>\n\n              <div\n                class=\"border-border/70 bg-muted/40 mt-4 flex w-full max-w-sm items-center justify-between gap-2 rounded-lg border p-2.5 text-xs\"\n              >\n                <div class=\"flex min-w-0 flex-wrap items-center gap-2\">\n                  <lucide-icon [img]=\"Target\" class=\"text-primary size-4 shrink-0\" />\n                  <div class=\"min-w-0 flex-1\">\n                    <span class=\"text-foreground block truncate font-medium\">{{ activeTask().title }}</span>\n                  </div>\n                </div>\n                <span ui-badge wrap variant=\"secondary\" class=\"shrink-0 gap-1 text-xs\">\n                  <span>🍅 {{ activeTask().currentPomodoros }}/{{ activeTask().targetPomodoros }}</span>\n                </span>\n              </div>\n            </div>\n\n            <div ui-card-footer class=\"flex flex-col gap-4 pt-2\">\n              <div class=\"flex items-center justify-center gap-4\">\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"icon\"\n                  class=\"text-muted-foreground hover:text-foreground size-11 rounded-full\"\n                  title=\"Reset current interval\"\n                  aria-label=\"Reset timer\"\n                  (click)=\"resetTimer()\"\n                >\n                  <lucide-icon [img]=\"RotateCcw\" class=\"size-4\" />\n                </button>\n\n                <button\n                  ui-button\n                  variant=\"default\"\n                  size=\"icon-lg\"\n                  class=\"size-14 rounded-full shadow-md transition-[color,background-color,border-color,box-shadow,opacity,transform,scale,translate,rotate] active:scale-95\"\n                  [attr.title]=\"isRunning() ? 'Pause Timer' : 'Start Focus Timer'\"\n                  [attr.aria-label]=\"isRunning() ? 'Pause Timer' : 'Start Focus Timer'\"\n                  (click)=\"toggleTimer()\"\n                >\n                  @if (isRunning()) {\n                    <lucide-icon [img]=\"Pause\" class=\"size-6 fill-current\" />\n                  } @else {\n                    <lucide-icon [img]=\"Play\" class=\"ml-0.5 size-6 fill-current\" />\n                  }\n                </button>\n\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"icon\"\n                  class=\"text-muted-foreground hover:text-foreground size-11 rounded-full\"\n                  title=\"Skip to next phase\"\n                  aria-label=\"Skip to next phase\"\n                  (click)=\"skipPhase()\"\n                >\n                  <lucide-icon [img]=\"SkipForward\" class=\"size-4\" />\n                </button>\n              </div>\n\n              <ui-separator />\n\n              <div class=\"text-muted-foreground flex w-full items-center justify-between text-xs\">\n                <span class=\"text-foreground font-medium\">Session {{ currentSession() }} of 4</span>\n\n                <div class=\"flex items-center gap-2\">\n                  @for (index of [1, 2, 3, 4]; track index) {\n                    <div [class]=\"sessionDotClass(index)\">\n                      @if (index < currentSession()) {\n                        <lucide-icon [img]=\"Check\" class=\"size-2.5 stroke-[3]\" />\n                      }\n                    </div>\n                  }\n                </div>\n\n                <span>Long break after 4</span>\n              </div>\n            </div>\n          </div>\n\n          <!-- Daily Focus Analytics Card -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center justify-between\">\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"BarChart3\" class=\"text-primary size-4\" />\n                  <h3 ui-card-title class=\"text-base font-semibold\">Daily Focus Analytics</h3>\n                </div>\n                <span ui-badge wrap variant=\"outline\" class=\"text-muted-foreground text-xs font-normal\"> Today </span>\n              </div>\n              <p ui-card-description class=\"text-xs\">Aggregated productivity stats and session completion rate</p>\n            </div>\n\n            <div ui-card-content class=\"space-y-4\">\n              <div class=\"grid grid-cols-3 gap-3\">\n                <div class=\"border-border/60 bg-muted/30 rounded-lg border p-3 text-center\">\n                  <div class=\"text-muted-foreground text-xs font-medium\">Total Focus</div>\n                  <div class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">3h 45m</div>\n                  <div class=\"text-success mt-0.5 text-xs\">+28m vs avg</div>\n                </div>\n\n                <div class=\"border-border/60 bg-muted/30 rounded-lg border p-3 text-center\">\n                  <div class=\"text-muted-foreground text-xs font-medium\">Completed</div>\n                  <div class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n                    {{ completedSessionsToday() }} <span class=\"text-muted-foreground text-xs font-normal\">/ 10</span>\n                  </div>\n                  <div class=\"text-muted-foreground mt-0.5 text-xs\">80% of goal</div>\n                </div>\n\n                <div class=\"border-border/60 bg-muted/30 rounded-lg border p-3 text-center\">\n                  <div class=\"text-muted-foreground text-xs font-medium\">Focus Score</div>\n                  <div class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">94%</div>\n                  <div class=\"text-primary mt-0.5 text-xs font-semibold\">High Flow</div>\n                </div>\n              </div>\n\n              <div class=\"space-y-1.5\">\n                <div class=\"flex items-center justify-between text-xs\">\n                  <span class=\"text-muted-foreground\">Daily Session Goal</span>\n                  <span class=\"text-foreground font-semibold tabular-nums\"\n                    >{{ completedSessionsToday() }}/10 (80%)</span\n                  >\n                </div>\n                <ui-progress [value]=\"80\" class=\"h-2\" />\n              </div>\n\n              <ui-separator />\n\n              <div class=\"space-y-2\">\n                <div class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                  Recent Completed Intervals\n                </div>\n                <div class=\"space-y-1.5 text-xs\">\n                  <div class=\"bg-muted/30 flex items-center justify-between rounded-md px-2.5 py-1.5\">\n                    <div class=\"flex items-center gap-2 truncate\">\n                      <span class=\"text-muted-foreground font-mono tabular-nums\">10:45 AM</span>\n                      <span class=\"text-foreground truncate font-medium\">Refactor OKLCH color-mix</span>\n                    </div>\n                    <span ui-badge wrap variant=\"success\" class=\"h-5 px-1.5 text-xs\">25m Focus</span>\n                  </div>\n                  <div class=\"bg-muted/30 flex items-center justify-between rounded-md px-2.5 py-1.5\">\n                    <div class=\"flex items-center gap-2 truncate\">\n                      <span class=\"text-muted-foreground font-mono tabular-nums\">10:15 AM</span>\n                      <span class=\"text-muted-foreground truncate\">Short Break · Lofi Beats</span>\n                    </div>\n                    <span ui-badge wrap variant=\"secondary\" class=\"h-5 px-1.5 text-xs\">5m Rest</span>\n                  </div>\n                  <div class=\"bg-muted/30 flex items-center justify-between rounded-md px-2.5 py-1.5\">\n                    <div class=\"flex items-center gap-2 truncate\">\n                      <span class=\"text-muted-foreground font-mono tabular-nums\">09:30 AM</span>\n                      <span class=\"text-foreground truncate font-medium\">Audit ARIA roles</span>\n                    </div>\n                    <span ui-badge wrap variant=\"success\" class=\"h-5 px-1.5 text-xs\">25m Focus</span>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Right Column: Active Task Queue -->\n        <div class=\"space-y-6 lg:col-span-6\">\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center justify-between\">\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"ListTodo\" class=\"text-primary size-4\" />\n                  <h3 ui-card-title class=\"text-base font-semibold\">Active Focus Task Queue</h3>\n                </div>\n                <span ui-badge wrap variant=\"secondary\" class=\"text-xs\">\n                  {{ completedTasksCount() }}/{{ tasks().length }} Completed\n                </span>\n              </div>\n              <p ui-card-description class=\"text-xs\">\n                Select an item to link it to the active timer countdown. Check off tasks as you finish.\n              </p>\n            </div>\n\n            <div ui-card-content class=\"space-y-4\">\n              <form class=\"flex items-center gap-2\" (submit)=\"addTaskFromForm($event)\">\n                <div class=\"flex-1\">\n                  <ui-input\n                    [value]=\"newTaskTitle()\"\n                    (valueChange)=\"newTaskTitle.set($event)\"\n                    placeholder=\"Add a new focus task...\"\n                    size=\"middle\"\n                    class=\"text-xs\"\n                  />\n                </div>\n                <div class=\"w-20\">\n                  <ui-input\n                    [value]=\"newTaskEstimateText()\"\n                    (valueChange)=\"newTaskEstimate.set(Number($event) || 2)\"\n                    type=\"number\"\n                    class=\"text-center text-xs tabular-nums\"\n                    title=\"Estimated Pomodoros\"\n                  />\n                </div>\n                <button ui-button type=\"submit\" size=\"default\" class=\"gap-1 text-xs\">\n                  <lucide-icon [img]=\"Plus\" class=\"size-3.5\" />\n                  <span>Add</span>\n                </button>\n              </form>\n\n              <ui-separator />\n\n              <div class=\"space-y-2.5\">\n                @for (task of tasks(); track task.id) {\n                  <div [class]=\"taskRowClass(task)\">\n                    <div class=\"flex min-w-0 flex-1 items-start gap-3\">\n                      <div class=\"pt-0.5\">\n                        <ui-checkbox [checked]=\"task.done\" (checkedChange)=\"toggleTaskDone(task.id)\" />\n                      </div>\n\n                      <button\n                        type=\"button\"\n                        class=\"focus-visible:ring-ring min-w-0 flex-1 cursor-pointer rounded-md text-left focus-visible:ring-2 focus-visible:outline-none\"\n                        [attr.aria-pressed]=\"task.id === activeTaskId()\"\n                        (click)=\"selectActiveTask(task.id)\"\n                      >\n                        <div class=\"flex items-center gap-2\">\n                          <p [class]=\"taskTitleClass(task)\">\n                            {{ task.title }}\n                          </p>\n                          @if (task.id === activeTaskId() && !task.done) {\n                            <span ui-badge wrap variant=\"default\" class=\"h-4.5 px-1.5 text-xs\"> Active </span>\n                          }\n                        </div>\n\n                        <div class=\"text-muted-foreground mt-1 flex items-center gap-2 text-xs\">\n                          <span ui-badge wrap variant=\"outline\" class=\"h-4 px-1 text-xs font-normal\">\n                            {{ task.tag }}\n                          </span>\n                          <span>Estimated: {{ task.targetPomodoros * 25 }}m</span>\n                        </div>\n                      </button>\n                    </div>\n\n                    <div class=\"flex items-center gap-2\">\n                      <span\n                        ui-badge\n                        [variant]=\"task.done ? 'secondary' : task.id === activeTaskId() ? 'default' : 'outline'\"\n                        class=\"shrink-0 gap-1 text-xs font-medium whitespace-normal tabular-nums\"\n                      >\n                        <span>🍅 {{ task.currentPomodoros }}/{{ task.targetPomodoros }}</span>\n                      </span>\n\n                      <button\n                        ui-button\n                        variant=\"ghost\"\n                        size=\"icon-sm\"\n                        class=\"text-muted-foreground hover:text-destructive size-7 opacity-0 group-hover:opacity-100\"\n                        title=\"Remove task\"\n                        (click)=\"removeTask(task.id)\"\n                      >\n                        <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" />\n                      </button>\n                    </div>\n                  </div>\n                }\n              </div>\n            </div>\n\n            <div\n              ui-card-footer\n              class=\"border-border/50 bg-muted/20 text-muted-foreground flex items-center justify-between border-t py-3 text-xs\"\n            >\n              <span>Tip: Switch tasks any time without resetting timer elapsed time.</span>\n              <button\n                ui-button\n                variant=\"ghost\"\n                size=\"sm\"\n                class=\"text-muted-foreground hover:text-foreground h-7 text-xs\"\n                (click)=\"clearDoneTasks()\"\n              >\n                Clear Done ({{ completedTasksCount() }})\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiPomodoroFocusTimerComponent {\n  protected readonly BarChart3 = BarChart3\n  protected readonly Check = Check\n  protected readonly Flame = Flame\n  protected readonly Headphones = Headphones\n  protected readonly ListTodo = ListTodo\n  protected readonly Music2 = Music2\n  protected readonly Pause = Pause\n  protected readonly Play = Play\n  protected readonly Plus = Plus\n  protected readonly RotateCcw = RotateCcw\n  protected readonly SkipForward = SkipForward\n  protected readonly Target = Target\n  protected readonly Trash2 = Trash2\n  protected readonly Trees = Trees\n  protected readonly Volume2 = Volume2\n  protected readonly VolumeX = VolumeX\n  protected readonly Waves = Waves\n\n  protected readonly ambientSounds = ambientSounds\n  protected readonly radius = RADIUS\n  protected readonly circumference = CIRCUMFERENCE\n\n  @Input('class') className?: string\n  @Input() set initialMode(v: TimerMode | undefined) {\n    if (v) {\n      this.currentMode.set(v)\n      this.remainingSeconds.set(MODE_DURATIONS[v])\n    }\n  }\n  @Input() set initialTime(v: number | undefined) {\n    if (v !== undefined) this.remainingSeconds.set(v)\n  }\n  @Input() set initialSound(v: string | undefined) {\n    if (v) this.selectedSound.set(v)\n  }\n  @Input() set initialSession(v: number | undefined) {\n    if (v !== undefined) this.currentSession.set(v)\n  }\n\n  readonly currentMode = signal<TimerMode>('focus')\n  readonly remainingSeconds = signal(1485)\n  readonly isRunning = signal(false)\n  readonly currentSession = signal(3)\n  readonly completedSessionsToday = signal(8)\n  readonly selectedSound = signal('lofi')\n  readonly isSoundMuted = signal(false)\n  readonly newTaskTitle = signal('')\n  readonly newTaskEstimate = signal(2)\n  readonly tasks = signal<FocusTask[]>(defaultTasks.map((t) => ({ ...t })))\n  readonly activeTaskId = signal('task-1')\n\n  private timerInterval: ReturnType<typeof setInterval> | null = null\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => {\n      if (this.timerInterval) clearInterval(this.timerInterval)\n    })\n  }\n\n  readonly activeTask = computed<FocusTask>(() => {\n    const list = this.tasks()\n    return (\n      list.find((t) => t.id === this.activeTaskId()) ??\n      list.find((t) => !t.done) ?? {\n        id: 'default',\n        title: 'General Deep Work Session',\n        tag: 'Productivity',\n        done: false,\n        currentPomodoros: 0,\n        targetPomodoros: 1,\n      }\n    )\n  })\n\n  readonly completedTasksCount = computed(() => this.tasks().filter((t) => t.done).length)\n  readonly totalModeSeconds = computed(() => MODE_DURATIONS[this.currentMode()])\n  readonly progressPercentage = computed(() => {\n    const elapsed = this.totalModeSeconds() - this.remainingSeconds()\n    return Math.min(100, Math.max(0, (elapsed / this.totalModeSeconds()) * 100))\n  })\n  readonly strokeDashoffset = computed(() => CIRCUMFERENCE * (1 - this.progressPercentage() / 100))\n  readonly newTaskEstimateText = computed(() => String(this.newTaskEstimate()))\n\n  get rootClass(): string {\n    return cn('mx-auto w-full max-w-5xl space-y-6', this.className)\n  }\n\n  get progressRingClass(): string {\n    return cn(\n      'transition-colors duration-700 ease-out motion-reduce:transition-none',\n      this.currentMode() === 'focus' && 'text-primary',\n      this.currentMode() === 'shortBreak' && 'text-success',\n      this.currentMode() === 'longBreak' && 'text-info',\n    )\n  }\n\n  get statusDotClass(): string {\n    return cn(\n      'size-2 rounded-full',\n      this.isRunning() ? 'animate-ping' : 'opacity-60',\n      this.currentMode() === 'focus' && 'bg-primary',\n      this.currentMode() === 'shortBreak' && 'bg-success',\n      this.currentMode() === 'longBreak' && 'bg-info',\n    )\n  }\n\n  get modeLabelClass(): string {\n    return cn(\n      'text-xs font-semibold tracking-wider uppercase',\n      this.currentMode() === 'focus' && 'text-primary',\n      this.currentMode() === 'shortBreak' && 'text-success',\n      this.currentMode() === 'longBreak' && 'text-info',\n    )\n  }\n\n  sessionDotClass(index: number): string {\n    return cn(\n      'flex size-4 items-center justify-center rounded-full transition-colors',\n      index < this.currentSession()\n        ? 'bg-primary text-primary-foreground'\n        : index === this.currentSession()\n          ? 'ring-primary ring-offset-background bg-primary/80 ring-2 ring-offset-2'\n          : 'border-muted-foreground/30 bg-muted/30 border-2',\n    )\n  }\n\n  taskRowClass(task: FocusTask): string {\n    return cn(\n      'group flex items-center justify-between gap-3 rounded-lg border p-3 transition-colors',\n      task.id === this.activeTaskId() && !task.done\n        ? 'border-primary/50 bg-primary/5 shadow-xs'\n        : 'border-border/70 bg-card hover:border-border hover:bg-muted/30',\n      task.done && 'bg-muted/20 opacity-65',\n    )\n  }\n\n  taskTitleClass(task: FocusTask): string {\n    return cn(\n      'text-foreground text-xs font-medium transition-colors',\n      task.done && 'text-muted-foreground line-through',\n    )\n  }\n\n  soundIcon(id: string): typeof VolumeX {\n    if (id === 'off') return VolumeX\n    if (id === 'whitenoise') return Waves\n    if (id === 'rainforest') return Trees\n    return Music2\n  }\n\n  formatTime(seconds: number): string {\n    return formatTimerTime(seconds)\n  }\n\n  selectAmbientSound(id: string): void {\n    this.selectedSound.set(id)\n    this.isSoundMuted.set(id === 'off')\n  }\n\n  startTimer(): void {\n    if (this.isRunning()) return\n    this.isRunning.set(true)\n    this.timerInterval = setInterval(() => {\n      if (this.remainingSeconds() > 0) {\n        this.remainingSeconds.update((s) => s - 1)\n      } else {\n        this.skipPhase()\n      }\n    }, 1000)\n  }\n\n  pauseTimer(): void {\n    this.isRunning.set(false)\n    if (this.timerInterval) {\n      clearInterval(this.timerInterval)\n      this.timerInterval = null\n    }\n  }\n\n  toggleTimer(): void {\n    if (this.isRunning()) this.pauseTimer()\n    else this.startTimer()\n  }\n\n  resetTimer(): void {\n    this.pauseTimer()\n    this.remainingSeconds.set(MODE_DURATIONS[this.currentMode()])\n  }\n\n  setMode(mode: TimerMode): void {\n    this.pauseTimer()\n    this.currentMode.set(mode)\n    this.remainingSeconds.set(MODE_DURATIONS[mode])\n  }\n\n  skipPhase(): void {\n    this.pauseTimer()\n    if (this.currentMode() === 'focus') {\n      this.completedSessionsToday.update((c) => c + 1)\n      const task = this.tasks().find((t) => t.id === this.activeTaskId())\n      if (task && task.currentPomodoros < task.targetPomodoros) {\n        this.tasks.update((prev) =>\n          prev.map((t) => (t.id === task.id ? { ...t, currentPomodoros: t.currentPomodoros + 1 } : t)),\n        )\n      }\n      if (this.currentSession() >= 4) {\n        this.currentMode.set('longBreak')\n        this.currentSession.set(1)\n      } else {\n        this.currentMode.set('shortBreak')\n        this.currentSession.update((s) => s + 1)\n      }\n    } else {\n      this.currentMode.set('focus')\n    }\n    this.remainingSeconds.set(MODE_DURATIONS[this.currentMode()])\n  }\n\n  toggleTaskDone(taskId: string): void {\n    this.tasks.update((prev) =>\n      prev.map((t) => {\n        if (t.id !== taskId) return t\n        const done = !t.done\n        return {\n          ...t,\n          done,\n          currentPomodoros: done ? Math.max(t.currentPomodoros, t.targetPomodoros) : t.currentPomodoros,\n        }\n      }),\n    )\n  }\n\n  selectActiveTask(taskId: string): void {\n    this.activeTaskId.set(taskId)\n  }\n\n  removeTask(taskId: string): void {\n    this.tasks.update((prev) => prev.filter((t) => t.id !== taskId))\n    if (this.activeTaskId() === taskId) {\n      const rest = this.tasks()\n      if (rest.length > 0) this.activeTaskId.set(rest[0].id)\n    }\n  }\n\n  addTask(): void {\n    const trimmed = this.newTaskTitle().trim()\n    if (!trimmed) return\n    const id = `task-${Date.now()}`\n    this.tasks.update((prev) => [\n      ...prev,\n      {\n        id,\n        title: trimmed,\n        tag: 'Focus',\n        done: false,\n        currentPomodoros: 0,\n        targetPomodoros: Math.max(1, this.newTaskEstimate() || 2),\n      },\n    ])\n    this.newTaskTitle.set('')\n    this.newTaskEstimate.set(2)\n    if (this.tasks().length === 1) this.activeTaskId.set(id)\n  }\n\n  addTaskFromForm(event: Event): void {\n    event.preventDefault()\n    this.addTask()\n  }\n\n  clearDoneTasks(): void {\n    this.tasks.update((prev) => prev.filter((t) => !t.done))\n  }\n\n  toggleSoundMute(): void {\n    this.isSoundMuted.update((m) => !m)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/pomodoro-focus-timer/pomodoro-focus-timer.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/pomodoro-focus-timer/index.ts",
      "content": "export {\n  UiPomodoroFocusTimerComponent,\n  MODE_DURATIONS,\n  formatTimerTime,\n  type FocusTask,\n  type TimerMode,\n} from './pomodoro-focus-timer.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/pomodoro-focus-timer/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/checkbox.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Pomodoro productivity workspace with focus and break interval timers, SVG circular progress countdown display, active task queue with pomodoro estimate badges, ambient sound generator switcher, and daily focus analytics metrics.",
  "categories": [
    "productivity",
    "dashboard",
    "app"
  ]
}