Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement fullscreen functionality using screenfull library for cross-browser fullscreen support in ng-events project
.claude/skills/aiskillstore-screenfull-fullscreen-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 33% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 106% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 211% | 0% |
This skill guides the implementation of fullscreen functionality using the screenfull library in the ng-events construction site management system.
Triggers: "fullscreen", "screenfull", "full screen mode", "toggle fullscreen", "exit fullscreen"
Use this skill when:
bash# Already installed in package.json yarn add screenfull@^6.0.2
typescriptimport { Component, inject, signal } from '@angular/core'; import screenfull from 'screenfull'; @Component({ selector: 'app-fullscreen-toggle', standalone: true, template: ` <button (click)="toggleFullscreen()" class="fullscreen-btn"> @if (isFullscreen()) { <i nz-icon nzType="fullscreen-exit" nzTheme="outline"></i> Exit Fullscreen } @else { <i nz-icon nzType="fullscreen" nzTheme="outline"></i> Enter Fullscreen } </button> ` }) export class FullscreenToggleComponent { isFullscreen = signal(false); toggleFullscreen(): void { if (screenfull.isEnabled) { screenfull.toggle(); this.updateFullscreenState(); // Listen for fullscreen changes screenfull.on('change', () => { this.updateFullscreenState(); }); } else { console.warn('Fullscreen API not supported'); } } private updateFullscreenState(): void { this.isFullscreen.set(screenfull.isFullscreen); } }
typescriptimport { Component, ElementRef, ViewChild, signal } from '@angular/core'; import screenfull from 'screenfull'; @Component({ selector: 'app-dashboard-fullscreen', standalone: true, template: ` <div #dashboardContainer class="dashboard-container"> <div class="dashboard-header"> <h2>Construction Progress Dashboard</h2> <button (click)="toggleFullscreen()" class="btn-fullscreen"> @if (isFullscreen()) { <i nz-icon nzType="fullscreen-exit"></i> } @else { <i nz-icon nzType="fullscreen"></i> } </button> </div> <div class="dashboard-content"> <app-progress-charts /> <app-task-summary /> <app-recent-activities /> </div> </div> `, styles: [` .dashboard-container { position: relative; background: white; padding: 20px; } .dashboard-container:-webkit-full-screen { background: #1f1f1f; color: white; } .dashboard-container:fullscreen { background: #1f1f1f; color: white; } .btn-fullscreen { position: absolute; top: 20px; right: 20px; } `] }) export class DashboardFullscreenComponent { @ViewChild('dashboardContainer', { static: true }) container!: ElementRef; isFullscreen = signal(false); toggleFullscreen(): void { if (screenfull.isEnabled) { screenfull.toggle(this.container.nativeElement); screenfull.on('change', () => { this.isFullscreen.set(screenfull.isFullscreen); }); } } }
typescriptimport { Component, signal } from '@angular/core'; import screenfull from 'screenfull'; import { NzImageModule } from 'ng-zorro-antd/image'; @Component({ selector: 'app-construction-photos', standalone: true, imports: [NzImageModule], template: ` <div class="photo-gallery"> <h3>Construction Site Photos</h3> <div class="photo-grid"> @for (photo of photos(); track photo.id) { <div class="photo-item" (click)="viewFullscreen(photo)"> <img [src]="photo.thumbnailUrl" [alt]="photo.title" /> <div class="photo-overlay"> <i nz-icon nzType="fullscreen" nzTheme="outline"></i> </div> </div> } </div> </div> @if (currentPhoto()) { <div class="fullscreen-viewer" #viewer> <button class="close-btn" (click)="exitFullscreen()"> <i nz-icon nzType="close"></i> </button> <img [src]="currentPhoto()!.fullUrl" [alt]="currentPhoto()!.title" /> <div class="photo-info"> <h4>{{ currentPhoto()!.title }}</h4> <p>{{ currentPhoto()!.description }}</p> <span>{{ currentPhoto()!.date | date }}</span> </div> </div> } `, styles: [` .photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; } .photo-item { position: relative; cursor: pointer; overflow: hidden; border-radius: 4px; } .photo-item img { width: 100%; height: 200px; object-fit: cover; transition: transform 0.3s; } .photo-item:hover img { transform: scale(1.1); } .photo-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s; } .photo-item:hover .photo-overlay { opacity: 1; } .fullscreen-viewer { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.95); display: flex; align-items: center; justify-content: center; z-index: 9999; } .fullscreen-viewer img { max-width: 90%; max-height: 90%; object-fit: contain; } .close-btn { position: absolute; top: 20px; right: 20px; background: rgba(255, 255, 255, 0.2); border: none; color: white; font-size: 24px; cursor: pointer; padding: 10px; border-radius: 4px; } .photo-info { position: absolute; bottom: 20px; left: 20px; color: white; text-align: left; } `] }) export class ConstructionPhotosComponent { photos = signal([ { id: '1', title: 'Foundation Work', description: 'Foundation concrete pouring', thumbnailUrl: 'assets/photos/thumb1.jpg', fullUrl: 'assets/photos/full1.jpg', date: new Date() } // ... more photos ]); currentPhoto = signal<Photo | null>(null); viewFullscreen(photo: Photo): void { this.currentPhoto.set(photo); if (screenfull.isEnabled) { const viewer = document.querySelector('.fullscreen-viewer'); if (viewer) { screenfull.request(viewer as HTMLElement); screenfull.on('change', () => { if (!screenfull.isFullscreen) { this.currentPhoto.set(null); } }); } } } exitFullscreen(): void { if (screenfull.isEnabled && screenfull.isFullscreen) { screenfull.exit(); } this.currentPhoto.set(null); } }
typescriptimport { Component, signal, OnInit, OnDestroy } from '@angular/core'; import screenfull from 'screenfull'; @Component({ selector: 'app-presentation-mode', standalone: true, template: ` <div class="presentation-container" [class.presentation-active]="isPresenting()"> <div class="presentation-header"> <h2>{{ currentSlide().title }}</h2> <div class="controls"> <button (click)="previousSlide()" [disabled]="currentSlideIndex() === 0"> <i nz-icon nzType="left"></i> Previous </button> <span>{{ currentSlideIndex() + 1 }} / {{ slides().length }}</span> <button (click)="nextSlide()" [disabled]="currentSlideIndex() === slides().length - 1"> Next <i nz-icon nzType="right"></i> </button> <button (click)="togglePresentation()" class="btn-present"> @if (isPresenting()) { <i nz-icon nzType="fullscreen-exit"></i> Exit } @else { <i nz-icon nzType="fullscreen"></i> Present } </button> </div> </div> <div class="slide-content"> <div [innerHTML]="currentSlide().content"></div> </div> </div> `, styles: [` .presentation-container { background: white; padding: 20px; } .presentation-active { background: #1a1a1a; color: white; padding: 40px; } .presentation-active .slide-content { font-size: 1.5em; } `] }) export class PresentationModeComponent implements OnInit, OnDestroy { slides = signal([ { title: 'Project Overview', content: '<h1>Construction Site Progress</h1><p>Q4 2025 Update</p>' }, { title: 'Milestones Achieved', content: '<ul><li>Foundation Complete</li><li>Structural Work 75%</li></ul>' } // ... more slides ]); currentSlideIndex = signal(0); isPresenting = signal(false); currentSlide = computed(() => this.slides()[this.currentSlideIndex()]); ngOnInit(): void { // Listen for keyboard shortcuts document.addEventListener('keydown', this.handleKeyPress.bind(this)); } ngOnDestroy(): void { document.removeEventListener('keydown', this.handleKeyPress.bind(this)); if (screenfull.isEnabled) { screenfull.off('change'); } } togglePresentation(): void { if (screenfull.isEnabled) { screenfull.toggle(); screenfull.on('change', () => { this.isPresenting.set(screenfull.isFullscreen); }); } } nextSlide(): void { if (this.currentSlideIndex() < this.slides().length - 1) { this.currentSlideIndex.update(i => i + 1); } } previousSlide(): void { if (this.currentSlideIndex() > 0) { this.currentSlideIndex.update(i => i - 1); } } private handleKeyPress(event: KeyboardEvent): void { if (!this.isPresenting()) return; switch (event.key) { case 'ArrowRight': case 'PageDown': this.nextSlide(); break; case 'ArrowLeft': case 'PageUp': this.previousSlide(); break; case 'Escape': if (screenfull.isEnabled && screenfull.isFullscreen) { screenfull.exit(); } break; } } }
typescript// Check if fullscreen API is supported screenfull.isEnabled // boolean // Toggle fullscreen screenfull.toggle() // Toggle document screenfull.toggle(element) // Toggle specific element // Request fullscreen screenfull.request() // Request for document screenfull.request(element) // Request for specific element // Exit fullscreen screenfull.exit() // Check if currently fullscreen screenfull.isFullscreen // boolean // Get fullscreen element screenfull.element // Element | null // Event listeners screenfull.on('change', () => {}) screenfull.on('error', (event) => {}) screenfull.off('change', handler)
When using screenfull:
screenfull.isEnabled before using APIscreenfull.isEnabled before use| Browser | Support | |---------|---------| | Chrome | ✅ Full support | | Firefox | ✅ Full support | | Safari | ✅ Full support (iOS limited) | | Edge | ✅ Full support | | iOS Safari | ⚠️ Limited (video only) | | Android Chrome | ✅ Full support |
Version: 1.0.0 Compatible with: screenfull 6.0.x Last Updated: 2025-12-25
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→pass | 12,820 | 16,407 | +28% | 1 | 1 | 0% | 2,614 | 5,274 | +102% | 0 | 0 | — |
case-01 | fail→pass | 23,156 | 13,533 | -42% | 1 | 1 | 0% | 4,921 | 6,556 | +33% | 0 | 0 | — |
case-02 | fail→pass | 21,088 | 24,029 | +14% | 1 | 1 | 0% | 4,255 | 8,777 | +106% | 0 | 0 | — |
case-03 | fail→pass | 28,340 | 27,925 | -1% | 1 | 1 | 0% | 6,183 | 9,806 | +59% | 0 | 0 | — |
case-04 | fail→pass | 9,347 | 10,513 | +12% | 1 | 1 | 0% | 1,885 | 5,871 | +211% | 0 | 0 | — |
case-05 | fail→pass | 9,534 | 6,714 | -30% | 1 | 1 | 0% | 1,744 | 4,880 | +180% | 0 | 0 | — |
case-06 | fail→pass | 13,551 | 12,978 | -4% | 1 | 1 | 0% | 2,366 | 6,129 | +159% | 0 | 0 | — |
case-07 | fail→pass | 11,432 | 10,131 | -11% | 1 | 1 | 0% | 1,919 | 5,499 | +187% | 0 | 0 | — |
case-08 | pass→pass | 10,948 | 9,352 | -15% | 1 | 1 | 0% | 1,968 | 5,449 | +177% | 0 | 0 | — |
case-09 | fail→pass | 10,501 | 9,132 | -13% | 1 | 1 | 0% | 2,080 | 5,297 | +155% | 0 | 0 | — |
case-11 | fail→pass | 9,279 | 6,691 | -28% | 1 | 1 | 0% | 1,785 | 4,991 | +180% | 0 | 0 | — |
case-12 | fail→pass | 13,518 | 11,842 | -12% | 1 | 1 | 0% | 2,574 | 6,017 | +134% | 0 | 0 | — |
case-13 | fail→pass | 13,282 | 12,660 | -5% | 1 | 1 | 0% | 2,758 | 6,296 | +128% | 0 | 0 | — |
case-14 | fail→pass | 11,524 | 9,242 | -20% | 1 | 1 | 0% | 2,187 | 5,263 | +141% | 0 | 0 | — |
case-15 | fail→pass | 9,688 | 5,676 | -41% | 1 | 1 | 0% | 1,745 | 4,634 | +166% | 0 | 0 | — |
case-16 | fail→pass | 14,361 | 13,663 | -5% | 1 | 1 | 0% | 2,701 | 6,556 | +143% | 0 | 0 | — |
case-17 | fail→pass | 15,183 | 12,199 | -20% | 1 | 1 | 0% | 2,775 | 6,056 | +118% | 0 | 0 | — |
case-18 | fail→pass | 12,236 | 9,018 | -26% | 1 | 1 | 0% | 2,225 | 5,307 | +139% | 0 | 0 | — |
case-19 | fail→pass | 27,817 | 8,486 | -69% | 1 | 1 | 0% | 2,459 | 5,227 | +113% | 0 | 0 | — |
case-20 | pass→pass | 19,677 | 23,930 | +22% | 1 | 1 | 0% | 3,997 | 8,267 | +107% | 0 | 0 | — |
case-21 | pass→fail | 30,294 | 18,323 | -40% | 1 | 1 | 0% | 4,450 | 7,571 | +70% | 0 | 0 | — |
case-22 | pass→fail | 27,803 | 27,189 | -2% | 1 | 1 | 0% | 6,173 | 9,729 | +58% | 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. 2 cases got worse with the skill loaded, and they are included in that figure.
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.