@campfire-interactive/shell-header 0.15.3 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -67,8 +67,16 @@ interface ShellUser {
67
67
  interface TenantOption {
68
68
  id: string;
69
69
  name: string;
70
- /** Human-readable role label ("owner", "admin", "member"). Optional. */
71
- role?: string;
70
+ /**
71
+ * Human-readable role label ("owner", "admin", "member"). Optional.
72
+ *
73
+ * No longer rendered — the switcher's role chip was removed (see UserMenu).
74
+ * Kept so hosts that still pass it compile, and `null` is accepted because
75
+ * identity-client types the active tenant's `role` as `string | null`
76
+ * (absent on tokens minted before the claim existed). Without the null,
77
+ * every host passing `user.tenant` straight through fails to typecheck.
78
+ */
79
+ role?: string | null;
72
80
  }
73
81
  /**
74
82
  * A host-supplied action rendered in the user (avatar) menu, above Sign out.
@@ -245,8 +253,19 @@ interface ShellHeaderProps {
245
253
  /**
246
254
  * The user's role in their currently-active tenant (e.g. "admin", "owner",
247
255
  * "member"). Apps in the catalog with `requiresRole` are shown only when
248
- * this matches one of the allowed roles. Hosts typically derive this from
249
- * `user.tenants.find(t => t.id === user.tenant.id)?.role`.
256
+ * this matches one of the allowed roles.
257
+ *
258
+ * Derive this from the JWT's `data.tenant.role` — the active tenant's own
259
+ * claim. The old recipe, `user.tenants.find(t => t.id === user.tenant.id)?.role`,
260
+ * still works but reads the `data.tenants[]` array that is being removed from
261
+ * the token (nextgen docs/plans/2026-08-07-slim-identity-jwt-tenants.md); once
262
+ * it is gone that lookup silently yields `undefined` and every `requiresRole`
263
+ * app disappears from the switcher. Prefer `tenant.role`, fall back to the
264
+ * array while both exist.
265
+ *
266
+ * NOTE this is distinct from the role that used to render beside each row in
267
+ * the tenant switcher — that chip has been removed. This one still gates the
268
+ * app catalog and is unaffected.
250
269
  */
251
270
  currentTenantRole?: string;
252
271
  /** Unread count for the bell badge. */
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ function styleInject(css, { insertAt } = {}) {
24
24
  }
25
25
 
26
26
  // src/styles.css
27
- styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: white;\n color: var(--cfi-color-gray-900, #111827);\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n position: relative;\n z-index: 1000;\n}\n.cfi-sh-left {\n display: flex;\n align-items: center;\n flex: 1;\n gap: var(--cfi-spacing-md, 1rem);\n overflow: hidden;\n}\n.cfi-sh-app-brand {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n color: white;\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n border-radius: var(--cfi-radius-md, 0.375rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-base, 1rem);\n color: var(--cfi-color-gray-900, #111827);\n white-space: nowrap;\n}\n.cfi-sh-suite-logo {\n height: 22px;\n width: auto;\n display: block;\n}\n.cfi-sh-right {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n flex-shrink: 0;\n}\n.cfi-sh-icon-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: transparent;\n border: none;\n border-radius: 50%;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n transition: background 150ms;\n}\n.cfi-sh-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: var(--cfi-color-error, #ef4444);\n color: white;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n border-radius: 99px;\n}\n.cfi-sh-app-switcher {\n position: relative;\n}\n.cfi-sh-app-grid {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem);\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n min-width: 280px;\n}\n.cfi-sh-app-grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-xs, 0.25rem);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms;\n font-family: inherit;\n}\n.cfi-sh-app-grid-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-app-grid-item-active {\n background: var(--cfi-color-primary-50, #fff7ed);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-item-active:hover {\n background: var(--cfi-color-primary-100, #ffedd5);\n}\n.cfi-sh-app-grid-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n.cfi-sh-app-grid-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-align: center;\n line-height: 1.35;\n max-width: 80px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n white-space: normal;\n overflow: hidden;\n min-height: 2.7em;\n}\n.cfi-sh-user-menu {\n position: relative;\n}\n.cfi-sh-avatar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--cfi-color-primary-600, #ea580c);\n border: none;\n border-radius: 50%;\n cursor: pointer;\n transition: opacity 150ms;\n}\n.cfi-sh-avatar-btn:hover {\n opacity: 0.85;\n}\n.cfi-sh-avatar-img {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n}\n.cfi-sh-avatar-initials {\n color: white;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n}\n.cfi-sh-user-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 200px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n}\n.cfi-sh-user-header {\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-user-name {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-user-email {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n}\n.cfi-sh-divider {\n height: 1px;\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-menu-item {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-family: inherit;\n text-align: left;\n text-decoration: none;\n box-sizing: border-box;\n transition: background 100ms;\n}\n.cfi-sh-menu-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-menu-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-switcher {\n position: relative;\n}\n.cfi-sh-locale-btn {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n height: 32px;\n padding: 0 var(--cfi-spacing-sm, 0.5rem);\n background: transparent;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-locale-btn:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-label {\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 180px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n padding: var(--cfi-spacing-xs, 0.25rem) 0;\n}\n.cfi-sh-locale-dropdown .cfi-sh-menu-item {\n justify-content: space-between;\n}\n.cfi-sh-locale-item-active {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-item-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-item-code {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-row {\n justify-content: flex-start;\n}\n.cfi-sh-locale-row-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-row-current {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n margin-right: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-locale-chevron {\n transition: transform 150ms ease;\n flex-shrink: 0;\n}\n.cfi-sh-locale-chevron-open {\n transform: rotate(180deg);\n}\n.cfi-sh-locale-sublist {\n border-top: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-locale-sub-item {\n padding-left: calc(var(--cfi-spacing-md, 1rem) + var(--cfi-spacing-md, 1rem));\n justify-content: space-between;\n}\n.cfi-sh-tenant-static {\n cursor: default;\n pointer-events: none;\n}\n.cfi-sh-tenant-check {\n color: var(--cfi-color-violet-600, #7c3aed);\n}\n.cfi-sh-notif {\n position: relative;\n}\n.cfi-sh-notif-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n width: 360px;\n max-height: 480px;\n display: flex;\n flex-direction: column;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n overflow: hidden;\n}\n.cfi-sh-notif-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n flex-shrink: 0;\n}\n.cfi-sh-notif-title {\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-notif-mark-all {\n background: none;\n border: none;\n color: var(--cfi-color-primary-600, #ea580c);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-family: inherit;\n padding: 0;\n}\n.cfi-sh-notif-mark-all:hover {\n text-decoration: underline;\n}\n.cfi-sh-notif-list {\n overflow-y: auto;\n flex: 1;\n}\n.cfi-sh-notif-empty {\n padding: var(--cfi-spacing-lg, 1.5rem) var(--cfi-spacing-md, 1rem);\n color: var(--cfi-color-gray-500, #6b7280);\n text-align: center;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-notif-item {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n text-align: left;\n cursor: pointer;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-notif-item:hover {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-notif-item:last-child {\n border-bottom: none;\n}\n.cfi-sh-notif-dot {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n background: var(--cfi-color-primary-600, #ea580c);\n border-radius: 50%;\n}\n.cfi-sh-notif-dot-placeholder {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n}\n.cfi-sh-notif-content {\n flex: 1;\n min-width: 0;\n}\n.cfi-sh-notif-item-title {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-bottom: 2px;\n}\n.cfi-sh-notif-item-unread .cfi-sh-notif-item-title {\n font-weight: 600;\n}\n.cfi-sh-notif-item-body {\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.cfi-sh-notif-item-time {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: 11px;\n margin-top: 4px;\n}\n.cfi-sh-about-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1100;\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n}\n.cfi-sh-about-card {\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n min-width: 360px;\n max-width: 500px;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-header {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-lg, 1.125rem);\n flex: 1;\n}\n.cfi-sh-about-env {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n padding: 2px 8px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n font-weight: 600;\n}\n.cfi-sh-about-close {\n background: none;\n border: none;\n font-size: 1.5rem;\n line-height: 1;\n cursor: pointer;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 0 4px;\n font-family: inherit;\n}\n.cfi-sh-about-close:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-body {\n padding: var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 1rem);\n}\n.cfi-sh-about-section-title {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n margin-bottom: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-about-list {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: var(--cfi-spacing-xs, 0.25rem) var(--cfi-spacing-md, 1rem);\n margin: 0;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-about-list dt {\n color: var(--cfi-color-gray-600, #4b5563);\n font-weight: 500;\n}\n.cfi-sh-about-list dd {\n margin: 0;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-list code {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n background: var(--cfi-color-gray-100, #f3f4f6);\n padding: 1px 6px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n}\n.cfi-sh-about-date {\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-about-footer {\n display: flex;\n justify-content: flex-end;\n padding: var(--cfi-spacing-md, 1rem);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-copy {\n background: var(--cfi-color-gray-100, #f3f4f6);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n padding: 6px 12px;\n border-radius: var(--cfi-radius-md, 0.375rem);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: background 150ms;\n}\n.cfi-sh-about-copy:hover {\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1100;\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n}\n.cfi-sh-bug-card {\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n width: 480px;\n max-width: calc(100vw - 32px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-bug-card-wide {\n width: min(1100px, calc(100vw - 64px));\n}\n.cfi-sh-bug-header {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n cursor: grab;\n touch-action: none;\n user-select: none;\n -webkit-user-select: none;\n}\n.cfi-sh-bug-header:active {\n cursor: grabbing;\n}\n.cfi-sh-bug-header .cfi-sh-bug-close {\n cursor: pointer;\n}\n.cfi-sh-bug-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-lg, 1.125rem);\n flex: 1;\n}\n.cfi-sh-bug-env {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n padding: 2px 8px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n font-weight: 600;\n}\n.cfi-sh-bug-close {\n background: none;\n border: none;\n font-size: 1.5rem;\n line-height: 1;\n cursor: pointer;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 0 4px;\n font-family: inherit;\n}\n.cfi-sh-bug-close:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-body {\n padding: var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 0.5rem);\n}\n.cfi-sh-bug-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-input,\n.cfi-sh-bug-textarea {\n font-family: inherit;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-900, #111827);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 0.375rem);\n padding: 8px 10px;\n outline: none;\n}\n.cfi-sh-bug-input:focus,\n.cfi-sh-bug-textarea:focus {\n border-color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-textarea {\n resize: vertical;\n min-height: 72px;\n}\n.cfi-sh-bug-shot {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-wrap: wrap;\n padding-top: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-thumb {\n max-width: 200px;\n max-height: 120px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-sm, 0.25rem);\n display: block;\n}\n.cfi-sh-bug-shot-actions {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-files {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding-top: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-file-row {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n max-width: 100%;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-sh-bug-file-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-sh-bug-file-size {\n flex-shrink: 0;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-file-remove {\n flex-shrink: 0;\n border: none;\n background: none;\n padding: 0 0.25rem;\n cursor: pointer;\n font-size: var(--cfi-font-size-md, 1rem);\n line-height: 1;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-file-remove:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-meta {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--cfi-spacing-sm, 0.5rem);\n}\n.cfi-sh-bug-modes {\n display: flex;\n gap: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-hint {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-canvas-wrap {\n overflow: auto;\n max-height: 65vh;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-sm, 0.25rem);\n background: var(--cfi-color-gray-100, #f3f4f6);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.cfi-sh-bug-canvas {\n max-width: 100%;\n max-height: 65vh;\n cursor: crosshair;\n touch-action: none;\n display: block;\n}\n.cfi-sh-bug-footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-btn {\n background: var(--cfi-color-gray-100, #f3f4f6);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n padding: 6px 12px;\n border-radius: var(--cfi-radius-md, 0.375rem);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: background 150ms;\n}\n.cfi-sh-bug-btn:hover {\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-btn:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.cfi-sh-bug-btn-primary {\n background: var(--cfi-color-gray-900, #111827);\n border-color: var(--cfi-color-gray-900, #111827);\n color: white;\n}\n.cfi-sh-bug-btn-primary:hover:not(:disabled) {\n background: var(--cfi-color-gray-700, #374151);\n}\n.cfi-sh-bug-btn-active {\n background: var(--cfi-color-gray-900, #111827);\n border-color: var(--cfi-color-gray-900, #111827);\n color: white;\n}\n.cfi-sh-bug-error {\n color: var(--cfi-color-red-600, #dc2626);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-right: auto;\n}\n.cfi-sh-bug-success {\n color: var(--cfi-color-green-600, #16a34a);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n}\n.cfi-sh-bug-done {\n gap: var(--cfi-spacing-md, 1rem);\n}\n.cfi-sh-bug-done-msg {\n margin: 0;\n font-size: var(--cfi-font-size-base, 1rem);\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-done-link {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-wrap: wrap;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-sh-bug-done-url {\n color: var(--cfi-color-primary-600, #2563eb);\n text-decoration: underline;\n overflow-wrap: anywhere;\n}\n.cfi-sh-bug-copy-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 4px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 0.375rem);\n background: white;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n line-height: 0;\n}\n.cfi-sh-bug-copy-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-issues-link {\n color: var(--cfi-color-gray-600, #4b5563);\n text-decoration: underline;\n}\n.cfi-sh-bug-issues-link:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n");
27
+ styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: white;\n color: var(--cfi-color-gray-900, #111827);\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n position: relative;\n z-index: 1000;\n}\n.cfi-sh-left {\n display: flex;\n align-items: center;\n flex: 1;\n gap: var(--cfi-spacing-md, 1rem);\n overflow: hidden;\n}\n.cfi-sh-app-brand {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n color: white;\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n border-radius: var(--cfi-radius-md, 0.375rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-base, 1rem);\n color: var(--cfi-color-gray-900, #111827);\n white-space: nowrap;\n}\n.cfi-sh-suite-logo {\n height: 22px;\n width: auto;\n display: block;\n}\n.cfi-sh-right {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n flex-shrink: 0;\n}\n.cfi-sh-icon-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: transparent;\n border: none;\n border-radius: 50%;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n transition: background 150ms;\n}\n.cfi-sh-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: var(--cfi-color-error, #ef4444);\n color: white;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n border-radius: 99px;\n}\n.cfi-sh-app-switcher {\n position: relative;\n}\n.cfi-sh-app-grid {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem);\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n min-width: 280px;\n}\n.cfi-sh-app-grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-xs, 0.25rem);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms;\n font-family: inherit;\n}\n.cfi-sh-app-grid-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-app-grid-item-active {\n background: var(--cfi-color-primary-50, #fff7ed);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-item-active:hover {\n background: var(--cfi-color-primary-100, #ffedd5);\n}\n.cfi-sh-app-grid-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n.cfi-sh-app-grid-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-align: center;\n line-height: 1.35;\n max-width: 80px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n white-space: normal;\n overflow: hidden;\n min-height: 2.7em;\n}\n.cfi-sh-user-menu {\n position: relative;\n}\n.cfi-sh-avatar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--cfi-color-primary-600, #ea580c);\n border: none;\n border-radius: 50%;\n cursor: pointer;\n transition: opacity 150ms;\n}\n.cfi-sh-avatar-btn:hover {\n opacity: 0.85;\n}\n.cfi-sh-avatar-img {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n}\n.cfi-sh-avatar-initials {\n color: white;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n}\n.cfi-sh-user-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 200px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n}\n.cfi-sh-user-header {\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-user-name {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-user-email {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n}\n.cfi-sh-divider {\n height: 1px;\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-menu-item {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-family: inherit;\n text-align: left;\n text-decoration: none;\n box-sizing: border-box;\n transition: background 100ms;\n}\n.cfi-sh-menu-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-menu-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-switcher {\n position: relative;\n}\n.cfi-sh-locale-btn {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n height: 32px;\n padding: 0 var(--cfi-spacing-sm, 0.5rem);\n background: transparent;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-locale-btn:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-label {\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 180px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n padding: var(--cfi-spacing-xs, 0.25rem) 0;\n}\n.cfi-sh-locale-dropdown .cfi-sh-menu-item {\n justify-content: space-between;\n}\n.cfi-sh-locale-item-active {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-item-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-item-code {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-row {\n justify-content: flex-start;\n}\n.cfi-sh-locale-row-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-row-current {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n margin-right: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-locale-chevron {\n transition: transform 150ms ease;\n flex-shrink: 0;\n}\n.cfi-sh-locale-chevron-open {\n transform: rotate(180deg);\n}\n.cfi-sh-locale-sublist {\n border-top: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-locale-sub-item {\n padding-left: calc(var(--cfi-spacing-md, 1rem) + var(--cfi-spacing-md, 1rem));\n justify-content: space-between;\n}\n.cfi-sh-tenant-static {\n cursor: default;\n pointer-events: none;\n}\n.cfi-sh-tenant-check {\n color: var(--cfi-color-violet-600, #7c3aed);\n}\n.cfi-sh-notif {\n position: relative;\n}\n.cfi-sh-notif-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n width: 360px;\n max-height: 480px;\n display: flex;\n flex-direction: column;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n overflow: hidden;\n}\n.cfi-sh-notif-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n flex-shrink: 0;\n}\n.cfi-sh-notif-title {\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-notif-mark-all {\n background: none;\n border: none;\n color: var(--cfi-color-primary-600, #ea580c);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-family: inherit;\n padding: 0;\n}\n.cfi-sh-notif-mark-all:hover {\n text-decoration: underline;\n}\n.cfi-sh-notif-list {\n overflow-y: auto;\n flex: 1;\n}\n.cfi-sh-notif-empty {\n padding: var(--cfi-spacing-lg, 1.5rem) var(--cfi-spacing-md, 1rem);\n color: var(--cfi-color-gray-500, #6b7280);\n text-align: center;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-notif-item {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n text-align: left;\n cursor: pointer;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-notif-item:hover {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-notif-item:last-child {\n border-bottom: none;\n}\n.cfi-sh-notif-dot {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n background: var(--cfi-color-primary-600, #ea580c);\n border-radius: 50%;\n}\n.cfi-sh-notif-dot-placeholder {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n}\n.cfi-sh-notif-content {\n flex: 1;\n min-width: 0;\n}\n.cfi-sh-notif-item-title {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-bottom: 2px;\n}\n.cfi-sh-notif-item-unread .cfi-sh-notif-item-title {\n font-weight: 600;\n}\n.cfi-sh-notif-item-body {\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.cfi-sh-notif-item-time {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: 11px;\n margin-top: 4px;\n}\n.cfi-sh-about-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1100;\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n}\n.cfi-sh-about-card {\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n min-width: 360px;\n max-width: 500px;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-header {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-lg, 1.125rem);\n flex: 1;\n}\n.cfi-sh-about-env {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n padding: 2px 8px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n font-weight: 600;\n}\n.cfi-sh-about-close {\n background: none;\n border: none;\n font-size: 1.5rem;\n line-height: 1;\n cursor: pointer;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 0 4px;\n font-family: inherit;\n}\n.cfi-sh-about-close:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-body {\n padding: var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 1rem);\n}\n.cfi-sh-about-section-title {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n margin-bottom: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-about-list {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: var(--cfi-spacing-xs, 0.25rem) var(--cfi-spacing-md, 1rem);\n margin: 0;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-about-list dt {\n color: var(--cfi-color-gray-600, #4b5563);\n font-weight: 500;\n}\n.cfi-sh-about-list dd {\n margin: 0;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-list code {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n background: var(--cfi-color-gray-100, #f3f4f6);\n padding: 1px 6px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n}\n.cfi-sh-about-date {\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-about-footer {\n display: flex;\n justify-content: flex-end;\n padding: var(--cfi-spacing-md, 1rem);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-copy {\n background: var(--cfi-color-gray-100, #f3f4f6);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n padding: 6px 12px;\n border-radius: var(--cfi-radius-md, 0.375rem);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: background 150ms;\n}\n.cfi-sh-about-copy:hover {\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1100;\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n}\n.cfi-sh-bug-card {\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n width: 480px;\n max-width: calc(100vw - 32px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-bug-card-wide {\n width: min(1100px, calc(100vw - 64px));\n}\n.cfi-sh-bug-header {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-lg, 1.125rem);\n flex: 1;\n}\n.cfi-sh-bug-env {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n padding: 2px 8px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n font-weight: 600;\n}\n.cfi-sh-bug-close {\n background: none;\n border: none;\n font-size: 1.5rem;\n line-height: 1;\n cursor: pointer;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 0 4px;\n font-family: inherit;\n}\n.cfi-sh-bug-close:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-body {\n padding: var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 0.5rem);\n}\n.cfi-sh-bug-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-input,\n.cfi-sh-bug-textarea {\n font-family: inherit;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-900, #111827);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 0.375rem);\n padding: 8px 10px;\n outline: none;\n}\n.cfi-sh-bug-input:focus,\n.cfi-sh-bug-textarea:focus {\n border-color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-textarea {\n resize: vertical;\n min-height: 72px;\n}\n.cfi-sh-bug-shot {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-wrap: wrap;\n padding-top: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-thumb {\n max-width: 200px;\n max-height: 120px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-sm, 0.25rem);\n display: block;\n}\n.cfi-sh-bug-shot-actions {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-files {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding-top: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-file-row {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n max-width: 100%;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-sh-bug-file-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-sh-bug-file-size {\n flex-shrink: 0;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-file-remove {\n flex-shrink: 0;\n border: none;\n background: none;\n padding: 0 0.25rem;\n cursor: pointer;\n font-size: var(--cfi-font-size-md, 1rem);\n line-height: 1;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-file-remove:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-meta {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--cfi-spacing-sm, 0.5rem);\n}\n.cfi-sh-bug-modes {\n display: flex;\n gap: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-bug-hint {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-bug-canvas-wrap {\n overflow: auto;\n max-height: 65vh;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-sm, 0.25rem);\n background: var(--cfi-color-gray-100, #f3f4f6);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.cfi-sh-bug-canvas {\n max-width: 100%;\n max-height: 65vh;\n cursor: crosshair;\n touch-action: none;\n display: block;\n}\n.cfi-sh-bug-footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-btn {\n background: var(--cfi-color-gray-100, #f3f4f6);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n padding: 6px 12px;\n border-radius: var(--cfi-radius-md, 0.375rem);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: background 150ms;\n}\n.cfi-sh-bug-btn:hover {\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-bug-btn:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.cfi-sh-bug-btn-primary {\n background: var(--cfi-color-gray-900, #111827);\n border-color: var(--cfi-color-gray-900, #111827);\n color: white;\n}\n.cfi-sh-bug-btn-primary:hover:not(:disabled) {\n background: var(--cfi-color-gray-700, #374151);\n}\n.cfi-sh-bug-btn-active {\n background: var(--cfi-color-gray-900, #111827);\n border-color: var(--cfi-color-gray-900, #111827);\n color: white;\n}\n.cfi-sh-bug-error {\n color: var(--cfi-color-red-600, #dc2626);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-right: auto;\n}\n.cfi-sh-bug-success {\n color: var(--cfi-color-green-600, #16a34a);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n}\n.cfi-sh-bug-done {\n gap: var(--cfi-spacing-md, 1rem);\n}\n.cfi-sh-bug-done-msg {\n margin: 0;\n font-size: var(--cfi-font-size-base, 1rem);\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-done-link {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-wrap: wrap;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-sh-bug-done-url {\n color: var(--cfi-color-primary-600, #2563eb);\n text-decoration: underline;\n overflow-wrap: anywhere;\n}\n.cfi-sh-bug-copy-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 4px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 0.375rem);\n background: white;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n line-height: 0;\n}\n.cfi-sh-bug-copy-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-bug-issues-link {\n color: var(--cfi-color-gray-600, #4b5563);\n text-decoration: underline;\n}\n.cfi-sh-bug-issues-link:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n");
28
28
 
29
29
  // src/appCatalog.ts
30
30
  var appCatalog = [
@@ -512,7 +512,7 @@ function UserMenu({
512
512
  disabled: updating || active,
513
513
  children: [
514
514
  /* @__PURE__ */ jsx4("span", { className: "cfi-sh-locale-item-label", children: t.name }),
515
- active ? /* @__PURE__ */ jsx4(Check, { size: 14, className: "cfi-sh-tenant-check" }) : t.role ? /* @__PURE__ */ jsx4("span", { className: "cfi-sh-locale-item-code", children: t.role }) : null
515
+ active ? /* @__PURE__ */ jsx4(Check, { size: 14, className: "cfi-sh-tenant-check" }) : null
516
516
  ]
517
517
  },
518
518
  t.id
@@ -843,13 +843,8 @@ async function captureTabScreenshot() {
843
843
  var MAX_ENTRIES = 50;
844
844
  var MAX_MESSAGE_CHARS = 500;
845
845
  var consoleBuffer = [];
846
- var networkFailureBuffer = [];
847
- var networkSuccessBuffer = [];
846
+ var networkBuffer = [];
848
847
  var installed = false;
849
- function stripQuery(url) {
850
- const i = url.indexOf("?");
851
- return i === -1 ? url : url.slice(0, i);
852
- }
853
848
  function push(buffer, entry) {
854
849
  buffer.push(entry);
855
850
  if (buffer.length > MAX_ENTRIES) buffer.shift();
@@ -898,7 +893,7 @@ function installBugReportCapture() {
898
893
  try {
899
894
  const res = await originalFetch(input, init);
900
895
  if (!res.ok) {
901
- push(networkFailureBuffer, {
896
+ push(networkBuffer, {
902
897
  at: (/* @__PURE__ */ new Date()).toISOString(),
903
898
  method: method.toUpperCase(),
904
899
  url,
@@ -906,19 +901,10 @@ function installBugReportCapture() {
906
901
  durationMs: Math.round(performance.now() - started),
907
902
  requestId: res.headers.get("x-request-id") ?? void 0
908
903
  });
909
- } else {
910
- push(networkSuccessBuffer, {
911
- at: (/* @__PURE__ */ new Date()).toISOString(),
912
- method: method.toUpperCase(),
913
- url: stripQuery(url),
914
- status: res.status,
915
- durationMs: Math.round(performance.now() - started),
916
- requestId: res.headers.get("x-request-id") ?? void 0
917
- });
918
904
  }
919
905
  return res;
920
906
  } catch (err) {
921
- push(networkFailureBuffer, {
907
+ push(networkBuffer, {
922
908
  at: (/* @__PURE__ */ new Date()).toISOString(),
923
909
  method: method.toUpperCase(),
924
910
  url,
@@ -930,10 +916,7 @@ function installBugReportCapture() {
930
916
  };
931
917
  }
932
918
  function snapshotBugReportCapture() {
933
- const network = [...networkFailureBuffer, ...networkSuccessBuffer].sort(
934
- (a, b) => a.at.localeCompare(b.at)
935
- );
936
- return { console: [...consoleBuffer], network };
919
+ return { console: [...consoleBuffer], network: [...networkBuffer] };
937
920
  }
938
921
 
939
922
  // src/BugReportModal.tsx
@@ -959,40 +942,6 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
959
942
  const [submitError, setSubmitError] = useState6(null);
960
943
  const [issueUrl, setIssueUrl] = useState6(null);
961
944
  const [linkCopied, setLinkCopied] = useState6(false);
962
- const cardRef = useRef4(null);
963
- const [cardOffset, setCardOffset] = useState6({ x: 0, y: 0 });
964
- const headerDragRef = useRef4(null);
965
- function onHeaderPointerDown(e) {
966
- if (e.target.closest("button")) return;
967
- const card = cardRef.current;
968
- if (!card) return;
969
- e.currentTarget.setPointerCapture(e.pointerId);
970
- headerDragRef.current = {
971
- pointerId: e.pointerId,
972
- startX: e.clientX,
973
- startY: e.clientY,
974
- baseX: cardOffset.x,
975
- baseY: cardOffset.y,
976
- rect: card.getBoundingClientRect()
977
- };
978
- }
979
- function onHeaderPointerMove(e) {
980
- const d = headerDragRef.current;
981
- if (!d || d.pointerId !== e.pointerId) return;
982
- let dx = e.clientX - d.startX;
983
- let dy = e.clientY - d.startY;
984
- const minX = 80 - d.rect.right;
985
- const maxX = window.innerWidth - 80 - d.rect.left;
986
- const minY = -d.rect.top;
987
- const maxY = window.innerHeight - 48 - d.rect.top;
988
- dx = Math.min(Math.max(dx, minX), maxX);
989
- dy = Math.min(Math.max(dy, minY), maxY);
990
- setCardOffset({ x: d.baseX + dx, y: d.baseY + dy });
991
- }
992
- function onHeaderPointerEnd(e) {
993
- if (headerDragRef.current?.pointerId !== e.pointerId) return;
994
- headerDragRef.current = null;
995
- }
996
945
  const workCanvasRef = useRef4(null);
997
946
  const originalRef = useRef4(null);
998
947
  const displayCanvasRef = useRef4(null);
@@ -1216,215 +1165,197 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
1216
1165
  "aria-label": "Report an issue",
1217
1166
  onPointerDown: (e) => e.stopPropagation(),
1218
1167
  onFocus: (e) => e.stopPropagation(),
1219
- children: /* @__PURE__ */ jsxs6(
1220
- "div",
1221
- {
1222
- ref: cardRef,
1223
- className: `cfi-sh-bug-card${view === "editor" ? " cfi-sh-bug-card-wide" : ""}`,
1224
- style: cardOffset.x !== 0 || cardOffset.y !== 0 ? { transform: `translate(${cardOffset.x}px, ${cardOffset.y}px)` } : void 0,
1225
- children: [
1226
- /* @__PURE__ */ jsxs6(
1227
- "div",
1228
- {
1229
- className: "cfi-sh-bug-header",
1230
- onPointerDown: onHeaderPointerDown,
1231
- onPointerMove: onHeaderPointerMove,
1232
- onPointerUp: onHeaderPointerEnd,
1233
- onPointerCancel: onHeaderPointerEnd,
1234
- children: [
1235
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-title", children: submitState === "success" ? "Issue submitted" : view === "editor" ? "New Issue - Attach Screenshot (Optional)" : "Report an issue" }),
1236
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-env", children: config.context.environment }),
1237
- /* @__PURE__ */ jsx6("button", { className: "cfi-sh-bug-close", type: "button", onClick: closeUnlessBusy, "aria-label": "Close", children: "\xD7" })
1238
- ]
1239
- }
1240
- ),
1241
- submitState === "success" ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1242
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body cfi-sh-bug-done", children: [
1243
- /* @__PURE__ */ jsx6("p", { className: "cfi-sh-bug-done-msg", children: "Thanks for submitting this issue." }),
1244
- issueUrl ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-done-link", children: [
1245
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-done-label", children: "You can view it here:" }),
1246
- /* @__PURE__ */ jsx6(
1247
- "a",
1248
- {
1249
- href: issueUrl,
1250
- target: "_blank",
1251
- rel: "noreferrer",
1252
- className: "cfi-sh-bug-done-url",
1253
- children: issueUrl
1254
- }
1255
- ),
1256
- /* @__PURE__ */ jsx6(
1257
- "button",
1258
- {
1259
- type: "button",
1260
- className: "cfi-sh-bug-copy-btn",
1261
- title: "Copy link",
1262
- "aria-label": "Copy link",
1263
- onClick: () => {
1264
- void navigator.clipboard.writeText(issueUrl).then(() => {
1265
- setLinkCopied(true);
1266
- window.setTimeout(() => setLinkCopied(false), 1500);
1267
- });
1268
- },
1269
- children: linkCopied ? /* @__PURE__ */ jsx6(Check2, { size: 16 }) : /* @__PURE__ */ jsx6(Copy, { size: 16 })
1270
- }
1271
- )
1272
- ] }) : null
1273
- ] }),
1274
- /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-footer", children: /* @__PURE__ */ jsx6(
1275
- "button",
1168
+ children: /* @__PURE__ */ jsxs6("div", { className: `cfi-sh-bug-card${view === "editor" ? " cfi-sh-bug-card-wide" : ""}`, children: [
1169
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-header", children: [
1170
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-title", children: submitState === "success" ? "Issue submitted" : view === "editor" ? "New Issue - Attach Screenshot (Optional)" : "Report an issue" }),
1171
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-env", children: config.context.environment }),
1172
+ /* @__PURE__ */ jsx6("button", { className: "cfi-sh-bug-close", type: "button", onClick: closeUnlessBusy, "aria-label": "Close", children: "\xD7" })
1173
+ ] }),
1174
+ submitState === "success" ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1175
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body cfi-sh-bug-done", children: [
1176
+ /* @__PURE__ */ jsx6("p", { className: "cfi-sh-bug-done-msg", children: "Thanks for submitting this issue." }),
1177
+ issueUrl ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-done-link", children: [
1178
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-done-label", children: "You can view it here:" }),
1179
+ /* @__PURE__ */ jsx6(
1180
+ "a",
1276
1181
  {
1277
- type: "button",
1278
- className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1279
- onClick: closeUnlessBusy,
1280
- children: "OK"
1182
+ href: issueUrl,
1183
+ target: "_blank",
1184
+ rel: "noreferrer",
1185
+ className: "cfi-sh-bug-done-url",
1186
+ children: issueUrl
1281
1187
  }
1282
- ) })
1283
- ] }) : view === "editor" ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1284
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-toolbar", children: [
1285
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1286
- /* @__PURE__ */ jsx6(
1287
- "button",
1288
- {
1289
- type: "button",
1290
- className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1291
- onClick: () => setMode("crop"),
1292
- children: "Crop"
1293
- }
1294
- ),
1295
- /* @__PURE__ */ jsx6(
1296
- "button",
1297
- {
1298
- type: "button",
1299
- className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1300
- onClick: () => setMode("redact"),
1301
- children: "Redact"
1302
- }
1303
- )
1304
- ] }),
1305
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-hint", children: mode === "crop" ? "Drag to crop to a region" : "Drag to black out sensitive data" }),
1306
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1307
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: () => setView("form"), children: "Skip" }),
1308
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleEditorReset, children: "Reset" }),
1309
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary", onClick: handleEditorDone, children: "Done" })
1310
- ] })
1311
- ] }),
1312
- /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-canvas-wrap", children: /* @__PURE__ */ jsx6(
1313
- "canvas",
1188
+ ),
1189
+ /* @__PURE__ */ jsx6(
1190
+ "button",
1314
1191
  {
1315
- ref: displayCanvasRef,
1316
- className: "cfi-sh-bug-canvas",
1317
- onPointerDown: handlePointerDown,
1318
- onPointerMove: handlePointerMove,
1319
- onPointerUp: handlePointerUp
1192
+ type: "button",
1193
+ className: "cfi-sh-bug-copy-btn",
1194
+ title: "Copy link",
1195
+ "aria-label": "Copy link",
1196
+ onClick: () => {
1197
+ void navigator.clipboard.writeText(issueUrl).then(() => {
1198
+ setLinkCopied(true);
1199
+ window.setTimeout(() => setLinkCopied(false), 1500);
1200
+ });
1201
+ },
1202
+ children: linkCopied ? /* @__PURE__ */ jsx6(Check2, { size: 16 }) : /* @__PURE__ */ jsx6(Copy, { size: 16 })
1320
1203
  }
1321
- ) })
1322
- ] }) : /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1323
- /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-title-input", children: "Title" }),
1204
+ )
1205
+ ] }) : null
1206
+ ] }),
1207
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-footer", children: /* @__PURE__ */ jsx6(
1208
+ "button",
1209
+ {
1210
+ type: "button",
1211
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1212
+ onClick: closeUnlessBusy,
1213
+ children: "OK"
1214
+ }
1215
+ ) })
1216
+ ] }) : view === "editor" ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1217
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-toolbar", children: [
1218
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1324
1219
  /* @__PURE__ */ jsx6(
1325
- "input",
1220
+ "button",
1326
1221
  {
1327
- id: "cfi-sh-bug-title-input",
1328
- className: "cfi-sh-bug-input",
1329
- value: title,
1330
- onChange: (e) => setTitle(e.target.value),
1331
- placeholder: "What went wrong?",
1332
- maxLength: 200,
1333
- autoFocus: true
1222
+ type: "button",
1223
+ className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1224
+ onClick: () => setMode("crop"),
1225
+ children: "Crop"
1334
1226
  }
1335
1227
  ),
