Install any skill in seconds. Free to start, no credit card required.
Get Started Free →@delon/util skill - Utility functions library for array, string, date, number manipulation. For ng-events construction site progress tracking system.
.claude/skills/aiskillstore-delon-util/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 123% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 60% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 155% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 75% | 0% |
Trigger patterns: "utility", "helper", "@delon/util", "format", "deepCopy", "deepMerge"
@delon/util provides a comprehensive collection of utility functions for common data manipulation tasks in ng-alain applications.
Package: @delon/util@20.1.0
array/)typescriptimport { deepCopy } from '@delon/util/array'; const original = { name: '任務', items: [1, 2, 3], meta: { id: 1 } }; const copy = deepCopy(original); // Changes to copy won't affect original copy.items.push(4); console.log(original.items); // [1, 2, 3] console.log(copy.items); // [1, 2, 3, 4]
Use Cases:
typescriptimport { deepMerge } from '@delon/util/array'; const defaults = { config: { theme: 'light', size: 'default' }, features: ['dashboard'] }; const custom = { config: { theme: 'dark' }, features: ['reports'] }; const merged = deepMerge(defaults, custom); // Result: { // config: { theme: 'dark', size: 'default' }, // features: ['dashboard', 'reports'] // }
typescriptimport { groupBy, // Group array by property uniq, // Remove duplicates uniqBy, // Remove duplicates by property orderBy // Sort array by properties } from '@delon/util/array'; // Group tasks by status const grouped = groupBy(tasks, 'status'); // { pending: [...], completed: [...] } // Remove duplicate IDs const uniqueIds = uniq([1, 2, 2, 3]); // [1, 2, 3] // Remove duplicate tasks by ID const uniqueTasks = uniqBy(tasks, 'id'); // Sort tasks const sorted = orderBy(tasks, ['priority', 'createdAt'], ['asc', 'desc']);
string/)typescriptimport { format } from '@delon/util/string'; // Template interpolation const message = format('任務 {0} 已指派給 {1}', taskName, userName); // Named parameters const message2 = format('任務 {name} 的狀態為 {status}', { name: '地基施工', status: '進行中' });
typescriptimport { toCamelCase, // Convert to camelCase toPascalCase, // Convert to PascalCase toKebabCase, // Convert to kebab-case toSnakeCase, // Convert to snake_case truncate // Truncate with ellipsis } from '@delon/util/string'; toCamelCase('task-name'); // 'taskName' toPascalCase('task-name'); // 'TaskName' toKebabCase('TaskName'); // 'task-name' toSnakeCase('TaskName'); // 'task_name' truncate('Long text...', 10); // 'Long te...'
date/)typescriptimport { getTimeDistance } from '@delon/util/date'; // Get today's date range const today = getTimeDistance('today'); // [Date(2024-12-25 00:00:00), Date(2024-12-25 23:59:59)] // Get this week's date range const week = getTimeDistance('week'); // Get this month's date range const month = getTimeDistance('month'); // Get this year's date range const year = getTimeDistance('year'); // Custom range with offset const lastWeek = getTimeDistance('week', -1);
Supported Types:
'today' - Current day'week' - Current week (Sunday to Saturday)'month' - Current month'year' - Current yeartypescriptimport { formatDistanceToNow } from '@delon/util/date'; const createdAt = new Date('2024-12-20'); const relative = formatDistanceToNow(createdAt); // "5 days ago" const futureDate = new Date('2024-12-30'); const future = formatDistanceToNow(futureDate); // "in 5 days"
number/)typescriptimport { currency } from '@delon/util/number'; // Format as currency currency(1234567.89); // "$1,234,567.89" currency(1234567.89, { unit: '¥' }); // "¥1,234,567.89" currency(1234.5, { precision: 0 }); // "$1,235"
typescriptimport { toFixed, // Round to fixed decimals toPercent, // Convert to percentage toThousands // Add thousands separators } from '@delon/util/number'; toFixed(1.2345, 2); // "1.23" toPercent(0.1234); // "12.34%" toPercent(0.1234, 1); // "12.3%" toThousands(1234567); // "1,234,567"
browser/)typescriptimport { copy } from '@delon/util/browser'; async copyTaskLink(taskId: string) { const link = `${window.location.origin}/tasks/${taskId}`; const success = await copy(link); if (success) { this.messageService.success('連結已複製'); } else { this.messageService.error('複製失敗'); } }
typescriptimport { scrollToTop, // Smooth scroll to top deepGet, // Get nested object property deepSet, // Set nested object property isEmpty, // Check if value is empty isEqual, // Deep equality check updateHostClass // Update host element classes } from '@delon/util/browser'; scrollToTop(); scrollToTop({ duration: 500 }); const value = deepGet(obj, 'user.profile.name'); deepSet(obj, 'user.profile.name', 'New Name'); isEmpty(null); // true isEmpty(''); // true isEmpty([]); // true isEmpty({}); // true isEqual({ a: 1 }, { a: 1 }); // true
typescriptimport { Component, signal, computed, inject } from '@angular/core'; import { deepCopy, groupBy, orderBy } from '@delon/util/array'; import { format } from '@delon/util/string'; import { getTimeDistance } from '@delon/util/date'; import { copy } from '@delon/util/browser'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-task-list', standalone: true, template: ` <nz-card> <div nz-row [nzGutter]="16"> @for (group of groupedTasks() | keyvalue; track group.key) { <div nz-col [nzSpan]="8"> <h3>{{ group.key }} ({{ group.value.length }})</h3> @for (task of group.value; track task.id) { <nz-card> <h4>{{ task.title }}</h4> <p>{{ formatTaskInfo(task) }}</p> <button nz-button (click)="copyTaskLink(task.id)"> 複製連結 </button> </nz-card> } </div> } </div> </nz-card> ` }) export class TaskListComponent { private messageService = inject(NzMessageService); // Original tasks from service tasks = signal<Task[]>([]); // Group tasks by status using @delon/util groupedTasks = computed(() => groupBy(this.sortedTasks(), 'status') ); // Sort tasks by priority and date sortedTasks = computed(() => orderBy( this.tasks(), ['priority', 'createdAt'], ['asc', 'desc'] ) ); // Format task information formatTaskInfo(task: Task): string { return format( '優先級: {priority}, 建立於 {date}', { priority: task.priority, date: this.formatDate(task.createdAt) } ); } // Copy task link to clipboard async copyTaskLink(taskId: string): Promise<void> { const link = `${window.location.origin}/tasks/${taskId}`; const success = await copy(link); if (success) { this.messageService.success('任務連結已複製'); } else { this.messageService.error('複製失敗,請手動複製'); } } // Clone task for editing cloneTaskForEdit(task: Task): Task { return deepCopy(task); } // Get this week's tasks getThisWeekTasks(): Task[] { const [start, end] = getTimeDistance('week'); return this.tasks().filter(t => t.createdAt >= start && t.createdAt <= end ); } private formatDate(date: Date): string { return format( '{year}-{month}-{day}', { year: date.getFullYear(), month: String(date.getMonth() + 1).padStart(2, '0'), day: String(date.getDate()).padStart(2, '0') } ); } }
typescriptimport { Component, signal } from '@angular/core'; import { deepCopy, deepMerge } from '@delon/util/array'; import { isEmpty } from '@delon/util/browser'; @Component({ selector: 'app-task-form', standalone: true, template: ` <form nz-form (ngSubmit)="handleSubmit()"> <!-- form fields --> <button nz-button [disabled]="hasEmptyRequired()"> 提交 </button> </form> ` }) export class TaskFormComponent { // Default form values private defaults = { priority: 'medium', status: 'pending', assignee: null, tags: [] }; // Form data with defaults formData = signal(deepCopy(this.defaults)); // Original task (for editing) originalTask = signal<Task | null>(null); // Load task for editing loadTask(task: Task): void { // Merge task data with defaults const merged = deepMerge(this.defaults, task); this.formData.set(merged); this.originalTask.set(deepCopy(task)); } // Check if required fields are empty hasEmptyRequired(): boolean { const data = this.formData(); return isEmpty(data.title) || isEmpty(data.assignee); } // Check if form has changes hasChanges(): boolean { const original = this.originalTask(); if (!original) return true; return !isEqual(original, this.formData()); } handleSubmit(): void { if (!this.hasEmptyRequired()) { // Create clean copy for submission const submitData = deepCopy(this.formData()); // Submit... } } }
✅ DO:
typescriptconst taskCopy = deepCopy(task); taskCopy.status = 'completed'; this.tasks.update(tasks => [...tasks, taskCopy]);
❌ DON'T:
typescripttask.status = 'completed'; this.tasks.update(tasks => [...tasks, task]); // Mutates original
✅ DO:
typescriptgroupedTasks = computed(() => groupBy(this.tasks(), 'status')); sortedTasks = computed(() => orderBy(this.tasks(), ['priority'], ['asc']));
✅ DO:
typescriptimport { deepCopy } from '@delon/util/array'; const copy: Task = deepCopy<Task>(originalTask);
Version: 1.0 Created: 2025-12-25 Maintainer: ng-events(GigHub) Development Team
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 13,148 | 9,383 | -29% | 1 | 1 | 0% | 2,584 | 5,218 | +102% | 0 | 0 | — |
case-02 | fail→pass | 9,901 | 6,726 | -32% | 1 | 1 | 0% | 2,141 | 4,775 | +123% | 0 | 0 | — |
case-03 | fail→pass | 18,451 | 11,519 | -38% | 1 | 1 | 0% | 3,544 | 5,664 | +60% | 0 | 0 | — |
case-04 | fail→pass | 9,805 | 7,850 | -20% | 1 | 1 | 0% | 1,967 | 5,013 | +155% | 0 | 0 | — |
case-05 | fail→pass | 18,378 | 12,701 | -31% | 1 | 1 | 0% | 3,378 | 5,907 | +75% | 0 | 0 | — |
case-06 | fail→pass | 15,297 | 10,525 | -31% | 1 | 1 | 0% | 2,942 | 5,548 | +89% | 0 | 0 | — |
case-07 | fail→pass | 21,085 | 8,997 | -57% | 1 | 1 | 0% | 3,761 | 5,237 | +39% | 0 | 0 | — |
case-08 | fail→pass | 38,778 | 7,623 | -80% | 1 | 1 | 0% | 3,383 | 4,941 | +46% | 0 | 0 | — |
case-09 | fail→pass | 10,973 | 9,311 | -15% | 1 | 1 | 0% | 2,041 | 5,163 | +153% | 0 | 0 | — |
case-10 | fail→pass | 16,530 | 21,209 | +28% | 1 | 1 | 0% | 3,255 | 6,088 | +87% | 0 | 0 | — |
case-11 | fail→pass | 24,022 | 7,189 | -70% | 1 | 1 | 0% | 4,367 | 4,773 | +9% | 0 | 0 | — |
case-12 | fail→pass | 9,044 | 6,797 | -25% | 1 | 1 | 0% | 1,754 | 4,648 | +165% | 0 | 0 | — |
case-13 | fail→pass | 11,962 | 9,117 | -24% | 1 | 1 | 0% | 2,243 | 5,044 | +125% | 0 | 0 | — |
case-14 | pass→pass | 10,451 | 7,887 | -25% | 1 | 1 | 0% | 1,726 | 4,868 | +182% | 0 | 0 | — |
case-19 | fail→pass | 11,172 | 19,476 | +74% | 1 | 1 | 0% | 2,112 | 5,488 | +160% | 0 | 0 | — |
case-15 | fail→pass | 9,579 | 9,095 | -5% | 1 | 1 | 0% | 1,713 | 5,287 | +209% | 0 | 0 | — |
case-16 | fail→pass | 12,188 | 9,209 | -24% | 1 | 1 | 0% | 2,234 | 5,155 | +131% | 0 | 0 | — |
case-17 | pass→pass | 8,939 | 5,383 | -40% | 1 | 1 | 0% | 1,721 | 4,509 | +162% | 0 | 0 | — |
case-18 | pass→pass | 14,684 | 13,147 | -10% | 1 | 1 | 0% | 2,537 | 5,818 | +129% | 0 | 0 | — |
case-20 | pass→pass | 5,658 | 5,031 | -11% | 1 | 1 | 0% | 1,082 | 4,332 | +300% | 0 | 0 | — |
case-21 | pass→pass | 6,619 | 8,647 | +31% | 1 | 1 | 0% | 1,259 | 5,201 | +313% | 0 | 0 | — |
case-22 | pass→pass | 12,873 | 11,651 | -9% | 1 | 1 | 0% | 2,309 | 5,504 | +138% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +73 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.