Features — Visual Changelog Diff
blockfeatureVisual semantic git changelog with unified code diff highlighting and commit verification.
Installation
$pnpm dlx uipkge-ng@latest add feature-visual-changelog-diff$npx uipkge-ng@latest add feature-visual-changelog-diff$yarn dlx uipkge-ng@latest add feature-visual-changelog-diff$bunx uipkge-ng@latest add feature-visual-changelog-diffManifest:
https://uipkge.dev/r/angular/feature-visual-changelog-diff.jsonInstalls to:src/app/components/blocks/feature-visual-changelog-diff/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
ChangelogDiffLineinterface ChangelogDiffLine {
type: 'add' | 'delete' | 'context'
content: string
}ChangelogReleaseinterface ChangelogRelease {
version: string
date: string
title: string
description: string
commitSha: string
diffLines: ChangelogDiffLine[]
}npm dependencies
Files installed (2)
src/app/components/blocks/feature-visual-changelog-diff/feature-visual-changelog-diff.component.ts8.7 kB
// Visual semantic git changelog with unified code diff highlighting and commit hash // copy verification. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core' import { ArrowRight, Check, Copy, GitCommit, GitPullRequest, LucideAngularModule } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' export interface ChangelogDiffLine { type: 'add' | 'delete' | 'context' content: string } export interface ChangelogRelease { version: string date: string title: string description: string commitSha: string diffLines: ChangelogDiffLine[] } const DEFAULT_RELEASES: ChangelogRelease[] = [ { version: 'v2.4.0', date: 'August 2026', title: 'Tailwind CSS v4 & OKLCH Semantic Color Spaces', description: 'Upgraded all cross-framework tokens to direct @theme inline CSS bindings. Eliminates legacy postcss configs.', commitSha: '7f9a2b1', diffLines: [ { type: 'delete', content: '- module.exports = { theme: { extend: { colors: { ... } } } }' }, { type: 'add', content: '+ @theme inline {' }, { type: 'add', content: '+ --color-background: oklch(1 0 0);' }, { type: 'add', content: '+ --color-primary: oklch(0.205 0 0);' }, { type: 'add', content: '+ }' }, { type: 'context', content: ' // Zero JavaScript compilation overhead in production bundles' }, ], }, { version: 'v2.3.0', date: 'July 2026', title: 'Headless Polymorphic Primitive Architecture', description: 'Unified Reka UI and Radix UI state machines across Vue 3.5 and React 19 mirrors.', commitSha: '4c8e1d9', diffLines: [ { type: 'delete', content: '- import { Dialog } from "legacy-untyped-modal"' }, { type: 'add', content: '+ import { DialogRoot, DialogPortal } from "reka-ui"' }, { type: 'add', content: '+ <Primitive data-slot="dialog" :as="as">' }, { type: 'context', content: ' // Full WCAG AA focus trapping and keyboard navigation' }, ], }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-feature-visual-changelog-diff, [ui-feature-visual-changelog-diff]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiCardComponent, UiCardContentComponent], template: ` <section data-slot="feature-visual-changelog-diff" [class]="rootClass"> <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <!-- Section Header --> <div class="mx-auto max-w-3xl space-y-4 text-center"> <a href="#changelog" class="group border-border/80 bg-secondary/60 hover:bg-secondary text-foreground inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs font-medium shadow-2xs transition-colors" > <lucide-icon [img]="GitPullRequest" class="text-primary size-3.5" /> <span>Continuous Release Ledger</span> <lucide-icon [img]="ArrowRight" class="text-muted-foreground size-3 transition-transform group-hover:translate-x-0.5" /> </a> <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl"> {{ titleText() }} </h2> <p class="text-muted-foreground text-base sm:text-lg"> {{ descriptionText() }} </p> </div> <!-- Changelog Timeline Entries --> <div class="mx-auto mt-12 max-w-4xl space-y-8"> @for (rel of activeReleases(); track rel.version) { <div ui-card class="border-border bg-card/80 overflow-hidden shadow-xs backdrop-blur-xs"> <div ui-card-content class="space-y-5 p-6"> <!-- Header Bar --> <div class="border-border flex flex-wrap items-center justify-between gap-2 border-b pb-3"> <div class="flex items-center gap-2.5"> <span ui-badge variant="outline" class="border-primary/40 bg-primary/10 text-primary font-mono text-xs font-bold" > {{ rel.version }} </span> <span class="text-foreground text-sm font-bold">{{ rel.title }}</span> </div> <div class="flex items-center gap-2"> <span class="text-muted-foreground font-mono text-xs">{{ rel.date }}</span> <button type="button" class="border-border bg-muted/30 text-muted-foreground hover:text-foreground inline-flex items-center gap-1 rounded border px-2 py-0.5 font-mono text-xs transition-colors" (click)="copySha(rel.commitSha)" > <lucide-icon [img]="GitCommit" class="text-primary size-3" /> <span>{{ rel.commitSha }}</span> @if (copiedSha() === rel.commitSha) { <lucide-icon [img]="Check" class="text-success ml-0.5 size-3" /> } @else { <lucide-icon [img]="Copy" class="ml-0.5 size-3" /> } </button> </div> </div> <p class="text-muted-foreground text-xs leading-relaxed"> {{ rel.description }} </p> <!-- Code Diff Box --> <div class="border-border bg-muted/40 overflow-hidden rounded-lg border font-mono text-xs"> <div class="border-border/80 bg-muted/60 text-muted-foreground flex items-center justify-between border-b px-3 py-1.5 text-xs font-bold" > <span>Unified Source Diff</span> <span class="text-success text-xs font-normal">Git verified</span> </div> <div class="space-y-1 overflow-x-auto p-3 text-xs"> @for (line of rel.diffLines; track line.content) { <div [class]="diffLineClass(line.type)"> {{ line.content }} </div> } </div> </div> </div> </div> } </div> </div> </section> `, }) export class UiFeatureVisualChangelogDiffComponent { protected readonly ArrowRight = ArrowRight protected readonly Check = Check protected readonly Copy = Copy protected readonly GitCommit = GitCommit protected readonly GitPullRequest = GitPullRequest @Input() set title(v: string) { this.titleText.set(v ?? 'Continuous evolution with transparent source-level changelogs.') } @Input() set description(v: string) { this.descriptionText.set( v ?? 'Every architectural improvement, performance optimization, and token adjustment documented with precise git diffs.', ) } @Input() set releases(v: ChangelogRelease[] | undefined) { this.releasesInput.set(v) } @Input('class') className?: string readonly titleText = signal('Continuous evolution with transparent source-level changelogs.') readonly descriptionText = signal( 'Every architectural improvement, performance optimization, and token adjustment documented with precise git diffs.', ) private readonly releasesInput = signal<ChangelogRelease[] | undefined>(undefined) readonly copiedSha = signal<string | null>(null) private copyTimer: ReturnType<typeof setTimeout> | undefined constructor() { inject(DestroyRef).onDestroy(() => clearTimeout(this.copyTimer)) } readonly activeReleases = computed(() => this.releasesInput() ?? DEFAULT_RELEASES) get rootClass(): string { return cn('bg-background relative overflow-hidden py-16 sm:py-24', this.className) } diffLineClass(type: ChangelogDiffLine['type']): string { return cn( 'rounded px-2 py-0.5 leading-relaxed', type === 'add' ? 'bg-success/10 text-success font-medium' : type === 'delete' ? 'bg-destructive/10 text-destructive text-destructive line-through opacity-80' : 'text-muted-foreground', ) } async copySha(sha: string): Promise<void> { try { await navigator.clipboard.writeText(sha) this.copiedSha.set(sha) clearTimeout(this.copyTimer) this.copyTimer = setTimeout(() => this.copiedSha.set(null), 2000) } catch { // Clipboard unavailable (e.g. insecure context): leave the icon unchanged. } } }src/app/components/blocks/feature-visual-changelog-diff/index.ts0.1 kB
export { UiFeatureVisualChangelogDiffComponent, type ChangelogDiffLine, type ChangelogRelease, } from './feature-visual-changelog-diff.component'
Raw manifest:https://uipkge.dev/r/angular/feature-visual-changelog-diff.json