1336
- /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-desc-input", children: "Details" }),
1337
1228
  /* @__PURE__ */ jsx6(
1338
- "textarea",
1229
+ "button",
1339
1230
  {
1340
- id: "cfi-sh-bug-desc-input",
1341
- className: "cfi-sh-bug-textarea",
1342
- value: description,
1343
- onChange: (e) => setDescription(e.target.value),
1344
- placeholder: "What did you expect? What happened instead? Steps to reproduce\u2026",
1345
- rows: 4,
1346
- maxLength: 1e4
1231
+ type: "button",
1232
+ className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1233
+ onClick: () => setMode("redact"),
1234
+ children: "Redact"
1347
1235
  }
1348
- ),
1349
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot", children: [
1350
- screenshot ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1351
- /* @__PURE__ */ jsx6("img", { className: "cfi-sh-bug-thumb", src: screenshot.previewUrl, alt: "Screenshot preview" }),
1352
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot-actions", children: [
1353
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Retake" }),
1354
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleRemoveScreenshot, children: "Remove" })
1355
- ] })
1356
- ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Capture screenshot" }),
1357
- captureError && /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-error", children: captureError })
1358
- ] }),
1359
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-files", children: [
1360
- attachments.map((file, i) => /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-file-row", children: [
1361
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-name", title: file.name, children: file.name }),
1362
- /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-size", children: formatFileSize(file.size) }),
1363
- /* @__PURE__ */ jsx6(
1364
- "button",
1365
- {
1366
- type: "button",
1367
- className: "cfi-sh-bug-file-remove",
1368
- onClick: () => handleRemoveAttachment(i),
1369
- "aria-label": `Remove ${file.name}`,
1370
- children: "\xD7"
1371
- }
1372
- )
1373
- ] }, `${file.name}-${file.size}-${i}`)),
1374
- /* @__PURE__ */ jsx6(
1375
- "button",
1376
- {
1377
- type: "button",
1378
- className: "cfi-sh-bug-btn",
1379
- onClick: () => fileInputRef.current?.click(),
1380
- children: "Attach files\u2026"
1381
- }
1382
- ),
1383
- /* @__PURE__ */ jsx6(
1384
- "input",
1385
- {
1386
- ref: fileInputRef,
1387
- type: "file",
1388
- multiple: true,
1389
- style: { display: "none" },
1390
- onChange: (e) => handleFilesPicked(e.target.files)
1391
- }
1392
- )
1393
- ] }),
1394
- /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-meta", children: [
1395
- "Includes: URL, app version, recent console errors and failed requests.",
1396
- config.context.issuesUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1397
- " ",
1398
- /* @__PURE__ */ jsx6(
1399
- "a",
1400
- {
1401
- className: "cfi-sh-bug-issues-link",
1402
- href: config.context.issuesUrl,
1403
- target: "_blank",
1404
- rel: "noreferrer",
1405
- children: "View known issues"
1406
- }
1407
- )
1408
- ] })
1409
- ] })
1236
+ )
1410
1237
  ] }),
