Repository: bitwarden/clients
Pull request #14705: [PM-18804] generator nudges
Author: login: Jingo88
Created: 2025-05-09T15:14:20Z | Merged: 2025-05-21T17:46:02Z

## 🎟️ Tracking

[PM-18804](https://bitwarden.atlassian.net/browse/PM-18804)

## 📔 Objective

- Add a spotlight component to the generator browser page
- User can dismiss spotlight, and it will stay dismissed
- Added aria to spotlight body
- Updates to copy and button styles on Send page
- Send page updates are behind feature flag

## 📸 Screenshots

Generator Spotlight Dismiss

https://github.com/user-attachments/assets/feff123e-d436-4f5d-adbc-53d4c93877ac


With Flag On
![PM-18804-send-yes-flag](https://github.com/user-attachments/assets/b608d899-d827-4fdc-a280-2fed1e86bbe6)

With Flag Off
![PM-18804-send-no-flag](https://github.com/user-attachments/assets/4285d879-f199-4e68-860c-67ddf3ad56c6)


## ⏰ Reminders before review

- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

## 🦮 Reviewer guidelines

<!-- Suggested interactions but feel free to use (or not) as you desire! -->

- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt
- ⛏ (`:pick:`) for minor or nitpick changes


[PM-18804]: https://bitwarden.atlassian.net/browse/PM-18804?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/.storybook/main.ts b/.storybook/main.ts
index 9583d1fc6f21..d5d116e99be0 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -22,6 +22,7 @@ const config: StorybookConfig = {
     "../bitwarden_license/bit-web/src/**/*.stories.@(js|jsx|ts|tsx)",
     "../libs/tools/card/src/**/*.mdx",
     "../libs/tools/card/src/**/*.stories.@(js|jsx|ts|tsx)",
+    "../libs/angular/src/**/*.stories.@(js|jsx|ts|tsx)",
   ],
   addons: [
     getAbsolutePath("@storybook/addon-links"),
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 3a5583f5468a..b4e092ee631c 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -3615,6 +3615,14 @@
     "message": "Use Send to securely share encrypted information with anyone.",
     "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
   },
+  "sendsTitleNoItems": {
+    "message": "Send sensitive information safely",
+    "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
+  },
+  "sendsBodyNoItems": {
+    "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.",
+    "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
+  },
   "inputRequired": {
     "message": "Input is required."
   },
@@ -5341,6 +5349,23 @@
     "description": "Two part message",
     "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent"
   },
+  "generatorNudgeTitle": {
+    "message": "Quickly create passwords"
+  },
+  "generatorNudgeBodyOne": {
+    "message": "Easily create strong and unique passwords by clicking on",
+    "description": "Two part message",
+    "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
+  },
+  "generatorNudgeBodyTwo": {
+    "message": "to help you keep your logins secure.",
+    "description": "Two part message",
+    "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure."
+  },
+  "generatorNudgeBodyAria": {
+    "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.",
+    "description": "Aria label for the body content of the generator nudge"
+  },
   "noPermissionsViewPage": {
     "message": "You do not have permissions to view this page. Try logging in with a different account."
   }
diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts
index 0605f5a03a60..e1a5a2fc2186 100644
--- a/apps/browser/src/autofill/popup/settings/autofill.component.ts
+++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts
@@ -15,6 +15,7 @@ import { filter, firstValueFrom, Observable, switchMap } from "rxjs";
 
 import { JslibModule } from "@bitwarden/angular/jslib.module";
 import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
+import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
 import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
 import { getUserId } from "@bitwarden/common/auth/services/account.service";
 import {
@@ -56,7 +57,6 @@ import {
   SelectModule,
   TypographyModule,
 } from "@bitwarden/components";
-import { SpotlightComponent } from "@bitwarden/vault";
 
 import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service";
 import { BrowserApi } from "../../../platform/browser/browser-api";
diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.html b/apps/browser/src/tools/popup/send-v2/send-v2.component.html
index d51bda45b551..d271f67fa3bd 100644
--- a/apps/browser/src/tools/popup/send-v2/send-v2.component.html
+++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.html
@@ -20,15 +20,29 @@
     *ngIf="listState === sendState.Empty"
     class="tw-flex tw-flex-col tw-h-full tw-justify-center"
   >
-    <bit-no-items [icon]="noItemIcon" class="tw-text-main">
-      <ng-container slot="title">{{ "sendsNoItemsTitle" | i18n }}</ng-container>
-      <ng-container slot="description">{{ "sendsNoItemsMessage" | i18n }}</ng-container>
-      <tools-new-send-dropdown
-        [hideIcon]="true"
-        *ngIf="!sendsDisabled"
-        slot="button"
-      ></tools-new-send-dropdown>
-    </bit-no-items>
+    <ng-container *ngIf="!(showSendSpotlight$ | async)">
+      <bit-no-items [icon]="noItemIcon" class="tw-text-main">
+        <ng-container slot="title">{{ "sendsNoItemsTitle" | i18n }}</ng-container>
+        <ng-container slot="description">{{ "sendsNoItemsMessage" | i18n }}</ng-container>
+        <tools-new-send-dropdown
+          [hideIcon]="true"
+          *ngIf="!sendsDisabled"
+          slot="button"
+        ></tools-new-send-dropdown>
+      </bit-no-items>
+    </ng-container>
+    <ng-container *ngIf="showSendSpotlight$ | async">
+      <bit-no-items [icon]="noItemIcon" class="tw-text-main">
+        <ng-container slot="title">{{ "sendsTitleNoItems" | i18n }}</ng-container>
+        <ng-container slot="description">{{ "sendsBodyNoItems" | i18n }}</ng-container>
+        <tools-new-send-dropdown
+          [hideIcon]="true"
+          *ngIf="!sendsDisabled"
+          slot="button"
+          [buttonType]="'secondary'"
+        ></tools-new-send-dropdown>
+      </bit-no-items>
+    </ng-container>
   </div>
 
   <ng-container *ngIf="listState !== sendState.Empty">
diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts
index 6fc4793f5c01..c1f8e9fb2630 100644
--- a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts
+++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts
@@ -6,6 +6,7 @@ import { MockProxy, mock } from "jest-mock-extended";
 import { of, BehaviorSubject } from "rxjs";
 
 import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { NudgesService } from "@bitwarden/angular/vault";
 import { SearchService } from "@bitwarden/common/abstractions/search.service";
 import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
 import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -121,6 +122,7 @@ describe("SendV2Component", () => {
         { provide: SendListFiltersService, useValue: sendListFiltersService },
         { provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
         { provide: PolicyService, useValue: policyService },
+        { provide: NudgesService, useValue: mock<NudgesService>() },
       ],
     }).compileComponents();
 
diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts
index def425a51a54..9fc19e98b34c 100644
--- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts
+++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts
@@ -1,10 +1,10 @@
 import { CommonModule } from "@angular/common";
-import { Component, OnDestroy, OnInit } from "@angular/core";
+import { Component, OnDestroy } from "@angular/core";
 import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { RouterLink } from "@angular/router";
-import { combineLatest, switchMap } from "rxjs";
+import { combineLatest, Observable, switchMap } from "rxjs";
 
 import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
 import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
 import { PolicyType } from "@bitwarden/common/admin-console/enums";
 import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -12,13 +12,13 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service";
 import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
 import { ButtonModule, CalloutModule, Icons, NoItemsModule } from "@bitwarden/components";
 import {
-  NoSendsIcon,
   NewSendDropdownComponent,
-  SendListItemsContainerComponent,
+  NoSendsIcon,
   SendItemsService,
-  SendSearchComponent,
   SendListFiltersComponent,
   SendListFiltersService,
+  SendListItemsContainerComponent,
+  SendSearchComponent,
 } from "@bitwarden/send-ui";
 
 import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component";
@@ -46,14 +46,13 @@ export enum SendState {
     JslibModule,
     CommonModule,
     ButtonModule,
-    RouterLink,
     NewSendDropdownComponent,
     SendListItemsContainerComponent,
     SendListFiltersComponent,
     SendSearchComponent,
   ],
 })
-export class SendV2Component implements OnInit, OnDestroy {
+export class SendV2Component implements OnDestroy {
   sendType = SendType;
   sendState = SendState;
 
@@ -63,6 +62,12 @@ export class SendV2Component implements OnInit, OnDestroy {
   protected title: string = "allSends";
   protected noItemIcon = NoSendsIcon;
   protected noResultsIcon = Icons.NoResults;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showSendSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.nudgesService.showNudgeSpotlight$(NudgeType.SendNudgeStatus, userId),
+    ),
+  );
 
   protected sendsDisabled = false;
 
@@ -71,6 +76,7 @@ export class SendV2Component implements OnInit, OnDestroy {
     protected sendListFiltersService: SendListFiltersService,
     private policyService: PolicyService,
     private accountService: AccountService,
+    private nudgesService: NudgesService,
   ) {
     combineLatest([
       this.sendItemsService.emptyList$,
@@ -111,7 +117,5 @@ export class SendV2Component implements OnInit, OnDestroy {
       });
   }
 
-  ngOnInit(): void {}
-
   ngOnDestroy(): void {}
 }
diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts
index 474524cf883a..db853d45940a 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts
@@ -17,10 +17,10 @@ import {
 
 import { JslibModule } from "@bitwarden/angular/jslib.module";
 import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
+import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
 import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
 import { getUserId } from "@bitwarden/common/auth/services/account.service";
 import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
 import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
 import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
 import { CipherType } from "@bitwarden/common/vault/enums";
@@ -31,7 +31,7 @@ import {
   NoItemsModule,
   TypographyModule,
 } from "@bitwarden/components";
-import { DecryptionFailureDialogComponent, SpotlightComponent, VaultIcons } from "@bitwarden/vault";
+import { DecryptionFailureDialogComponent, VaultIcons } from "@bitwarden/vault";
 
 import { CurrentAccountComponent } from "../../../../auth/popup/account-switching/current-account.component";
 import { BrowserApi } from "../../../../platform/browser/browser-api";
@@ -154,7 +154,6 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy {
     private introCarouselService: IntroCarouselService,
     private nudgesService: NudgesService,
     private router: Router,
-    private i18nService: I18nService,
   ) {
     combineLatest([
       this.vaultPopupItemsService.emptyVault$,
diff --git a/libs/vault/src/components/spotlight/spotlight.component.html b/libs/angular/src/vault/components/spotlight/spotlight.component.html
similarity index 100%
rename from libs/vault/src/components/spotlight/spotlight.component.html
rename to libs/angular/src/vault/components/spotlight/spotlight.component.html
diff --git a/libs/vault/src/components/spotlight/spotlight.component.ts b/libs/angular/src/vault/components/spotlight/spotlight.component.ts
similarity index 100%
rename from libs/vault/src/components/spotlight/spotlight.component.ts
rename to libs/angular/src/vault/components/spotlight/spotlight.component.ts
diff --git a/libs/vault/src/components/spotlight/spotlight.stories.ts b/libs/angular/src/vault/components/spotlight/spotlight.stories.ts
similarity index 100%
rename from libs/vault/src/components/spotlight/spotlight.stories.ts
rename to libs/angular/src/vault/components/spotlight/spotlight.stories.ts
diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts
index 6784e9c7b4e3..49736f7a300b 100644
--- a/libs/angular/src/vault/services/nudges.service.ts
+++ b/libs/angular/src/vault/services/nudges.service.ts
@@ -38,6 +38,8 @@ export enum NudgeType {
   NewIdentityItemStatus = "new-identity-item-status",
   NewNoteItemStatus = "new-note-item-status",
   NewSshItemStatus = "new-ssh-item-status",
+  GeneratorNudgeStatus = "generator-nudge-status",
+  SendNudgeStatus = "send-nudge-status",
 }
 
 export const NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition<
diff --git a/libs/tools/generator/components/src/credential-generator.component.html b/libs/tools/generator/components/src/credential-generator.component.html
index e95df388f397..241469684562 100644
--- a/libs/tools/generator/components/src/credential-generator.component.html
+++ b/libs/tools/generator/components/src/credential-generator.component.html
@@ -11,6 +11,8 @@
   </bit-toggle>
 </bit-toggle-group>
 
+<nudge-generator-spotlight></nudge-generator-spotlight>
+
 <bit-card class="tw-flex tw-justify-between tw-mb-4">
   <div class="tw-grow tw-flex tw-items-center">
     <bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password>
diff --git a/libs/tools/generator/components/src/generator.module.ts b/libs/tools/generator/components/src/generator.module.ts
index f0d09b53ebb1..d710f368106e 100644
--- a/libs/tools/generator/components/src/generator.module.ts
+++ b/libs/tools/generator/components/src/generator.module.ts
@@ -22,6 +22,7 @@ import { CatchallSettingsComponent } from "./catchall-settings.component";
 import { CredentialGeneratorComponent } from "./credential-generator.component";
 import { ForwarderSettingsComponent } from "./forwarder-settings.component";
 import { GeneratorServicesModule } from "./generator-services.module";
+import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
 import { PassphraseSettingsComponent } from "./passphrase-settings.component";
 import { PasswordGeneratorComponent } from "./password-generator.component";
 import { PasswordSettingsComponent } from "./password-settings.component";
@@ -48,6 +49,7 @@ import { UsernameSettingsComponent } from "./username-settings.component";
     SelectModule,
     ToggleGroupModule,
     TypographyModule,
+    NudgeGeneratorSpotlightComponent,
   ],
   declarations: [
     CatchallSettingsComponent,
diff --git a/libs/tools/generator/components/src/nudge-generator-spotlight.component.html b/libs/tools/generator/components/src/nudge-generator-spotlight.component.html
new file mode 100644
index 000000000000..9c65a1cea96f
--- /dev/null
+++ b/libs/tools/generator/components/src/nudge-generator-spotlight.component.html
@@ -0,0 +1,15 @@
+<div class="tw-mb-4" *ngIf="showGeneratorSpotlight$ | async">
+  <bit-spotlight
+    [title]="'generatorNudgeTitle' | i18n"
+    (onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)"
+  >
+    <p
+      class="tw-text-main"
+      bitTypography="body2"
+      [attr.aria-label]="'generatorNudgeIconAria' | i18n"
+    >
+      {{ "generatorNudgeBodyOne" | i18n }} <i class="bwi bwi-generate"></i>
+      {{ "generatorNudgeBodyTwo" | i18n }}
+    </p>
+  </bit-spotlight>
+</div>
diff --git a/libs/tools/generator/components/src/nudge-generator-spotlight.component.ts b/libs/tools/generator/components/src/nudge-generator-spotlight.component.ts
new file mode 100644
index 000000000000..a0008bac7824
--- /dev/null
+++ b/libs/tools/generator/components/src/nudge-generator-spotlight.component.ts
@@ -0,0 +1,38 @@
+import { AsyncPipe, CommonModule } from "@angular/common";
+import { Component } from "@angular/core";
+import { firstValueFrom, Observable, switchMap } from "rxjs";
+
+import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
+import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { getUserId } from "@bitwarden/common/auth/services/account.service";
+import { UserId } from "@bitwarden/common/types/guid";
+import { TypographyModule } from "@bitwarden/components";
+import { I18nPipe } from "@bitwarden/ui-common";
+
+@Component({
+  standalone: true,
+  selector: "nudge-generator-spotlight",
+  templateUrl: "nudge-generator-spotlight.component.html",
+  imports: [I18nPipe, SpotlightComponent, AsyncPipe, CommonModule, TypographyModule],
+})
+export class NudgeGeneratorSpotlightComponent {
+  protected readonly NudgeType = NudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.nudgesService.showNudgeSpotlight$(NudgeType.GeneratorNudgeStatus, userId),
+    ),
+  );
+
+  constructor(
+    private nudgesService: NudgesService,
+    private accountService: AccountService,
+  ) {}
+
+  async dismissGeneratorSpotlight(type: NudgeType) {
+    const activeUserId = await firstValueFrom(this.activeUserId$);
+
+    await this.nudgesService.dismissNudge(type, activeUserId as UserId);
+  }
+}
diff --git a/libs/tools/generator/components/tsconfig.json b/libs/tools/generator/components/tsconfig.json
index e0e4da268daf..9a3a08b40fc8 100644
--- a/libs/tools/generator/components/tsconfig.json
+++ b/libs/tools/generator/components/tsconfig.json
@@ -11,7 +11,8 @@
       "@bitwarden/generator-history": ["../../../tools/generator/extensions/history/src"],
       "@bitwarden/key-management": ["../../../key-management/src"],
       "@bitwarden/platform": ["../../../platform/src"],
-      "@bitwarden/ui-common": ["../../../ui/common/src"]
+      "@bitwarden/ui-common": ["../../../ui/common/src"],
+      "@bitwarden/vault": ["../../../vault/src"]
     }
   },
   "include": ["src"],
diff --git a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html
index ef8b28aba337..c62e646540b3 100644
--- a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html
+++ b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html
@@ -1,4 +1,10 @@
-<button bitButton size="small" [bitMenuTriggerFor]="itemOptions" buttonType="primary" type="button">
+<button
+  bitButton
+  size="small"
+  [bitMenuTriggerFor]="itemOptions"
+  [buttonType]="buttonType"
+  type="button"
+>
   <i *ngIf="!hideIcon" class="bwi bwi-plus" aria-hidden="true"></i>
   {{ (hideIcon ? "createSend" : "new") | i18n }}
 </button>
diff --git a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts
index 19f9d3a174a7..6563789f1c7d 100644
--- a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts
+++ b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.ts
@@ -7,7 +7,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
 import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
 import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
 import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
-import { BadgeModule, ButtonModule, MenuModule } from "@bitwarden/components";
+import { BadgeModule, ButtonModule, ButtonType, MenuModule } from "@bitwarden/components";
 
 @Component({
   selector: "tools-new-send-dropdown",
@@ -17,6 +17,7 @@ import { BadgeModule, ButtonModule, MenuModule } from "@bitwarden/components";
 })
 export class NewSendDropdownComponent implements OnInit {
   @Input() hideIcon: boolean = false;
+  @Input() buttonType: ButtonType = "primary";
 
   sendType = SendType;
 
diff --git a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts
index 6781cafcbb21..705b98f241a1 100644
--- a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts
+++ b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts
@@ -3,14 +3,13 @@ import { Component, Input, OnInit } from "@angular/core";
 import { firstValueFrom } from "rxjs";
 
 import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
+import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
 import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
 import { getUserId } from "@bitwarden/common/auth/services/account.service";
 import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
 import { UserId } from "@bitwarden/common/types/guid";
 import { CipherType } from "@bitwarden/sdk-internal";
 
-import { SpotlightComponent } from "../../../components/spotlight/spotlight.component";
-
 @Component({
   selector: "vault-new-item-nudge",
   templateUrl: "./new-item-nudge.component.html",
diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts
index 9d19e4c239d2..b39bb85ab304 100644
--- a/libs/vault/src/index.ts
+++ b/libs/vault/src/index.ts
@@ -27,5 +27,3 @@ export { SshImportPromptService } from "./services/ssh-import-prompt.service";
 
 export * from "./abstractions/change-login-password.service";
 export * from "./services/default-change-login-password.service";
-
-export { SpotlightComponent } from "./components/spotlight/spotlight.component";


Reviews:
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T15:34:10Z
user: login: nick-livefront
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-13T20:04:09Z
user: login: gbubemismith
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T20:22:24Z
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T21:19:17Z
user: login: audreyality
body: 📝 The requested change is marked with an ❌.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-14T13:10:12Z
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:58:30Z
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:58:35Z
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:58:42Z
user: login: Jingo88
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:59:02Z
user: login: audreyality
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T17:04:36Z
user: login: nick-livefront
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-19T20:46:43Z
user: login: audreyality
body: 👍🏻 Thanks for addressing my feedback!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-19T21:23:39Z
user: login: willmartian
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T12:44:24Z
user: login: jprusik
body: Approved for Autofill concerns
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T14:13:14Z
user: login: tangowithfoxtrot
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:24:06Z
user: login: nick-livefront
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-21T13:50:53Z
user: login: shane-melton
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-21T16:55:04Z

Review comments:
diff_hunk: @@ -109,7 +113,11 @@ export class SendV2Component implements OnInit, OnDestroy {
       });
   }
 
-  ngOnInit(): void {}
+  async ngOnInit(): Promise<void> {
+    this.onboardingNudgesFlag = await this.configService.getFeatureFlag(
path: apps/browser/src/tools/popup/send-v2/send-v2.component.ts
user: login: Jingo88
body: I know there were some discussions on recent PR's about code churn regarding feature flags. I'm not aware if there is another way in this instance for differentiating copy and styling changes. 
created_at: 2025-05-09T15:34:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +32,40 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent implements OnInit {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  protected generatorSpotlightTitle: string = "";
+  protected generatorSpotlightBody: string = "";
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
+  );
+
+  constructor(
+    private vaultNudgesService: VaultNudgesService,
+    private i18nService: I18nService,
+    private accountService: AccountService,
+  ) {}
+
+  async ngOnInit() {
+    const nudgeBodyOne = this.i18nService.t("generatorNudgeBodyOne");
+    const nudgeBodyTwo = this.i18nService.t("generatorNudgeBodyTwo");
+    const nudgeBodyAria = this.i18nService.t("generatorNudgeIconAria");
+    this.generatorSpotlightTitle = this.i18nService.t("generatorNudgeTitle");
+    this.generatorSpotlightBody = `<span aria-label="${nudgeBodyAria}">${nudgeBodyOne} <i class="bwi bwi-generate"></i> ${nudgeBodyTwo}</span>`;
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: nick-livefront
body: ❓ Do these need to be in the component? Can they be defined int he template?
created_at: 2025-05-13T20:01:01Z
author_association: COLLABORATOR
diff_hunk: @@ -34,6 +34,7 @@ export enum VaultNudgeType {
   newIdentityItemStatus = "new-identity-item-status",
   newNoteItemStatus = "new-note-item-status",
   newSshItemStatus = "new-ssh-item-status",
+  GeneratorNudgeStatus = "generator-nudge-status",
path: libs/vault/src/services/vault-nudges.service.ts
user: login: gbubemismith
body: 🎨 Noticed some inconsistent naming conventions (i.e pascalCase, camelCase) in the `VaultNudgeType` enum members. Can you rename to use a consistent style for all members?  
created_at: 2025-05-13T20:15:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,7 +113,11 @@ export class SendV2Component implements OnInit, OnDestroy {
       });
   }
 
-  ngOnInit(): void {}
+  async ngOnInit(): Promise<void> {
+    this.onboardingNudgesFlag = await this.configService.getFeatureFlag(
path: apps/browser/src/tools/popup/send-v2/send-v2.component.ts
user: login: gbubemismith
body: You can use `getFeatureFlag$` and subscribe on the client, you won't need the `ngOnInit`
created_at: 2025-05-13T20:21:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -34,6 +34,7 @@ export enum VaultNudgeType {
   newIdentityItemStatus = "new-identity-item-status",
   newNoteItemStatus = "new-note-item-status",
   newSshItemStatus = "new-ssh-item-status",
+  GeneratorNudgeStatus = "generator-nudge-status",
path: libs/vault/src/services/vault-nudges.service.ts
user: login: Jingo88
body: I'm planning on making a ticket/PR later to update the names to be more consistent. 
created_at: 2025-05-13T21:19:17Z
author_association: CONTRIBUTOR
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: audreyality
body: ❓ Are the nudges limited to the browser environment?
created_at: 2025-05-14T12:44:02Z
author_association: MEMBER
diff_hunk: @@ -11,6 +11,8 @@
   </bit-toggle>
 </bit-toggle-group>
 
+<ng-content select="[slot=after-toggle]"></ng-content>
path: libs/tools/generator/components/src/credential-generator.component.html
user: login: audreyality
body: 🤔 I'm not sure that I like using `ng-content` for a feature that seems like it should be "baked in" to the generator. What alternatives did you consider?

📝 Please do not make any changes in light of this feedback. I want to talk it out before we change anything.
created_at: 2025-05-14T12:46:28Z
author_association: MEMBER
diff_hunk: @@ -25,6 +32,31 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
+    TypographyModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
+  );
+
+  constructor(
+    private vaultNudgesService: VaultNudgesService,
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: audreyality
body: ❌ This isn't a vault nudge service. It's now affecting the generators and sends, which are not vault features. The nudge service, in all likelihood, should be generalized. At a minimum it should be renamed.

created_at: 2025-05-14T12:51:37Z
author_association: MEMBER
diff_hunk: @@ -25,6 +32,31 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
+    TypographyModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: audreyality
body: ⛏️ I suspect this map should be encapsulated by the nude service. That is, I'd expect `showNudge$` to just output a boolean value. If the full status is needed, provide a separate `status$` endpoint.
created_at: 2025-05-14T12:55:31Z
author_association: MEMBER
diff_hunk: @@ -109,7 +113,11 @@ export class SendV2Component implements OnInit, OnDestroy {
       });
   }
 
-  ngOnInit(): void {}
+  async ngOnInit(): Promise<void> {
+    this.onboardingNudgesFlag = await this.configService.getFeatureFlag(
path: apps/browser/src/tools/popup/send-v2/send-v2.component.ts
user: login: audreyality
body: > I know there were some discussions on recent PR's about code churn regarding feature flags. I'm not aware if there is another way in this instance for differentiating copy and styling changes.

On the last PR, I noted I was looking for a way to inject a different implementation of the nudge service, but angular DI simply isn't compatible with observables.

There's good news, though. It's not necessary to use DI! You can achieve a similar outcome by lowering the config service into your nudge service. Methods like `showNudge$` can then simply output "false" when the service is disabled.

Say you change `showNudge$` so that it outputs only a boolean value and that value encapsulates *all* criteria for whether or not the new text should be shown. You could bind the style/copy swap's visibility to `showNudge$`. It'll never be dismissed, but that's not that big a deal.
created_at: 2025-05-14T13:07:35Z
author_association: MEMBER
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: Jingo88
body: Yes these nudges are limited to the browser environment. All nudges will be for the browser, with the exception of some work in the vault items add/edit.
created_at: 2025-05-14T16:58:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,6 +11,8 @@
   </bit-toggle>
 </bit-toggle-group>
 
+<ng-content select="[slot=after-toggle]"></ng-content>
path: libs/tools/generator/components/src/credential-generator.component.html
user: login: Jingo88
body: I can see that, will message offline
created_at: 2025-05-14T16:58:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +32,31 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
+    TypographyModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
+  );
+
+  constructor(
+    private vaultNudgesService: VaultNudgesService,
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: Jingo88
body: This feels more true as this epic moves forward and the nudges are incorporated into outside vault code. Will rename the service. 
created_at: 2025-05-14T16:58:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +32,31 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
+    TypographyModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: Jingo88
body: I like it. Created a new Observable for separation between spotlight and badges for nudges.
created_at: 2025-05-14T16:59:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +32,31 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
     PopupFooterComponent,
     RouterModule,
     ItemModule,
+    SpotlightComponent,
+    AsyncPipe,
+    CommonModule,
+    TypographyModule,
   ],
 })
-export class CredentialGeneratorComponent {}
+export class CredentialGeneratorComponent {
+  protected readonly VaultNudgeType = VaultNudgeType;
+  private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
+  protected showGeneratorSpotlight$: Observable<boolean> = this.activeUserId$.pipe(
+    switchMap((userId) =>
+      this.vaultNudgesService.showNudge$(VaultNudgeType.GeneratorNudgeStatus, userId),
+    ),
+    map((nudgeStatus) => !nudgeStatus.hasSpotlightDismissed),
+  );
+
+  constructor(
+    private vaultNudgesService: VaultNudgesService,
path: apps/browser/src/tools/popup/generator/credential-generator.component.ts
user: login: audreyality
body: Thanks! You mentioned in [a different thread](https://github.com/bitwarden/clients/pull/14705#discussion_r2087650531) that you'll create a separate PR to align the nudge type names. I imagine this feedback will be addressed there?
created_at: 2025-05-14T17:04:36Z
author_association: MEMBER
diff_hunk: @@ -22,6 +22,7 @@ const config: StorybookConfig = {
     "../bitwarden_license/bit-web/src/**/*.stories.@(js|jsx|ts|tsx)",
     "../libs/tools/card/src/**/*.mdx",
     "../libs/tools/card/src/**/*.stories.@(js|jsx|ts|tsx)",
+    "../libs/angular/src/**/*.stories.@(js|jsx|ts|tsx)",
path: .storybook/main.ts
user: login: nick-livefront
body: @bitwarden/team-ui-foundation The spotlight component was moved to `libs/angular/vault` so it can be imported into multiple `libs/` this allows for the associated stories to be shown. 
created_at: 2025-05-19T20:46:43Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-09T15:16:36Z
body: ## [Codecov](https://app.codecov.io/gh/bitwarden/clients/pull/14705?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) Report
Attention: Patch coverage is `68.75000%` with `5 lines` in your changes missing coverage. Please review.
> Project coverage is 36.40%. Comparing base [(`83c4438`)](https://app.codecov.io/gh/bitwarden/clients/commit/83c4438efe7ebe2d5bccc37ecd02fa15e8f88df3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) to head [(`78cbe81`)](https://app.codecov.io/gh/bitwarden/clients/commit/78cbe81f05b4f8af57945f626562af48b53fbac0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).
> Report is 16 commits behind head on main.

:white_check_mark: All tests successful. No failed tests found.

| [Files with missing lines](https://app.codecov.io/gh/bitwarden/clients/pull/14705?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) | Patch % | Lines |
|---|---|---|
| [...lt/popup/components/vault-v2/vault-v2.component.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fvault%2Fpopup%2Fcomponents%2Fvault-v2%2Fvault-v2.component.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy92YXVsdC9wb3B1cC9jb21wb25lbnRzL3ZhdWx0LXYyL3ZhdWx0LXYyLmNvbXBvbmVudC50cw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |
| [...c/new-send-dropdown/new-send-dropdown.component.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&filepath=libs%2Ftools%2Fsend%2Fsend-ui%2Fsrc%2Fnew-send-dropdown%2Fnew-send-dropdown.component.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-bGlicy90b29scy9zZW5kL3NlbmQtdWkvc3JjL25ldy1zZW5kLWRyb3Bkb3duL25ldy1zZW5kLWRyb3Bkb3duLmNvbXBvbmVudC50cw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |
| [.../src/autofill/popup/settings/autofill.component.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fautofill%2Fpopup%2Fsettings%2Fautofill.component.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy9hdXRvZmlsbC9wb3B1cC9zZXR0aW5ncy9hdXRvZmlsbC5jb21wb25lbnQudHM=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14705?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main   #14705   +/-   ##
=======================================
  Coverage   36.39%   36.40%           
=======================================
  Files        3208     3208           
  Lines       93137    93142    +5     
  Branches    16817    16817           
=======================================
+ Hits        33901    33907    +6     
+ Misses      56775    56773    -2     
- Partials     2461     2462    +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/bitwarden/clients/pull/14705?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T15:24:51Z
body: ![Logo](https://cdn.ast.checkmarx.net/integrations/logo/Checkmarx.png)
**Checkmarx One – Scan Summary & Details** – [cb2e90ea\-2547\-48ba\-a11c\-dab34c71e89c](https://ast.checkmarx.net/projects/41bd746e-d417-4eda-afeb-01a59d162661/scans?id=cb2e90ea-2547-48ba-a11c-dab34c71e89c&branch=PM-18804-generator-nudges)
### Great job, no security vulnerabilities found in this Pull Request

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-19T20:45:33Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=bitwarden_clients&pullRequest=14705) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=bitwarden_clients&pullRequest=14705&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=bitwarden_clients&pullRequest=14705&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=bitwarden_clients&pullRequest=14705&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=bitwarden_clients&pullRequest=14705&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=bitwarden_clients&pullRequest=14705&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=bitwarden_clients&pullRequest=14705)


author_association: NONE