[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JB2NF6S1WZ48KJDB5604R7":3,"skill-same:01M6JB2NF6S1WZ48KJDB5604R7":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":27,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":13,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M6JB2NF6S1WZ48KJDB5604R7","screenfull-fullscreen-api",null,"Implement fullscreen functionality using screenfull library for cross-browser fullscreen support in ng-events project","superhealth\u002Fskills-hub","superhealth","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6292112?v=4","01M6JA6E7F21BYK5W3E4P270TT","01M6JA6EC0W8MPHKDJVBZDSEZ0",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fscreenfull-fullscreen-api\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":25,"taxonomyTypeDesc":26},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[28,29,30,31,32,33,34,35,36,37,38],"api","screenfull fullscreen api","implement","support","browser","library","project","events","ng","cross-browser","for","2026-11-10 10:12:30",false,"---\nname: screenfull-fullscreen-api\ndescription: Implement fullscreen functionality using screenfull library for cross-browser fullscreen support in ng-events project\nlicense: MIT\n---\n\n# Screenfull Fullscreen API Skill\n\nThis skill guides the implementation of fullscreen functionality using the screenfull library in the ng-events construction site management system.\n\n## When to Use This Skill\n\n**Triggers**: \"fullscreen\", \"screenfull\", \"full screen mode\", \"toggle fullscreen\", \"exit fullscreen\"\n\nUse this skill when:\n- Adding fullscreen toggle buttons\n- Creating fullscreen viewers (images, videos, dashboards)\n- Implementing presentation modes\n- Building immersive UI experiences\n- Providing fullscreen for specific components\n\n## Installation & Setup\n\n```bash\n# Already installed in package.json\nyarn add screenfull@^6.0.2\n```\n\n## Core Patterns\n\n### 1. Basic Fullscreen Toggle\n\n```typescript\nimport { Component, inject, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-fullscreen-toggle',\n  standalone: true,\n  template: `\n    \u003Cbutton (click)=\"toggleFullscreen()\" class=\"fullscreen-btn\">\n      @if (isFullscreen()) {\n        \u003Ci nz-icon nzType=\"fullscreen-exit\" nzTheme=\"outline\">\u003C\u002Fi>\n        Exit Fullscreen\n      } @else {\n        \u003Ci nz-icon nzType=\"fullscreen\" nzTheme=\"outline\">\u003C\u002Fi>\n        Enter Fullscreen\n      }\n    \u003C\u002Fbutton>\n  `\n})\nexport class FullscreenToggleComponent {\n  isFullscreen = signal(false);\n  \n  toggleFullscreen(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle();\n      this.updateFullscreenState();\n      \n      \u002F\u002F Listen for fullscreen changes\n      screenfull.on('change', () => {\n        this.updateFullscreenState();\n      });\n    } else {\n      console.warn('Fullscreen API not supported');\n    }\n  }\n  \n  private updateFullscreenState(): void {\n    this.isFullscreen.set(screenfull.isFullscreen);\n  }\n}\n```\n\n### 2. Fullscreen Specific Element\n\n```typescript\nimport { Component, ElementRef, ViewChild, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-dashboard-fullscreen',\n  standalone: true,\n  template: `\n    \u003Cdiv #dashboardContainer class=\"dashboard-container\">\n      \u003Cdiv class=\"dashboard-header\">\n        \u003Ch2>Construction Progress Dashboard\u003C\u002Fh2>\n        \u003Cbutton (click)=\"toggleFullscreen()\" class=\"btn-fullscreen\">\n          @if (isFullscreen()) {\n            \u003Ci nz-icon nzType=\"fullscreen-exit\">\u003C\u002Fi>\n          } @else {\n            \u003Ci nz-icon nzType=\"fullscreen\">\u003C\u002Fi>\n          }\n        \u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"dashboard-content\">\n        \u003Capp-progress-charts \u002F>\n        \u003Capp-task-summary \u002F>\n        \u003Capp-recent-activities \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `,\n  styles: [`\n    .dashboard-container {\n      position: relative;\n      background: white;\n      padding: 20px;\n    }\n    \n    .dashboard-container:-webkit-full-screen {\n      background: #1f1f1f;\n      color: white;\n    }\n    \n    .dashboard-container:fullscreen {\n      background: #1f1f1f;\n      color: white;\n    }\n    \n    .btn-fullscreen {\n      position: absolute;\n      top: 20px;\n      right: 20px;\n    }\n  `]\n})\nexport class DashboardFullscreenComponent {\n  @ViewChild('dashboardContainer', { static: true }) container!: ElementRef;\n  isFullscreen = signal(false);\n  \n  toggleFullscreen(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle(this.container.nativeElement);\n      \n      screenfull.on('change', () => {\n        this.isFullscreen.set(screenfull.isFullscreen);\n      });\n    }\n  }\n}\n```\n\n### 3. Image Gallery Fullscreen\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\nimport { NzImageModule } from 'ng-zorro-antd\u002Fimage';\n\n@Component({\n  selector: 'app-construction-photos',\n  standalone: true,\n  imports: [NzImageModule],\n  template: `\n    \u003Cdiv class=\"photo-gallery\">\n      \u003Ch3>Construction Site Photos\u003C\u002Fh3>\n      \n      \u003Cdiv class=\"photo-grid\">\n        @for (photo of photos(); track photo.id) {\n          \u003Cdiv class=\"photo-item\" (click)=\"viewFullscreen(photo)\">\n            \u003Cimg [src]=\"photo.thumbnailUrl\" [alt]=\"photo.title\" \u002F>\n            \u003Cdiv class=\"photo-overlay\">\n              \u003Ci nz-icon nzType=\"fullscreen\" nzTheme=\"outline\">\u003C\u002Fi>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        }\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \n    @if (currentPhoto()) {\n      \u003Cdiv class=\"fullscreen-viewer\" #viewer>\n        \u003Cbutton class=\"close-btn\" (click)=\"exitFullscreen()\">\n          \u003Ci nz-icon nzType=\"close\">\u003C\u002Fi>\n        \u003C\u002Fbutton>\n        \u003Cimg [src]=\"currentPhoto()!.fullUrl\" [alt]=\"currentPhoto()!.title\" \u002F>\n        \u003Cdiv class=\"photo-info\">\n          \u003Ch4>{{ currentPhoto()!.title }}\u003C\u002Fh4>\n          \u003Cp>{{ currentPhoto()!.description }}\u003C\u002Fp>\n          \u003Cspan>{{ currentPhoto()!.date | date }}\u003C\u002Fspan>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    }\n  `,\n  styles: [`\n    .photo-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n      gap: 16px;\n    }\n    \n    .photo-item {\n      position: relative;\n      cursor: pointer;\n      overflow: hidden;\n      border-radius: 4px;\n    }\n    \n    .photo-item img {\n      width: 100%;\n      height: 200px;\n      object-fit: cover;\n      transition: transform 0.3s;\n    }\n    \n    .photo-item:hover img {\n      transform: scale(1.1);\n    }\n    \n    .photo-overlay {\n      position: absolute;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      background: rgba(0, 0, 0, 0.5);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      opacity: 0;\n      transition: opacity 0.3s;\n    }\n    \n    .photo-item:hover .photo-overlay {\n      opacity: 1;\n    }\n    \n    .fullscreen-viewer {\n      position: fixed;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      background: rgba(0, 0, 0, 0.95);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      z-index: 9999;\n    }\n    \n    .fullscreen-viewer img {\n      max-width: 90%;\n      max-height: 90%;\n      object-fit: contain;\n    }\n    \n    .close-btn {\n      position: absolute;\n      top: 20px;\n      right: 20px;\n      background: rgba(255, 255, 255, 0.2);\n      border: none;\n      color: white;\n      font-size: 24px;\n      cursor: pointer;\n      padding: 10px;\n      border-radius: 4px;\n    }\n    \n    .photo-info {\n      position: absolute;\n      bottom: 20px;\n      left: 20px;\n      color: white;\n      text-align: left;\n    }\n  `]\n})\nexport class ConstructionPhotosComponent {\n  photos = signal([\n    {\n      id: '1',\n      title: 'Foundation Work',\n      description: 'Foundation concrete pouring',\n      thumbnailUrl: 'assets\u002Fphotos\u002Fthumb1.jpg',\n      fullUrl: 'assets\u002Fphotos\u002Ffull1.jpg',\n      date: new Date()\n    }\n    \u002F\u002F ... more photos\n  ]);\n  \n  currentPhoto = signal\u003CPhoto | null>(null);\n  \n  viewFullscreen(photo: Photo): void {\n    this.currentPhoto.set(photo);\n    \n    if (screenfull.isEnabled) {\n      const viewer = document.querySelector('.fullscreen-viewer');\n      if (viewer) {\n        screenfull.request(viewer as HTMLElement);\n        \n        screenfull.on('change', () => {\n          if (!screenfull.isFullscreen) {\n            this.currentPhoto.set(null);\n          }\n        });\n      }\n    }\n  }\n  \n  exitFullscreen(): void {\n    if (screenfull.isEnabled && screenfull.isFullscreen) {\n      screenfull.exit();\n    }\n    this.currentPhoto.set(null);\n  }\n}\n```\n\n### 4. Presentation Mode\n\n```typescript\nimport { Component, signal, OnInit, OnDestroy } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-presentation-mode',\n  standalone: true,\n  template: `\n    \u003Cdiv class=\"presentation-container\" [class.presentation-active]=\"isPresenting()\">\n      \u003Cdiv class=\"presentation-header\">\n        \u003Ch2>{{ currentSlide().title }}\u003C\u002Fh2>\n        \u003Cdiv class=\"controls\">\n          \u003Cbutton (click)=\"previousSlide()\" [disabled]=\"currentSlideIndex() === 0\">\n            \u003Ci nz-icon nzType=\"left\">\u003C\u002Fi> Previous\n          \u003C\u002Fbutton>\n          \u003Cspan>{{ currentSlideIndex() + 1 }} \u002F {{ slides().length }}\u003C\u002Fspan>\n          \u003Cbutton (click)=\"nextSlide()\" [disabled]=\"currentSlideIndex() === slides().length - 1\">\n            Next \u003Ci nz-icon nzType=\"right\">\u003C\u002Fi>\n          \u003C\u002Fbutton>\n          \u003Cbutton (click)=\"togglePresentation()\" class=\"btn-present\">\n            @if (isPresenting()) {\n              \u003Ci nz-icon nzType=\"fullscreen-exit\">\u003C\u002Fi> Exit\n            } @else {\n              \u003Ci nz-icon nzType=\"fullscreen\">\u003C\u002Fi> Present\n            }\n          \u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"slide-content\">\n        \u003Cdiv [innerHTML]=\"currentSlide().content\">\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `,\n  styles: [`\n    .presentation-container {\n      background: white;\n      padding: 20px;\n    }\n    \n    .presentation-active {\n      background: #1a1a1a;\n      color: white;\n      padding: 40px;\n    }\n    \n    .presentation-active .slide-content {\n      font-size: 1.5em;\n    }\n  `]\n})\nexport class PresentationModeComponent implements OnInit, OnDestroy {\n  slides = signal([\n    {\n      title: 'Project Overview',\n      content: '\u003Ch1>Construction Site Progress\u003C\u002Fh1>\u003Cp>Q4 2025 Update\u003C\u002Fp>'\n    },\n    {\n      title: 'Milestones Achieved',\n      content: '\u003Cul>\u003Cli>Foundation Complete\u003C\u002Fli>\u003Cli>Structural Work 75%\u003C\u002Fli>\u003C\u002Ful>'\n    }\n    \u002F\u002F ... more slides\n  ]);\n  \n  currentSlideIndex = signal(0);\n  isPresenting = signal(false);\n  \n  currentSlide = computed(() => this.slides()[this.currentSlideIndex()]);\n  \n  ngOnInit(): void {\n    \u002F\u002F Listen for keyboard shortcuts\n    document.addEventListener('keydown', this.handleKeyPress.bind(this));\n  }\n  \n  ngOnDestroy(): void {\n    document.removeEventListener('keydown', this.handleKeyPress.bind(this));\n    \n    if (screenfull.isEnabled) {\n      screenfull.off('change');\n    }\n  }\n  \n  togglePresentation(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle();\n      \n      screenfull.on('change', () => {\n        this.isPresenting.set(screenfull.isFullscreen);\n      });\n    }\n  }\n  \n  nextSlide(): void {\n    if (this.currentSlideIndex() \u003C this.slides().length - 1) {\n      this.currentSlideIndex.update(i => i + 1);\n    }\n  }\n  \n  previousSlide(): void {\n    if (this.currentSlideIndex() > 0) {\n      this.currentSlideIndex.update(i => i - 1);\n    }\n  }\n  \n  private handleKeyPress(event: KeyboardEvent): void {\n    if (!this.isPresenting()) return;\n    \n    switch (event.key) {\n      case 'ArrowRight':\n      case 'PageDown':\n        this.nextSlide();\n        break;\n      case 'ArrowLeft':\n      case 'PageUp':\n        this.previousSlide();\n        break;\n      case 'Escape':\n        if (screenfull.isEnabled && screenfull.isFullscreen) {\n          screenfull.exit();\n        }\n        break;\n    }\n  }\n}\n```\n\n## API Reference\n\n### screenfull Methods\n\n```typescript\n\u002F\u002F Check if fullscreen API is supported\nscreenfull.isEnabled \u002F\u002F boolean\n\n\u002F\u002F Toggle fullscreen\nscreenfull.toggle() \u002F\u002F Toggle document\nscreenfull.toggle(element) \u002F\u002F Toggle specific element\n\n\u002F\u002F Request fullscreen\nscreenfull.request() \u002F\u002F Request for document\nscreenfull.request(element) \u002F\u002F Request for specific element\n\n\u002F\u002F Exit fullscreen\nscreenfull.exit()\n\n\u002F\u002F Check if currently fullscreen\nscreenfull.isFullscreen \u002F\u002F boolean\n\n\u002F\u002F Get fullscreen element\nscreenfull.element \u002F\u002F Element | null\n\n\u002F\u002F Event listeners\nscreenfull.on('change', () => {})\nscreenfull.on('error', (event) => {})\nscreenfull.off('change', handler)\n```\n\n## Integration Checklist\n\nWhen using screenfull:\n- [ ] Check `screenfull.isEnabled` before using API\n- [ ] Handle browser compatibility gracefully\n- [ ] Clean up event listeners on component destroy\n- [ ] Provide exit fullscreen button\n- [ ] Test on different browsers (Chrome, Firefox, Safari)\n- [ ] Handle keyboard shortcuts (Escape to exit)\n- [ ] Style fullscreen mode appropriately\n- [ ] Consider mobile browsers (limited support)\n\n## Best Practices\n\n### DO ✅\n- Always check `screenfull.isEnabled` before use\n- Provide clear UI to exit fullscreen\n- Use Escape key to exit fullscreen\n- Style fullscreen elements appropriately\n- Clean up event listeners\n- Handle errors gracefully\n\n### DON'T ❌\n- Assume fullscreen API is always available\n- Force fullscreen without user interaction\n- Forget to remove event listeners\n- Ignore mobile browser limitations\n- Use fullscreen for sensitive actions\n\n## Browser Compatibility\n\n| Browser | Support |\n|---------|---------|\n| Chrome | ✅ Full support |\n| Firefox | ✅ Full support |\n| Safari | ✅ Full support (iOS limited) |\n| Edge | ✅ Full support |\n| iOS Safari | ⚠️ Limited (video only) |\n| Android Chrome | ✅ Full support |\n\n## References\n\n- [screenfull.js GitHub](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fscreenfull.js)\n- [Fullscreen API MDN](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FFullscreen_API)\n- [Can I Use Fullscreen](https:\u002F\u002Fcaniuse.com\u002Ffullscreen)\n\n---\n\n**Version**: 1.0.0  \n**Compatible with**: screenfull 6.0.x  \n**Last Updated**: 2025-12-25\n","# Screenfull Fullscreen API Skill\n\nThis skill guides the implementation of fullscreen functionality using the screenfull library in the ng-events construction site management system.\n\n## When to Use This Skill\n\n**Triggers**: \"fullscreen\", \"screenfull\", \"full screen mode\", \"toggle fullscreen\", \"exit fullscreen\"\n\nUse this skill when:\n- Adding fullscreen toggle buttons\n- Creating fullscreen viewers (images, videos, dashboards)\n- Implementing presentation modes\n- Building immersive UI experiences\n- Providing fullscreen for specific components\n\n## Installation & Setup\n\n```bash\n# Already installed in package.json\nyarn add screenfull@^6.0.2\n```\n\n## Core Patterns\n\n### 1. Basic Fullscreen Toggle\n\n```typescript\nimport { Component, inject, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-fullscreen-toggle',\n  standalone: true,\n  template: `\n    \u003Cbutton (click)=\"toggleFullscreen()\" class=\"fullscreen-btn\">\n      @if (isFullscreen()) {\n        \u003Ci nz-icon nzType=\"fullscreen-exit\" nzTheme=\"outline\">\u003C\u002Fi>\n        Exit Fullscreen\n      } @else {\n        \u003Ci nz-icon nzType=\"fullscreen\" nzTheme=\"outline\">\u003C\u002Fi>\n        Enter Fullscreen\n      }\n    \u003C\u002Fbutton>\n  `\n})\nexport class FullscreenToggleComponent {\n  isFullscreen = signal(false);\n  \n  toggleFullscreen(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle();\n      this.updateFullscreenState();\n      \n      \u002F\u002F Listen for fullscreen changes\n      screenfull.on('change', () => {\n        this.updateFullscreenState();\n      });\n    } else {\n      console.warn('Fullscreen API not supported');\n    }\n  }\n  \n  private updateFullscreenState(): void {\n    this.isFullscreen.set(screenfull.isFullscreen);\n  }\n}\n```\n\n### 2. Fullscreen Specific Element\n\n```typescript\nimport { Component, ElementRef, ViewChild, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-dashboard-fullscreen',\n  standalone: true,\n  template: `\n    \u003Cdiv #dashboardContainer class=\"dashboard-container\">\n      \u003Cdiv class=\"dashboard-header\">\n        \u003Ch2>Construction Progress Dashboard\u003C\u002Fh2>\n        \u003Cbutton (click)=\"toggleFullscreen()\" class=\"btn-fullscreen\">\n          @if (isFullscreen()) {\n            \u003Ci nz-icon nzType=\"fullscreen-exit\">\u003C\u002Fi>\n          } @else {\n            \u003Ci nz-icon nzType=\"fullscreen\">\u003C\u002Fi>\n          }\n        \u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"dashboard-content\">\n        \u003Capp-progress-charts \u002F>\n        \u003Capp-task-summary \u002F>\n        \u003Capp-recent-activities \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `,\n  styles: [`\n    .dashboard-container {\n      position: relative;\n      background: white;\n      padding: 20px;\n    }\n    \n    .dashboard-container:-webkit-full-screen {\n      background: #1f1f1f;\n      color: white;\n    }\n    \n    .dashboard-container:fullscreen {\n      background: #1f1f1f;\n      color: white;\n    }\n    \n    .btn-fullscreen {\n      position: absolute;\n      top: 20px;\n      right: 20px;\n    }\n  `]\n})\nexport class DashboardFullscreenComponent {\n  @ViewChild('dashboardContainer', { static: true }) container!: ElementRef;\n  isFullscreen = signal(false);\n  \n  toggleFullscreen(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle(this.container.nativeElement);\n      \n      screenfull.on('change', () => {\n        this.isFullscreen.set(screenfull.isFullscreen);\n      });\n    }\n  }\n}\n```\n\n### 3. Image Gallery Fullscreen\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\nimport { NzImageModule } from 'ng-zorro-antd\u002Fimage';\n\n@Component({\n  selector: 'app-construction-photos',\n  standalone: true,\n  imports: [NzImageModule],\n  template: `\n    \u003Cdiv class=\"photo-gallery\">\n      \u003Ch3>Construction Site Photos\u003C\u002Fh3>\n      \n      \u003Cdiv class=\"photo-grid\">\n        @for (photo of photos(); track photo.id) {\n          \u003Cdiv class=\"photo-item\" (click)=\"viewFullscreen(photo)\">\n            \u003Cimg [src]=\"photo.thumbnailUrl\" [alt]=\"photo.title\" \u002F>\n            \u003Cdiv class=\"photo-overlay\">\n              \u003Ci nz-icon nzType=\"fullscreen\" nzTheme=\"outline\">\u003C\u002Fi>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        }\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \n    @if (currentPhoto()) {\n      \u003Cdiv class=\"fullscreen-viewer\" #viewer>\n        \u003Cbutton class=\"close-btn\" (click)=\"exitFullscreen()\">\n          \u003Ci nz-icon nzType=\"close\">\u003C\u002Fi>\n        \u003C\u002Fbutton>\n        \u003Cimg [src]=\"currentPhoto()!.fullUrl\" [alt]=\"currentPhoto()!.title\" \u002F>\n        \u003Cdiv class=\"photo-info\">\n          \u003Ch4>{{ currentPhoto()!.title }}\u003C\u002Fh4>\n          \u003Cp>{{ currentPhoto()!.description }}\u003C\u002Fp>\n          \u003Cspan>{{ currentPhoto()!.date | date }}\u003C\u002Fspan>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    }\n  `,\n  styles: [`\n    .photo-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n      gap: 16px;\n    }\n    \n    .photo-item {\n      position: relative;\n      cursor: pointer;\n      overflow: hidden;\n      border-radius: 4px;\n    }\n    \n    .photo-item img {\n      width: 100%;\n      height: 200px;\n      object-fit: cover;\n      transition: transform 0.3s;\n    }\n    \n    .photo-item:hover img {\n      transform: scale(1.1);\n    }\n    \n    .photo-overlay {\n      position: absolute;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      background: rgba(0, 0, 0, 0.5);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      opacity: 0;\n      transition: opacity 0.3s;\n    }\n    \n    .photo-item:hover .photo-overlay {\n      opacity: 1;\n    }\n    \n    .fullscreen-viewer {\n      position: fixed;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      background: rgba(0, 0, 0, 0.95);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      z-index: 9999;\n    }\n    \n    .fullscreen-viewer img {\n      max-width: 90%;\n      max-height: 90%;\n      object-fit: contain;\n    }\n    \n    .close-btn {\n      position: absolute;\n      top: 20px;\n      right: 20px;\n      background: rgba(255, 255, 255, 0.2);\n      border: none;\n      color: white;\n      font-size: 24px;\n      cursor: pointer;\n      padding: 10px;\n      border-radius: 4px;\n    }\n    \n    .photo-info {\n      position: absolute;\n      bottom: 20px;\n      left: 20px;\n      color: white;\n      text-align: left;\n    }\n  `]\n})\nexport class ConstructionPhotosComponent {\n  photos = signal([\n    {\n      id: '1',\n      title: 'Foundation Work',\n      description: 'Foundation concrete pouring',\n      thumbnailUrl: 'assets\u002Fphotos\u002Fthumb1.jpg',\n      fullUrl: 'assets\u002Fphotos\u002Ffull1.jpg',\n      date: new Date()\n    }\n    \u002F\u002F ... more photos\n  ]);\n  \n  currentPhoto = signal\u003CPhoto | null>(null);\n  \n  viewFullscreen(photo: Photo): void {\n    this.currentPhoto.set(photo);\n    \n    if (screenfull.isEnabled) {\n      const viewer = document.querySelector('.fullscreen-viewer');\n      if (viewer) {\n        screenfull.request(viewer as HTMLElement);\n        \n        screenfull.on('change', () => {\n          if (!screenfull.isFullscreen) {\n            this.currentPhoto.set(null);\n          }\n        });\n      }\n    }\n  }\n  \n  exitFullscreen(): void {\n    if (screenfull.isEnabled && screenfull.isFullscreen) {\n      screenfull.exit();\n    }\n    this.currentPhoto.set(null);\n  }\n}\n```\n\n### 4. Presentation Mode\n\n```typescript\nimport { Component, signal, OnInit, OnDestroy } from '@angular\u002Fcore';\nimport screenfull from 'screenfull';\n\n@Component({\n  selector: 'app-presentation-mode',\n  standalone: true,\n  template: `\n    \u003Cdiv class=\"presentation-container\" [class.presentation-active]=\"isPresenting()\">\n      \u003Cdiv class=\"presentation-header\">\n        \u003Ch2>{{ currentSlide().title }}\u003C\u002Fh2>\n        \u003Cdiv class=\"controls\">\n          \u003Cbutton (click)=\"previousSlide()\" [disabled]=\"currentSlideIndex() === 0\">\n            \u003Ci nz-icon nzType=\"left\">\u003C\u002Fi> Previous\n          \u003C\u002Fbutton>\n          \u003Cspan>{{ currentSlideIndex() + 1 }} \u002F {{ slides().length }}\u003C\u002Fspan>\n          \u003Cbutton (click)=\"nextSlide()\" [disabled]=\"currentSlideIndex() === slides().length - 1\">\n            Next \u003Ci nz-icon nzType=\"right\">\u003C\u002Fi>\n          \u003C\u002Fbutton>\n          \u003Cbutton (click)=\"togglePresentation()\" class=\"btn-present\">\n            @if (isPresenting()) {\n              \u003Ci nz-icon nzType=\"fullscreen-exit\">\u003C\u002Fi> Exit\n            } @else {\n              \u003Ci nz-icon nzType=\"fullscreen\">\u003C\u002Fi> Present\n            }\n          \u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"slide-content\">\n        \u003Cdiv [innerHTML]=\"currentSlide().content\">\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `,\n  styles: [`\n    .presentation-container {\n      background: white;\n      padding: 20px;\n    }\n    \n    .presentation-active {\n      background: #1a1a1a;\n      color: white;\n      padding: 40px;\n    }\n    \n    .presentation-active .slide-content {\n      font-size: 1.5em;\n    }\n  `]\n})\nexport class PresentationModeComponent implements OnInit, OnDestroy {\n  slides = signal([\n    {\n      title: 'Project Overview',\n      content: '\u003Ch1>Construction Site Progress\u003C\u002Fh1>\u003Cp>Q4 2025 Update\u003C\u002Fp>'\n    },\n    {\n      title: 'Milestones Achieved',\n      content: '\u003Cul>\u003Cli>Foundation Complete\u003C\u002Fli>\u003Cli>Structural Work 75%\u003C\u002Fli>\u003C\u002Ful>'\n    }\n    \u002F\u002F ... more slides\n  ]);\n  \n  currentSlideIndex = signal(0);\n  isPresenting = signal(false);\n  \n  currentSlide = computed(() => this.slides()[this.currentSlideIndex()]);\n  \n  ngOnInit(): void {\n    \u002F\u002F Listen for keyboard shortcuts\n    document.addEventListener('keydown', this.handleKeyPress.bind(this));\n  }\n  \n  ngOnDestroy(): void {\n    document.removeEventListener('keydown', this.handleKeyPress.bind(this));\n    \n    if (screenfull.isEnabled) {\n      screenfull.off('change');\n    }\n  }\n  \n  togglePresentation(): void {\n    if (screenfull.isEnabled) {\n      screenfull.toggle();\n      \n      screenfull.on('change', () => {\n        this.isPresenting.set(screenfull.isFullscreen);\n      });\n    }\n  }\n  \n  nextSlide(): void {\n    if (this.currentSlideIndex() \u003C this.slides().length - 1) {\n      this.currentSlideIndex.update(i => i + 1);\n    }\n  }\n  \n  previousSlide(): void {\n    if (this.currentSlideIndex() > 0) {\n      this.currentSlideIndex.update(i => i - 1);\n    }\n  }\n  \n  private handleKeyPress(event: KeyboardEvent): void {\n    if (!this.isPresenting()) return;\n    \n    switch (event.key) {\n      case 'ArrowRight':\n      case 'PageDown':\n        this.nextSlide();\n        break;\n      case 'ArrowLeft':\n      case 'PageUp':\n        this.previousSlide();\n        break;\n      case 'Escape':\n        if (screenfull.isEnabled && screenfull.isFullscreen) {\n          screenfull.exit();\n        }\n        break;\n    }\n  }\n}\n```\n\n## API Reference\n\n### screenfull Methods\n\n```typescript\n\u002F\u002F Check if fullscreen API is supported\nscreenfull.isEnabled \u002F\u002F boolean\n\n\u002F\u002F Toggle fullscreen\nscreenfull.toggle() \u002F\u002F Toggle document\nscreenfull.toggle(element) \u002F\u002F Toggle specific element\n\n\u002F\u002F Request fullscreen\nscreenfull.request() \u002F\u002F Request for document\nscreenfull.request(element) \u002F\u002F Request for specific element\n\n\u002F\u002F Exit fullscreen\nscreenfull.exit()\n\n\u002F\u002F Check if currently fullscreen\nscreenfull.isFullscreen \u002F\u002F boolean\n\n\u002F\u002F Get fullscreen element\nscreenfull.element \u002F\u002F Element | null\n\n\u002F\u002F Event listeners\nscreenfull.on('change', () => {})\nscreenfull.on('error', (event) => {})\nscreenfull.off('change', handler)\n```\n\n## Integration Checklist\n\nWhen using screenfull:\n- [ ] Check `screenfull.isEnabled` before using API\n- [ ] Handle browser compatibility gracefully\n- [ ] Clean up event listeners on component destroy\n- [ ] Provide exit fullscreen button\n- [ ] Test on different browsers (Chrome, Firefox, Safari)\n- [ ] Handle keyboard shortcuts (Escape to exit)\n- [ ] Style fullscreen mode appropriately\n- [ ] Consider mobile browsers (limited support)\n\n## Best Practices\n\n### DO ✅\n- Always check `screenfull.isEnabled` before use\n- Provide clear UI to exit fullscreen\n- Use Escape key to exit fullscreen\n- Style fullscreen elements appropriately\n- Clean up event listeners\n- Handle errors gracefully\n\n### DON'T ❌\n- Assume fullscreen API is always available\n- Force fullscreen without user interaction\n- Forget to remove event listeners\n- Ignore mobile browser limitations\n- Use fullscreen for sensitive actions\n\n## Browser Compatibility\n\n| Browser | Support |\n|---------|---------|\n| Chrome | ✅ Full support |\n| Firefox | ✅ Full support |\n| Safari | ✅ Full support (iOS limited) |\n| Edge | ✅ Full support |\n| iOS Safari | ⚠️ Limited (video only) |\n| Android Chrome | ✅ Full support |\n\n## References\n\n- [screenfull.js GitHub](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fscreenfull.js)\n- [Fullscreen API MDN](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FFullscreen_API)\n- [Can I Use Fullscreen](https:\u002F\u002Fcaniuse.com\u002Ffullscreen)\n\n---\n\n**Version**: 1.0.0  \n**Compatible with**: screenfull 6.0.x  \n**Last Updated**: 2025-12-25\n",{"name":5,"license":14,"description":7},"master","08cc2532077058aa50e6e18f54695bc256c88152","skills\u002F7spade\u002Fscreenfull-fullscreen-api","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub","https:\u002F\u002Fgithub.com\u002Fsuperhealth","java engineer","2026-04-06 22:07:23",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add superhealth\u002Fskills-hub --skill screenfull-fullscreen-api",{"tool":57,"command":58},"bunx","bunx skills add superhealth\u002Fskills-hub --skill screenfull-fullscreen-api",{"tool":60,"command":61},"pnpm","pnpm dlx skills add superhealth\u002Fskills-hub --skill screenfull-fullscreen-api","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fscreenfull-fullscreen-api\u002FSKILL.md",true,[65],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},"skill-report.json","REFERENCE","参考文档",11935,"https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fscreenfull-fullscreen-api\u002Fskill-report.json",[72,102,129,142,158,175],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":81,"publishedAt":39},"01M6MC9F3WHV8S8FZ2W4TRB0FE","vercel-react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fzhanlincui\u002Fvercel-react-best-practices\u002FSKILL.md",[78],{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101],"react","vercel react best practices","best practices","vercel","react best practices","data fetching","code","triggers","pages","next","patterns","refactoring","data","Optimization","tasks","guidelines","react components","components","performance","writing",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":112,"publishedAt":39},"01M6MB8KWE1Q5A5KERR7RFTQWP","vue-testing-best-practices","Use for Vue.js testing. Covers Vitest, Vue Test Utils, component testing, mocking, testing patterns, and Playwright for E2E testing.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-testing-best-practices\u002FSKILL.md",[108,111],{"categoryNo":109,"categoryName":110,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},[113,114,115,84,116,117,118,119,120,121,122,92,123,124,38,125,126,127,128],"vue testing best practices","testing best practices","vue","testing","testing patterns","js","e2e","vitest","use","component","test","mocking","utils","e2e testing","github","playwright",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":136,"publishedAt":39},"01M6MB8GK06M6GA4KVYNDV1BZP","vue-router-best-practices","Vue Router 4 patterns, navigation guards, route params, and route-component lifecycle interactions.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-router-best-practices\u002FSKILL.md",[135],{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},[84,115,137,138,139,92,140,141,122,127],"router","vue router best practices","route","navigation","lifecycle",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":149,"publishedAt":39},"01M6MB8DQF21TZMQ9XC3745ERW","vue-pinia-best-practices","Pinia stores, state management patterns, store setup, and reactivity with stores.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-pinia-best-practices\u002FSKILL.md",[148],{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},[150,151,115,84,152,153,92,154,155,156,157,127],"vue pinia best practices","pinia","with","store","management","state","state management","setup",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":167,"publishedAt":39},"01M6MB8A7MC08747J9TV4GWQ9A","vue-options-api-best-practices","Vue 3 Options API style (data(), methods, this context). Each reference shows Options API solution only.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-options-api-best-practices\u002FSKILL.md",[164,165,166],{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":23,"categoryName":24,"taxonomyType":25,"taxonomyTypeDesc":26},[115,168,169,28,84,170,171,172,173,94,127,174],"options","vue options api best practices","methods","reference","only","context","style",{"skillNo":176,"name":177,"nameZh":6,"description":178,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":179,"categories":180,"keywords":182,"publishedAt":39},"01M6MB85ZTQC24Q04X49MM4YXN","vue-development-guides","A collection of best practices and tips for developing applications using Vue.js. This skill MUST be apply when developing, refactoring or reviewing Vue.js or Nuxt projects.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-development-guides\u002FSKILL.md",[181],{"categoryNo":79,"categoryName":80,"taxonomyType":20,"taxonomyTypeDesc":21},[183,115,184,185,127,186,187,38,34,188,189,118,93,190,191,84],"development","guides","vue development guides","tips","apply","nuxt","projects","collection","applications"]