1411
- view === "form" && submitState !== "success" && /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-footer", children: [
1412
- submitState === "error" && submitError && /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-error", children: submitError }),
1413
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: closeUnlessBusy, children: "Cancel" }),
1238
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-hint", children: mode === "crop" ? "Drag to crop to a region" : "Drag to black out sensitive data" }),
1239
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1240
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: () => setView("form"), children: "Skip" }),
1241
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleEditorReset, children: "Reset" }),
1242
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary", onClick: handleEditorDone, children: "Done" })
1243
+ ] })
1244
+ ] }),
1245
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-canvas-wrap", children: /* @__PURE__ */ jsx6(
1246
+ "canvas",
1247
+ {
1248
+ ref: displayCanvasRef,
1249
+ className: "cfi-sh-bug-canvas",
1250
+ onPointerDown: handlePointerDown,
1251
+ onPointerMove: handlePointerMove,
1252
+ onPointerUp: handlePointerUp
1253
+ }
1254
+ ) })
1255
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1256
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-title-input", children: "Title" }),
1257
+ /* @__PURE__ */ jsx6(
1258
+ "input",
1259
+ {
1260
+ id: "cfi-sh-bug-title-input",
1261
+ className: "cfi-sh-bug-input",
1262
+ value: title,
1263
+ onChange: (e) => setTitle(e.target.value),
1264
+ placeholder: "What went wrong?",
1265
+ maxLength: 200,
1266
+ autoFocus: true
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-desc-input", children: "Details" }),
1270
+ /* @__PURE__ */ jsx6(
1271
+ "textarea",
1272
+ {
1273
+ id: "cfi-sh-bug-desc-input",
1274
+ className: "cfi-sh-bug-textarea",
1275
+ value: description,
1276
+ onChange: (e) => setDescription(e.target.value),
1277
+ placeholder: "What did you expect? What happened instead? Steps to reproduce\u2026",
1278
+ rows: 4,
1279
+ maxLength: 1e4
1280
+ }
1281
+ ),
1282
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot", children: [
1283
+ screenshot ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1284
+ /* @__PURE__ */ jsx6("img", { className: "cfi-sh-bug-thumb", src: screenshot.previewUrl, alt: "Screenshot preview" }),
1285
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot-actions", children: [
1286
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Retake" }),
1287
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleRemoveScreenshot, children: "Remove" })
1288
+ ] })
1289
+ ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Capture screenshot" }),
1290
+ captureError && /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-error", children: captureError })
1291
+ ] }),
1292
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-files", children: [
1293
+ attachments.map((file, i) => /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-file-row", children: [
1294
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-name", title: file.name, children: file.name }),
1295
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-size", children: formatFileSize(file.size) }),
1414
1296
  /* @__PURE__ */ jsx6(
1415
1297
  "button",
1416
1298
  {
1417
1299
  type: "button",
1418
- className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1419
- onClick: handleSubmit,
1420
- disabled: !title.trim() || submitState === "submitting",
1421
- children: submitState === "submitting" ? "Submitting\u2026" : "Submit report"
1300
+ className: "cfi-sh-bug-file-remove",
1301
+ onClick: () => handleRemoveAttachment(i),
1302
+ "aria-label": `Remove ${file.name}`,
1303
+ children: "\xD7"
1304
+ }
1305
+ )
1306
+ ] }, `${file.name}-${file.size}-${i}`)),
1307
+ /* @__PURE__ */ jsx6(
1308
+ "button",
1309
+ {
1310
+ type: "button",
1311
+ className: "cfi-sh-bug-btn",
1312
+ onClick: () => fileInputRef.current?.click(),
1313
+ children: "Attach files\u2026"
1314
+ }
1315
+ ),
1316
+ /* @__PURE__ */ jsx6(
1317
+ "input",
1318
+ {
1319
+ ref: fileInputRef,
1320
+ type: "file",
1321
+ multiple: true,
1322
+ style: { display: "none" },
1323
+ onChange: (e) => handleFilesPicked(e.target.files)
1324
+ }
1325
+ )
1326
+ ] }),
1327
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-meta", children: [
1328
+ "Includes: URL, app version, recent console errors and failed requests.",
1329
+ config.context.issuesUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1330
+ " ",
1331
+ /* @__PURE__ */ jsx6(
1332
+ "a",
1333
+ {
1334
+ className: "cfi-sh-bug-issues-link",
1335
+ href: config.context.issuesUrl,
1336
+ target: "_blank",
1337
+ rel: "noreferrer",
1338
+ children: "View known issues"
1422
1339
  }
1423
1340
  )
