[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EJ44XEPYYJEBW46SWD5MXY":3,"skill-same:01M6EJ44XEPYYJEBW46SWD5MXY":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":34,"linkOnly":35,"markdownContent":36,"markdownBody":37,"frontMatter":38,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":42,"repositoryHtmlUrl":43,"repositoryHomepageUrl":44,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":46,"updatedAt":47,"install":48,"resources":61},"01M6EJ44XEPYYJEBW46SWD5MXY","delon-util",null,"@delon\u002Futil skill - Utility functions library for array, string, date, number manipulation. For ng-events construction site progress tracking system.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002F7spade\u002Fdelon-util\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33],"delon util","library","system","site","tracking","functions","progress","STRING","events","utility","2026-11-10 10:12:30",false,"---\nname: delon-util\ndescription: '@delon\u002Futil skill - Utility functions library for array, string, date, number manipulation. For ng-events construction site progress tracking system.'\n---\n\n# @delon\u002Futil - Utility Functions Library\n\nTrigger patterns: \"utility\", \"helper\", \"@delon\u002Futil\", \"format\", \"deepCopy\", \"deepMerge\"\n\n## Overview\n\n@delon\u002Futil provides a comprehensive collection of utility functions for common data manipulation tasks in ng-alain applications.\n\n**Package**: @delon\u002Futil@20.1.0\n\n## Categories\n\n### 1. Array Utilities (`array\u002F`)\n\n#### deepCopy - Deep Copy Arrays\u002FObjects\n\n```typescript\nimport { deepCopy } from '@delon\u002Futil\u002Farray';\n\nconst original = { name: '任務', items: [1, 2, 3], meta: { id: 1 } };\nconst copy = deepCopy(original);\n\n\u002F\u002F Changes to copy won't affect original\ncopy.items.push(4);\nconsole.log(original.items); \u002F\u002F [1, 2, 3]\nconsole.log(copy.items);     \u002F\u002F [1, 2, 3, 4]\n```\n\n**Use Cases**:\n- Clone state objects for immutability\n- Create independent copies before mutations\n- Deep cloning form data\n\n#### deepMerge - Deep Merge Objects\n\n```typescript\nimport { deepMerge } from '@delon\u002Futil\u002Farray';\n\nconst defaults = {\n  config: { theme: 'light', size: 'default' },\n  features: ['dashboard']\n};\n\nconst custom = {\n  config: { theme: 'dark' },\n  features: ['reports']\n};\n\nconst merged = deepMerge(defaults, custom);\n\u002F\u002F Result: {\n\u002F\u002F   config: { theme: 'dark', size: 'default' },\n\u002F\u002F   features: ['dashboard', 'reports']\n\u002F\u002F }\n```\n\n#### Other Array Functions\n\n```typescript\nimport { \n  groupBy,     \u002F\u002F Group array by property\n  uniq,        \u002F\u002F Remove duplicates\n  uniqBy,      \u002F\u002F Remove duplicates by property\n  orderBy      \u002F\u002F Sort array by properties\n} from '@delon\u002Futil\u002Farray';\n\n\u002F\u002F Group tasks by status\nconst grouped = groupBy(tasks, 'status');\n\u002F\u002F { pending: [...], completed: [...] }\n\n\u002F\u002F Remove duplicate IDs\nconst uniqueIds = uniq([1, 2, 2, 3]); \u002F\u002F [1, 2, 3]\n\n\u002F\u002F Remove duplicate tasks by ID\nconst uniqueTasks = uniqBy(tasks, 'id');\n\n\u002F\u002F Sort tasks\nconst sorted = orderBy(tasks, ['priority', 'createdAt'], ['asc', 'desc']);\n```\n\n### 2. String Utilities (`string\u002F`)\n\n#### format - String Formatting\n\n```typescript\nimport { format } from '@delon\u002Futil\u002Fstring';\n\n\u002F\u002F Template interpolation\nconst message = format('任務 {0} 已指派給 {1}', taskName, userName);\n\n\u002F\u002F Named parameters\nconst message2 = format('任務 {name} 的狀態為 {status}', { \n  name: '地基施工', \n  status: '進行中' \n});\n```\n\n#### Other String Functions\n\n```typescript\nimport { \n  toCamelCase,    \u002F\u002F Convert to camelCase\n  toPascalCase,   \u002F\u002F Convert to PascalCase\n  toKebabCase,    \u002F\u002F Convert to kebab-case\n  toSnakeCase,    \u002F\u002F Convert to snake_case\n  truncate        \u002F\u002F Truncate with ellipsis\n} from '@delon\u002Futil\u002Fstring';\n\ntoCamelCase('task-name');     \u002F\u002F 'taskName'\ntoPascalCase('task-name');    \u002F\u002F 'TaskName'\ntoKebabCase('TaskName');      \u002F\u002F 'task-name'\ntoSnakeCase('TaskName');      \u002F\u002F 'task_name'\ntruncate('Long text...', 10); \u002F\u002F 'Long te...'\n```\n\n### 3. Date Utilities (`date\u002F`)\n\n#### getTimeDistance - Get Time Ranges\n\n```typescript\nimport { getTimeDistance } from '@delon\u002Futil\u002Fdate';\n\n\u002F\u002F Get today's date range\nconst today = getTimeDistance('today');\n\u002F\u002F [Date(2024-12-25 00:00:00), Date(2024-12-25 23:59:59)]\n\n\u002F\u002F Get this week's date range\nconst week = getTimeDistance('week');\n\n\u002F\u002F Get this month's date range\nconst month = getTimeDistance('month');\n\n\u002F\u002F Get this year's date range\nconst year = getTimeDistance('year');\n\n\u002F\u002F Custom range with offset\nconst lastWeek = getTimeDistance('week', -1);\n```\n\n**Supported Types**:\n- `'today'` - Current day\n- `'week'` - Current week (Sunday to Saturday)\n- `'month'` - Current month\n- `'year'` - Current year\n- Custom offset (negative for past, positive for future)\n\n#### formatDistanceToNow - Relative Time\n\n```typescript\nimport { formatDistanceToNow } from '@delon\u002Futil\u002Fdate';\n\nconst createdAt = new Date('2024-12-20');\nconst relative = formatDistanceToNow(createdAt);\n\u002F\u002F \"5 days ago\"\n\nconst futureDate = new Date('2024-12-30');\nconst future = formatDistanceToNow(futureDate);\n\u002F\u002F \"in 5 days\"\n```\n\n### 4. Number Utilities (`number\u002F`)\n\n#### currency - Currency Formatting\n\n```typescript\nimport { currency } from '@delon\u002Futil\u002Fnumber';\n\n\u002F\u002F Format as currency\ncurrency(1234567.89);              \u002F\u002F \"$1,234,567.89\"\ncurrency(1234567.89, { unit: '¥' }); \u002F\u002F \"¥1,234,567.89\"\ncurrency(1234.5, { precision: 0 });  \u002F\u002F \"$1,235\"\n```\n\n#### Other Number Functions\n\n```typescript\nimport { \n  toFixed,      \u002F\u002F Round to fixed decimals\n  toPercent,    \u002F\u002F Convert to percentage\n  toThousands   \u002F\u002F Add thousands separators\n} from '@delon\u002Futil\u002Fnumber';\n\ntoFixed(1.2345, 2);           \u002F\u002F \"1.23\"\ntoPercent(0.1234);            \u002F\u002F \"12.34%\"\ntoPercent(0.1234, 1);         \u002F\u002F \"12.3%\"\ntoThousands(1234567);         \u002F\u002F \"1,234,567\"\n```\n\n### 5. Browser Utilities (`browser\u002F`)\n\n#### copyToClipboard - Copy to Clipboard\n\n```typescript\nimport { copy } from '@delon\u002Futil\u002Fbrowser';\n\nasync copyTaskLink(taskId: string) {\n  const link = `${window.location.origin}\u002Ftasks\u002F${taskId}`;\n  const success = await copy(link);\n  \n  if (success) {\n    this.messageService.success('連結已複製');\n  } else {\n    this.messageService.error('複製失敗');\n  }\n}\n```\n\n#### Other Browser Functions\n\n```typescript\nimport { \n  scrollToTop,    \u002F\u002F Smooth scroll to top\n  deepGet,        \u002F\u002F Get nested object property\n  deepSet,        \u002F\u002F Set nested object property\n  isEmpty,        \u002F\u002F Check if value is empty\n  isEqual,        \u002F\u002F Deep equality check\n  updateHostClass \u002F\u002F Update host element classes\n} from '@delon\u002Futil\u002Fbrowser';\n\nscrollToTop();\nscrollToTop({ duration: 500 });\n\nconst value = deepGet(obj, 'user.profile.name');\ndeepSet(obj, 'user.profile.name', 'New Name');\n\nisEmpty(null);      \u002F\u002F true\nisEmpty('');        \u002F\u002F true\nisEmpty([]);        \u002F\u002F true\nisEmpty({});        \u002F\u002F true\n\nisEqual({ a: 1 }, { a: 1 }); \u002F\u002F true\n```\n\n## Real-World Examples\n\n### Task Management Utilities\n\n```typescript\nimport { Component, signal, computed, inject } from '@angular\u002Fcore';\nimport { deepCopy, groupBy, orderBy } from '@delon\u002Futil\u002Farray';\nimport { format } from '@delon\u002Futil\u002Fstring';\nimport { getTimeDistance } from '@delon\u002Futil\u002Fdate';\nimport { copy } from '@delon\u002Futil\u002Fbrowser';\nimport { NzMessageService } from 'ng-zorro-antd\u002Fmessage';\n\n@Component({\n  selector: 'app-task-list',\n  standalone: true,\n  template: `\n    \u003Cnz-card>\n      \u003Cdiv nz-row [nzGutter]=\"16\">\n        @for (group of groupedTasks() | keyvalue; track group.key) {\n          \u003Cdiv nz-col [nzSpan]=\"8\">\n            \u003Ch3>{{ group.key }} ({{ group.value.length }})\u003C\u002Fh3>\n            @for (task of group.value; track task.id) {\n              \u003Cnz-card>\n                \u003Ch4>{{ task.title }}\u003C\u002Fh4>\n                \u003Cp>{{ formatTaskInfo(task) }}\u003C\u002Fp>\n                \u003Cbutton nz-button (click)=\"copyTaskLink(task.id)\">\n                  複製連結\n                \u003C\u002Fbutton>\n              \u003C\u002Fnz-card>\n            }\n          \u003C\u002Fdiv>\n        }\n      \u003C\u002Fdiv>\n    \u003C\u002Fnz-card>\n  `\n})\nexport class TaskListComponent {\n  private messageService = inject(NzMessageService);\n  \n  \u002F\u002F Original tasks from service\n  tasks = signal\u003CTask[]>([]);\n  \n  \u002F\u002F Group tasks by status using @delon\u002Futil\n  groupedTasks = computed(() => \n    groupBy(this.sortedTasks(), 'status')\n  );\n  \n  \u002F\u002F Sort tasks by priority and date\n  sortedTasks = computed(() => \n    orderBy(\n      this.tasks(),\n      ['priority', 'createdAt'],\n      ['asc', 'desc']\n    )\n  );\n  \n  \u002F\u002F Format task information\n  formatTaskInfo(task: Task): string {\n    return format(\n      '優先級: {priority}, 建立於 {date}',\n      {\n        priority: task.priority,\n        date: this.formatDate(task.createdAt)\n      }\n    );\n  }\n  \n  \u002F\u002F Copy task link to clipboard\n  async copyTaskLink(taskId: string): Promise\u003Cvoid> {\n    const link = `${window.location.origin}\u002Ftasks\u002F${taskId}`;\n    const success = await copy(link);\n    \n    if (success) {\n      this.messageService.success('任務連結已複製');\n    } else {\n      this.messageService.error('複製失敗，請手動複製');\n    }\n  }\n  \n  \u002F\u002F Clone task for editing\n  cloneTaskForEdit(task: Task): Task {\n    return deepCopy(task);\n  }\n  \n  \u002F\u002F Get this week's tasks\n  getThisWeekTasks(): Task[] {\n    const [start, end] = getTimeDistance('week');\n    return this.tasks().filter(t => \n      t.createdAt >= start && t.createdAt \u003C= end\n    );\n  }\n  \n  private formatDate(date: Date): string {\n    return format(\n      '{year}-{month}-{day}',\n      {\n        year: date.getFullYear(),\n        month: String(date.getMonth() + 1).padStart(2, '0'),\n        day: String(date.getDate()).padStart(2, '0')\n      }\n    );\n  }\n}\n```\n\n### Form Data Utilities\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { deepCopy, deepMerge } from '@delon\u002Futil\u002Farray';\nimport { isEmpty } from '@delon\u002Futil\u002Fbrowser';\n\n@Component({\n  selector: 'app-task-form',\n  standalone: true,\n  template: `\n    \u003Cform nz-form (ngSubmit)=\"handleSubmit()\">\n      \u003C!-- form fields -->\n      \u003Cbutton nz-button [disabled]=\"hasEmptyRequired()\">\n        提交\n      \u003C\u002Fbutton>\n    \u003C\u002Fform>\n  `\n})\nexport class TaskFormComponent {\n  \u002F\u002F Default form values\n  private defaults = {\n    priority: 'medium',\n    status: 'pending',\n    assignee: null,\n    tags: []\n  };\n  \n  \u002F\u002F Form data with defaults\n  formData = signal(deepCopy(this.defaults));\n  \n  \u002F\u002F Original task (for editing)\n  originalTask = signal\u003CTask | null>(null);\n  \n  \u002F\u002F Load task for editing\n  loadTask(task: Task): void {\n    \u002F\u002F Merge task data with defaults\n    const merged = deepMerge(this.defaults, task);\n    this.formData.set(merged);\n    this.originalTask.set(deepCopy(task));\n  }\n  \n  \u002F\u002F Check if required fields are empty\n  hasEmptyRequired(): boolean {\n    const data = this.formData();\n    return isEmpty(data.title) || isEmpty(data.assignee);\n  }\n  \n  \u002F\u002F Check if form has changes\n  hasChanges(): boolean {\n    const original = this.originalTask();\n    if (!original) return true;\n    \n    return !isEqual(original, this.formData());\n  }\n  \n  handleSubmit(): void {\n    if (!this.hasEmptyRequired()) {\n      \u002F\u002F Create clean copy for submission\n      const submitData = deepCopy(this.formData());\n      \u002F\u002F Submit...\n    }\n  }\n}\n```\n\n## Best Practices\n\n### 1. Use Utilities for Immutability\n\n✅ **DO**:\n```typescript\nconst taskCopy = deepCopy(task);\ntaskCopy.status = 'completed';\nthis.tasks.update(tasks => [...tasks, taskCopy]);\n```\n\n❌ **DON'T**:\n```typescript\ntask.status = 'completed';\nthis.tasks.update(tasks => [...tasks, task]); \u002F\u002F Mutates original\n```\n\n### 2. Leverage Computed Signals with Utilities\n\n✅ **DO**:\n```typescript\ngroupedTasks = computed(() => groupBy(this.tasks(), 'status'));\nsortedTasks = computed(() => orderBy(this.tasks(), ['priority'], ['asc']));\n```\n\n### 3. Use Type-Safe Utilities\n\n✅ **DO**:\n```typescript\nimport { deepCopy } from '@delon\u002Futil\u002Farray';\n\nconst copy: Task = deepCopy\u003CTask>(originalTask);\n```\n\n## Performance Considerations\n\n1. **deepCopy**: Expensive for large objects - use sparingly\n2. **groupBy\u002ForderBy**: Wrap in computed() to avoid recalculation\n3. **getTimeDistance**: Cache results if used frequently\n4. **copy**: Async operation - handle loading states\n\n## Integration Checklist\n\n- [ ] Install @delon\u002Futil@20.1.0\n- [ ] Import specific utilities (tree-shaking friendly)\n- [ ] Use with Angular Signals for reactivity\n- [ ] Add TypeScript generics for type safety\n- [ ] Handle async operations (copy)\n- [ ] Test edge cases (null, undefined, empty)\n\n## Cross-References\n\n- **angular-component** - Signals integration\n- **delon-form** - Form utilities for validation\n- **firebase-repository** - deepCopy for state management\n\n---\n\n**Version**: 1.0  \n**Created**: 2025-12-25  \n**Maintainer**: ng-events(GigHub) Development Team\n","# @delon\u002Futil - Utility Functions Library\n\nTrigger patterns: \"utility\", \"helper\", \"@delon\u002Futil\", \"format\", \"deepCopy\", \"deepMerge\"\n\n## Overview\n\n@delon\u002Futil provides a comprehensive collection of utility functions for common data manipulation tasks in ng-alain applications.\n\n**Package**: @delon\u002Futil@20.1.0\n\n## Categories\n\n### 1. Array Utilities (`array\u002F`)\n\n#### deepCopy - Deep Copy Arrays\u002FObjects\n\n```typescript\nimport { deepCopy } from '@delon\u002Futil\u002Farray';\n\nconst original = { name: '任務', items: [1, 2, 3], meta: { id: 1 } };\nconst copy = deepCopy(original);\n\n\u002F\u002F Changes to copy won't affect original\ncopy.items.push(4);\nconsole.log(original.items); \u002F\u002F [1, 2, 3]\nconsole.log(copy.items);     \u002F\u002F [1, 2, 3, 4]\n```\n\n**Use Cases**:\n- Clone state objects for immutability\n- Create independent copies before mutations\n- Deep cloning form data\n\n#### deepMerge - Deep Merge Objects\n\n```typescript\nimport { deepMerge } from '@delon\u002Futil\u002Farray';\n\nconst defaults = {\n  config: { theme: 'light', size: 'default' },\n  features: ['dashboard']\n};\n\nconst custom = {\n  config: { theme: 'dark' },\n  features: ['reports']\n};\n\nconst merged = deepMerge(defaults, custom);\n\u002F\u002F Result: {\n\u002F\u002F   config: { theme: 'dark', size: 'default' },\n\u002F\u002F   features: ['dashboard', 'reports']\n\u002F\u002F }\n```\n\n#### Other Array Functions\n\n```typescript\nimport { \n  groupBy,     \u002F\u002F Group array by property\n  uniq,        \u002F\u002F Remove duplicates\n  uniqBy,      \u002F\u002F Remove duplicates by property\n  orderBy      \u002F\u002F Sort array by properties\n} from '@delon\u002Futil\u002Farray';\n\n\u002F\u002F Group tasks by status\nconst grouped = groupBy(tasks, 'status');\n\u002F\u002F { pending: [...], completed: [...] }\n\n\u002F\u002F Remove duplicate IDs\nconst uniqueIds = uniq([1, 2, 2, 3]); \u002F\u002F [1, 2, 3]\n\n\u002F\u002F Remove duplicate tasks by ID\nconst uniqueTasks = uniqBy(tasks, 'id');\n\n\u002F\u002F Sort tasks\nconst sorted = orderBy(tasks, ['priority', 'createdAt'], ['asc', 'desc']);\n```\n\n### 2. String Utilities (`string\u002F`)\n\n#### format - String Formatting\n\n```typescript\nimport { format } from '@delon\u002Futil\u002Fstring';\n\n\u002F\u002F Template interpolation\nconst message = format('任務 {0} 已指派給 {1}', taskName, userName);\n\n\u002F\u002F Named parameters\nconst message2 = format('任務 {name} 的狀態為 {status}', { \n  name: '地基施工', \n  status: '進行中' \n});\n```\n\n#### Other String Functions\n\n```typescript\nimport { \n  toCamelCase,    \u002F\u002F Convert to camelCase\n  toPascalCase,   \u002F\u002F Convert to PascalCase\n  toKebabCase,    \u002F\u002F Convert to kebab-case\n  toSnakeCase,    \u002F\u002F Convert to snake_case\n  truncate        \u002F\u002F Truncate with ellipsis\n} from '@delon\u002Futil\u002Fstring';\n\ntoCamelCase('task-name');     \u002F\u002F 'taskName'\ntoPascalCase('task-name');    \u002F\u002F 'TaskName'\ntoKebabCase('TaskName');      \u002F\u002F 'task-name'\ntoSnakeCase('TaskName');      \u002F\u002F 'task_name'\ntruncate('Long text...', 10); \u002F\u002F 'Long te...'\n```\n\n### 3. Date Utilities (`date\u002F`)\n\n#### getTimeDistance - Get Time Ranges\n\n```typescript\nimport { getTimeDistance } from '@delon\u002Futil\u002Fdate';\n\n\u002F\u002F Get today's date range\nconst today = getTimeDistance('today');\n\u002F\u002F [Date(2024-12-25 00:00:00), Date(2024-12-25 23:59:59)]\n\n\u002F\u002F Get this week's date range\nconst week = getTimeDistance('week');\n\n\u002F\u002F Get this month's date range\nconst month = getTimeDistance('month');\n\n\u002F\u002F Get this year's date range\nconst year = getTimeDistance('year');\n\n\u002F\u002F Custom range with offset\nconst lastWeek = getTimeDistance('week', -1);\n```\n\n**Supported Types**:\n- `'today'` - Current day\n- `'week'` - Current week (Sunday to Saturday)\n- `'month'` - Current month\n- `'year'` - Current year\n- Custom offset (negative for past, positive for future)\n\n#### formatDistanceToNow - Relative Time\n\n```typescript\nimport { formatDistanceToNow } from '@delon\u002Futil\u002Fdate';\n\nconst createdAt = new Date('2024-12-20');\nconst relative = formatDistanceToNow(createdAt);\n\u002F\u002F \"5 days ago\"\n\nconst futureDate = new Date('2024-12-30');\nconst future = formatDistanceToNow(futureDate);\n\u002F\u002F \"in 5 days\"\n```\n\n### 4. Number Utilities (`number\u002F`)\n\n#### currency - Currency Formatting\n\n```typescript\nimport { currency } from '@delon\u002Futil\u002Fnumber';\n\n\u002F\u002F Format as currency\ncurrency(1234567.89);              \u002F\u002F \"$1,234,567.89\"\ncurrency(1234567.89, { unit: '¥' }); \u002F\u002F \"¥1,234,567.89\"\ncurrency(1234.5, { precision: 0 });  \u002F\u002F \"$1,235\"\n```\n\n#### Other Number Functions\n\n```typescript\nimport { \n  toFixed,      \u002F\u002F Round to fixed decimals\n  toPercent,    \u002F\u002F Convert to percentage\n  toThousands   \u002F\u002F Add thousands separators\n} from '@delon\u002Futil\u002Fnumber';\n\ntoFixed(1.2345, 2);           \u002F\u002F \"1.23\"\ntoPercent(0.1234);            \u002F\u002F \"12.34%\"\ntoPercent(0.1234, 1);         \u002F\u002F \"12.3%\"\ntoThousands(1234567);         \u002F\u002F \"1,234,567\"\n```\n\n### 5. Browser Utilities (`browser\u002F`)\n\n#### copyToClipboard - Copy to Clipboard\n\n```typescript\nimport { copy } from '@delon\u002Futil\u002Fbrowser';\n\nasync copyTaskLink(taskId: string) {\n  const link = `${window.location.origin}\u002Ftasks\u002F${taskId}`;\n  const success = await copy(link);\n  \n  if (success) {\n    this.messageService.success('連結已複製');\n  } else {\n    this.messageService.error('複製失敗');\n  }\n}\n```\n\n#### Other Browser Functions\n\n```typescript\nimport { \n  scrollToTop,    \u002F\u002F Smooth scroll to top\n  deepGet,        \u002F\u002F Get nested object property\n  deepSet,        \u002F\u002F Set nested object property\n  isEmpty,        \u002F\u002F Check if value is empty\n  isEqual,        \u002F\u002F Deep equality check\n  updateHostClass \u002F\u002F Update host element classes\n} from '@delon\u002Futil\u002Fbrowser';\n\nscrollToTop();\nscrollToTop({ duration: 500 });\n\nconst value = deepGet(obj, 'user.profile.name');\ndeepSet(obj, 'user.profile.name', 'New Name');\n\nisEmpty(null);      \u002F\u002F true\nisEmpty('');        \u002F\u002F true\nisEmpty([]);        \u002F\u002F true\nisEmpty({});        \u002F\u002F true\n\nisEqual({ a: 1 }, { a: 1 }); \u002F\u002F true\n```\n\n## Real-World Examples\n\n### Task Management Utilities\n\n```typescript\nimport { Component, signal, computed, inject } from '@angular\u002Fcore';\nimport { deepCopy, groupBy, orderBy } from '@delon\u002Futil\u002Farray';\nimport { format } from '@delon\u002Futil\u002Fstring';\nimport { getTimeDistance } from '@delon\u002Futil\u002Fdate';\nimport { copy } from '@delon\u002Futil\u002Fbrowser';\nimport { NzMessageService } from 'ng-zorro-antd\u002Fmessage';\n\n@Component({\n  selector: 'app-task-list',\n  standalone: true,\n  template: `\n    \u003Cnz-card>\n      \u003Cdiv nz-row [nzGutter]=\"16\">\n        @for (group of groupedTasks() | keyvalue; track group.key) {\n          \u003Cdiv nz-col [nzSpan]=\"8\">\n            \u003Ch3>{{ group.key }} ({{ group.value.length }})\u003C\u002Fh3>\n            @for (task of group.value; track task.id) {\n              \u003Cnz-card>\n                \u003Ch4>{{ task.title }}\u003C\u002Fh4>\n                \u003Cp>{{ formatTaskInfo(task) }}\u003C\u002Fp>\n                \u003Cbutton nz-button (click)=\"copyTaskLink(task.id)\">\n                  複製連結\n                \u003C\u002Fbutton>\n              \u003C\u002Fnz-card>\n            }\n          \u003C\u002Fdiv>\n        }\n      \u003C\u002Fdiv>\n    \u003C\u002Fnz-card>\n  `\n})\nexport class TaskListComponent {\n  private messageService = inject(NzMessageService);\n  \n  \u002F\u002F Original tasks from service\n  tasks = signal\u003CTask[]>([]);\n  \n  \u002F\u002F Group tasks by status using @delon\u002Futil\n  groupedTasks = computed(() => \n    groupBy(this.sortedTasks(), 'status')\n  );\n  \n  \u002F\u002F Sort tasks by priority and date\n  sortedTasks = computed(() => \n    orderBy(\n      this.tasks(),\n      ['priority', 'createdAt'],\n      ['asc', 'desc']\n    )\n  );\n  \n  \u002F\u002F Format task information\n  formatTaskInfo(task: Task): string {\n    return format(\n      '優先級: {priority}, 建立於 {date}',\n      {\n        priority: task.priority,\n        date: this.formatDate(task.createdAt)\n      }\n    );\n  }\n  \n  \u002F\u002F Copy task link to clipboard\n  async copyTaskLink(taskId: string): Promise\u003Cvoid> {\n    const link = `${window.location.origin}\u002Ftasks\u002F${taskId}`;\n    const success = await copy(link);\n    \n    if (success) {\n      this.messageService.success('任務連結已複製');\n    } else {\n      this.messageService.error('複製失敗，請手動複製');\n    }\n  }\n  \n  \u002F\u002F Clone task for editing\n  cloneTaskForEdit(task: Task): Task {\n    return deepCopy(task);\n  }\n  \n  \u002F\u002F Get this week's tasks\n  getThisWeekTasks(): Task[] {\n    const [start, end] = getTimeDistance('week');\n    return this.tasks().filter(t => \n      t.createdAt >= start && t.createdAt \u003C= end\n    );\n  }\n  \n  private formatDate(date: Date): string {\n    return format(\n      '{year}-{month}-{day}',\n      {\n        year: date.getFullYear(),\n        month: String(date.getMonth() + 1).padStart(2, '0'),\n        day: String(date.getDate()).padStart(2, '0')\n      }\n    );\n  }\n}\n```\n\n### Form Data Utilities\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { deepCopy, deepMerge } from '@delon\u002Futil\u002Farray';\nimport { isEmpty } from '@delon\u002Futil\u002Fbrowser';\n\n@Component({\n  selector: 'app-task-form',\n  standalone: true,\n  template: `\n    \u003Cform nz-form (ngSubmit)=\"handleSubmit()\">\n      \u003C!-- form fields -->\n      \u003Cbutton nz-button [disabled]=\"hasEmptyRequired()\">\n        提交\n      \u003C\u002Fbutton>\n    \u003C\u002Fform>\n  `\n})\nexport class TaskFormComponent {\n  \u002F\u002F Default form values\n  private defaults = {\n    priority: 'medium',\n    status: 'pending',\n    assignee: null,\n    tags: []\n  };\n  \n  \u002F\u002F Form data with defaults\n  formData = signal(deepCopy(this.defaults));\n  \n  \u002F\u002F Original task (for editing)\n  originalTask = signal\u003CTask | null>(null);\n  \n  \u002F\u002F Load task for editing\n  loadTask(task: Task): void {\n    \u002F\u002F Merge task data with defaults\n    const merged = deepMerge(this.defaults, task);\n    this.formData.set(merged);\n    this.originalTask.set(deepCopy(task));\n  }\n  \n  \u002F\u002F Check if required fields are empty\n  hasEmptyRequired(): boolean {\n    const data = this.formData();\n    return isEmpty(data.title) || isEmpty(data.assignee);\n  }\n  \n  \u002F\u002F Check if form has changes\n  hasChanges(): boolean {\n    const original = this.originalTask();\n    if (!original) return true;\n    \n    return !isEqual(original, this.formData());\n  }\n  \n  handleSubmit(): void {\n    if (!this.hasEmptyRequired()) {\n      \u002F\u002F Create clean copy for submission\n      const submitData = deepCopy(this.formData());\n      \u002F\u002F Submit...\n    }\n  }\n}\n```\n\n## Best Practices\n\n### 1. Use Utilities for Immutability\n\n✅ **DO**:\n```typescript\nconst taskCopy = deepCopy(task);\ntaskCopy.status = 'completed';\nthis.tasks.update(tasks => [...tasks, taskCopy]);\n```\n\n❌ **DON'T**:\n```typescript\ntask.status = 'completed';\nthis.tasks.update(tasks => [...tasks, task]); \u002F\u002F Mutates original\n```\n\n### 2. Leverage Computed Signals with Utilities\n\n✅ **DO**:\n```typescript\ngroupedTasks = computed(() => groupBy(this.tasks(), 'status'));\nsortedTasks = computed(() => orderBy(this.tasks(), ['priority'], ['asc']));\n```\n\n### 3. Use Type-Safe Utilities\n\n✅ **DO**:\n```typescript\nimport { deepCopy } from '@delon\u002Futil\u002Farray';\n\nconst copy: Task = deepCopy\u003CTask>(originalTask);\n```\n\n## Performance Considerations\n\n1. **deepCopy**: Expensive for large objects - use sparingly\n2. **groupBy\u002ForderBy**: Wrap in computed() to avoid recalculation\n3. **getTimeDistance**: Cache results if used frequently\n4. **copy**: Async operation - handle loading states\n\n## Integration Checklist\n\n- [ ] Install @delon\u002Futil@20.1.0\n- [ ] Import specific utilities (tree-shaking friendly)\n- [ ] Use with Angular Signals for reactivity\n- [ ] Add TypeScript generics for type safety\n- [ ] Handle async operations (copy)\n- [ ] Test edge cases (null, undefined, empty)\n\n## Cross-References\n\n- **angular-component** - Signals integration\n- **delon-form** - Form utilities for validation\n- **firebase-repository** - deepCopy for state management\n\n---\n\n**Version**: 1.0  \n**Created**: 2025-12-25  \n**Maintainer**: ng-events(GigHub) Development Team\n",{"name":5,"description":7},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Faiskillstore\u002Fmarketplace\u002F7spade\u002Fdelon-util",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill delon-util",{"tool":54,"command":55},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill delon-util",{"tool":57,"command":58},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill delon-util","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002F7spade\u002Fdelon-util\u002FSKILL.md",true,[62,68,71],{"path":63,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":66,"sourceHtmlUrl":67},"install.sh","SCRIPT","脚本",20,"https:\u002F\u002Fgithub.com\u002Fberlinguyinca\u002Fautospec\u002Fblob\u002Fmain\u002Fcrates\u002Fautospec-cli\u002Ftests\u002Ffixtures\u002Fvalidation-cutover\u002Fshared-script-install\u002Fskills\u002Fowns\u002Finstall.sh",{"path":69,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":66,"sourceHtmlUrl":70},"scripts\u002Flocal.sh","https:\u002F\u002Fgithub.com\u002Fberlinguyinca\u002Fautospec\u002Fblob\u002Fmain\u002Fcrates\u002Fautospec-cli\u002Ftests\u002Ffixtures\u002Fvalidation-cutover\u002Fshared-script-install\u002Fskills\u002Fowns\u002Fscripts\u002Flocal.sh",{"path":72,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":75,"sourceHtmlUrl":76},"skill-report.json","REFERENCE","参考文档",13019,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002F7spade\u002Fdelon-util\u002Fskill-report.json",[78,88,98,107,113,120],{"skillNo":79,"name":80,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":84,"publishedAt":34},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[83],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[85,86,87],"model","model hierarchy","model hierarchy skill",{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":34},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[93],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[95,96,97],"MCP","zoom","zoom mcp",{"skillNo":99,"name":100,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":34},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[104,105,106,96],"sdk","zoom apps sdk","apps",{"skillNo":108,"name":109,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":34},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[109],{"skillNo":114,"name":115,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":119,"publishedAt":34},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[118],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[115],{"skillNo":121,"name":122,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":34},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[122]]