1424
1341
  ] })
1425
- ]
1426
- }
1427
- )
1342
+ ] })
1343
+ ] }),
1344
+ view === "form" && submitState !== "success" && /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-footer", children: [
1345
+ submitState === "error" && submitError && /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-error", children: submitError }),
1346
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: closeUnlessBusy, children: "Cancel" }),
1347
+ /* @__PURE__ */ jsx6(
1348
+ "button",
1349
+ {
1350
+ type: "button",
1351
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1352
+ onClick: handleSubmit,
1353
+ disabled: !title.trim() || submitState === "submitting",
1354
+ children: submitState === "submitting" ? "Submitting\u2026" : "Submit report"
1355
+ }
1356
+ )
1357
+ ] })
1358
+ ] })
1428
1359
  }
1429
1360
  );
1430
1361
  }
@@ -1463,16 +1394,14 @@ function useBugReportShortcut(enabled, onShortcut) {
1463
1394
  function handleKeyDown(e) {
1464
1395
  const platformMod = e.ctrlKey || e.metaKey;
1465
1396
  if (!platformMod || !e.altKey || e.shiftKey) return;
1466
- const isB = e.code === "KeyB" || e.key.toLowerCase() === "b";
1467
- const isS = e.code === "KeyS" || e.key.toLowerCase() === "s";
1468
- if (!isB && !isS) return;
1397
+ if (e.code !== "KeyB" && e.key.toLowerCase() !== "b") return;
1469
1398
  const target = e.target;
1470
1399
  if (target) {
1471
1400
  const tag = target.tagName;
1472
1401
  if (tag === "INPUT" || tag === "TEXTAREA" || target.isContentEditable) return;
1473
1402
  }
1474
1403
  e.preventDefault();
1475
- handlerRef.current(isS ? "screenshot" : "plain");
1404
+ handlerRef.current();
1476
1405
  }
1477
1406
  document.addEventListener("keydown", handleKeyDown);
1478
1407
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -1521,17 +1450,12 @@ function ShellHeader({
1521
1450
  const [bugOpen, setBugOpen] = useState7(false);
1522
1451
  const [bugShot, setBugShot] = useState7(null);
1523
1452
  const bugCaptureBusy = useRef6(false);
1524
- useBugReportShortcut(bugReport !== void 0, (variant) => {
1453
+ useBugReportShortcut(bugReport !== void 0, () => {
1525
1454
  if (bugOpen) {
1526
1455
  setBugOpen(false);
1527
1456
  return;
1528
1457
  }
1529
1458
  if (bugCaptureBusy.current) return;
1530
- if (variant === "plain") {
1531
- setBugShot(null);
1532
- setBugOpen(true);
1533
- return;
1534
- }
1535
1459
  bugCaptureBusy.current = true;
1536
1460
  captureTabScreenshot().then((shot) => setBugShot(shot)).catch(() => setBugShot(null)).finally(() => {
1537
1461
  bugCaptureBusy.current = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campfire-interactive/shell-header",
3
- "version": "0.15.3",
3
+ "version": "0.16.1",
4
4
  "description": "Shared shell header with app switcher for Campfire Suite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",