@campfire-interactive/shell-header 0.16.1 → 0.17.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.
Files changed (2) hide show
  1. package/dist/index.js +269 -180
  2. package/package.json +1 -1
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}\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 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");
28
28
 
29
29
  // src/appCatalog.ts
30
30
  var appCatalog = [
@@ -133,6 +133,17 @@ var appCatalog = [
133
133
  requiresRole: ["admin", "owner"],
134
134
  localPort: 3600,
135
135
  path: "/admin/users"
136
+ },
137
+ {
138
+ id: "integrations",
139
+ name: "Integrations",
140
+ icon: "Cable",
141
+ letter: "I",
142
+ color: "#0891b2",
143
+ byline: "Connect Campfire to your ERP, PLM, and data-lake systems \u2014 inbound feeds and outbound extracts.",
144
+ group: "admin",
145
+ requiresRole: ["admin", "owner"],
146
+ localPort: 3110
136
147
  }
137
148
  ];
138
149
  var BASE_DOMAIN = "campfiresuite.com";
@@ -164,6 +175,7 @@ import {
164
175
  TrendingUp,
165
176
  BarChart3,
166
177
  Calculator,
178
+ Cable,
167
179
  FileText,
168
180
  Files,
169
181
  Shield,
@@ -183,6 +195,7 @@ var iconMap = {
183
195
  TrendingUp,
184
196
  BarChart3,
185
197
  Calculator,
198
+ Cable,
186
199
  FileText,
187
200
  Files,
188
201
  Shield
@@ -843,8 +856,13 @@ async function captureTabScreenshot() {
843
856
  var MAX_ENTRIES = 50;
844
857
  var MAX_MESSAGE_CHARS = 500;
845
858
  var consoleBuffer = [];
846
- var networkBuffer = [];
859
+ var networkFailureBuffer = [];
860
+ var networkSuccessBuffer = [];
847
861
  var installed = false;
862
+ function stripQuery(url) {
863
+ const i = url.indexOf("?");
864
+ return i === -1 ? url : url.slice(0, i);
865
+ }
848
866
  function push(buffer, entry) {
849
867
  buffer.push(entry);
850
868
  if (buffer.length > MAX_ENTRIES) buffer.shift();
@@ -893,7 +911,7 @@ function installBugReportCapture() {
893
911
  try {
894
912
  const res = await originalFetch(input, init);
895
913
  if (!res.ok) {
896
- push(networkBuffer, {
914
+ push(networkFailureBuffer, {
897
915
  at: (/* @__PURE__ */ new Date()).toISOString(),
898
916
  method: method.toUpperCase(),
899
917
  url,
@@ -901,10 +919,19 @@ function installBugReportCapture() {
901
919
  durationMs: Math.round(performance.now() - started),
902
920
  requestId: res.headers.get("x-request-id") ?? void 0
903
921
  });
922
+ } else {
923
+ push(networkSuccessBuffer, {
924
+ at: (/* @__PURE__ */ new Date()).toISOString(),
925
+ method: method.toUpperCase(),
926
+ url: stripQuery(url),
927
+ status: res.status,
928
+ durationMs: Math.round(performance.now() - started),
929
+ requestId: res.headers.get("x-request-id") ?? void 0
930
+ });
904
931
  }
905
932
  return res;
906
933
  } catch (err) {
907
- push(networkBuffer, {
934
+ push(networkFailureBuffer, {
908
935
  at: (/* @__PURE__ */ new Date()).toISOString(),
909
936
  method: method.toUpperCase(),
910
937
  url,
@@ -916,7 +943,10 @@ function installBugReportCapture() {
916
943
  };
917
944
  }
918
945
  function snapshotBugReportCapture() {
919
- return { console: [...consoleBuffer], network: [...networkBuffer] };
946
+ const network = [...networkFailureBuffer, ...networkSuccessBuffer].sort(
947
+ (a, b) => a.at.localeCompare(b.at)
948
+ );
949
+ return { console: [...consoleBuffer], network };
920
950
  }
921
951
 
922
952
  // src/BugReportModal.tsx
@@ -942,6 +972,40 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
942
972
  const [submitError, setSubmitError] = useState6(null);
943
973
  const [issueUrl, setIssueUrl] = useState6(null);
944
974
  const [linkCopied, setLinkCopied] = useState6(false);
975
+ const cardRef = useRef4(null);
976
+ const [cardOffset, setCardOffset] = useState6({ x: 0, y: 0 });
977
+ const headerDragRef = useRef4(null);
978
+ function onHeaderPointerDown(e) {
979
+ if (e.target.closest("button")) return;
980
+ const card = cardRef.current;
981
+ if (!card) return;
982
+ e.currentTarget.setPointerCapture(e.pointerId);
983
+ headerDragRef.current = {
984
+ pointerId: e.pointerId,
985
+ startX: e.clientX,
986
+ startY: e.clientY,
987
+ baseX: cardOffset.x,
988
+ baseY: cardOffset.y,
989
+ rect: card.getBoundingClientRect()
990
+ };
991
+ }
992
+ function onHeaderPointerMove(e) {
993
+ const d = headerDragRef.current;
994
+ if (!d || d.pointerId !== e.pointerId) return;
995
+ let dx = e.clientX - d.startX;
996
+ let dy = e.clientY - d.startY;
997
+ const minX = 80 - d.rect.right;
998
+ const maxX = window.innerWidth - 80 - d.rect.left;
999
+ const minY = -d.rect.top;
1000
+ const maxY = window.innerHeight - 48 - d.rect.top;
1001
+ dx = Math.min(Math.max(dx, minX), maxX);
1002
+ dy = Math.min(Math.max(dy, minY), maxY);
1003
+ setCardOffset({ x: d.baseX + dx, y: d.baseY + dy });
1004
+ }
1005
+ function onHeaderPointerEnd(e) {
1006
+ if (headerDragRef.current?.pointerId !== e.pointerId) return;
1007
+ headerDragRef.current = null;
1008
+ }
945
1009
  const workCanvasRef = useRef4(null);
946
1010
  const originalRef = useRef4(null);
947
1011
  const displayCanvasRef = useRef4(null);
@@ -1165,197 +1229,215 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
1165
1229
  "aria-label": "Report an issue",
1166
1230
  onPointerDown: (e) => e.stopPropagation(),
1167
1231
  onFocus: (e) => e.stopPropagation(),
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",
1181
- {
1182
- href: issueUrl,
1183
- target: "_blank",
1184
- rel: "noreferrer",
1185
- className: "cfi-sh-bug-done-url",
1186
- children: issueUrl
1187
- }
1188
- ),
1189
- /* @__PURE__ */ jsx6(
1232
+ children: /* @__PURE__ */ jsxs6(
1233
+ "div",
1234
+ {
1235
+ ref: cardRef,
1236
+ className: `cfi-sh-bug-card${view === "editor" ? " cfi-sh-bug-card-wide" : ""}`,
1237
+ style: cardOffset.x !== 0 || cardOffset.y !== 0 ? { transform: `translate(${cardOffset.x}px, ${cardOffset.y}px)` } : void 0,
1238
+ children: [
1239
+ /* @__PURE__ */ jsxs6(
1240
+ "div",
1241
+ {
1242
+ className: "cfi-sh-bug-header",
1243
+ onPointerDown: onHeaderPointerDown,
1244
+ onPointerMove: onHeaderPointerMove,
1245
+ onPointerUp: onHeaderPointerEnd,
1246
+ onPointerCancel: onHeaderPointerEnd,
1247
+ children: [
1248
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-title", children: submitState === "success" ? "Issue submitted" : view === "editor" ? "New Issue - Attach Screenshot (Optional)" : "Report an issue" }),
1249
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-env", children: config.context.environment }),
1250
+ /* @__PURE__ */ jsx6("button", { className: "cfi-sh-bug-close", type: "button", onClick: closeUnlessBusy, "aria-label": "Close", children: "\xD7" })
1251
+ ]
1252
+ }
1253
+ ),
1254
+ submitState === "success" ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1255
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body cfi-sh-bug-done", children: [
1256
+ /* @__PURE__ */ jsx6("p", { className: "cfi-sh-bug-done-msg", children: "Thanks for submitting this issue." }),
1257
+ issueUrl ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-done-link", children: [
1258
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-done-label", children: "You can view it here:" }),
1259
+ /* @__PURE__ */ jsx6(
1260
+ "a",
1261
+ {
1262
+ href: issueUrl,
1263
+ target: "_blank",
1264
+ rel: "noreferrer",
1265
+ className: "cfi-sh-bug-done-url",
1266
+ children: issueUrl
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsx6(
1270
+ "button",
1271
+ {
1272
+ type: "button",
1273
+ className: "cfi-sh-bug-copy-btn",
1274
+ title: "Copy link",
1275
+ "aria-label": "Copy link",
1276
+ onClick: () => {
1277
+ void navigator.clipboard.writeText(issueUrl).then(() => {
1278
+ setLinkCopied(true);
1279
+ window.setTimeout(() => setLinkCopied(false), 1500);
1280
+ });
1281
+ },
1282
+ children: linkCopied ? /* @__PURE__ */ jsx6(Check2, { size: 16 }) : /* @__PURE__ */ jsx6(Copy, { size: 16 })
1283
+ }
1284
+ )
1285
+ ] }) : null
1286
+ ] }),
1287
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-footer", children: /* @__PURE__ */ jsx6(
1190
1288
  "button",
1191
1289
  {
1192
1290
  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 })
1291
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1292
+ onClick: closeUnlessBusy,
1293
+ children: "OK"
1203
1294
  }
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: [
1295
+ ) })
1296
+ ] }) : view === "editor" ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1297
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-toolbar", children: [
1298
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1299
+ /* @__PURE__ */ jsx6(
1300
+ "button",
1301
+ {
1302
+ type: "button",
1303
+ className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1304
+ onClick: () => setMode("crop"),
1305
+ children: "Crop"
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ jsx6(
1309
+ "button",
1310
+ {
1311
+ type: "button",
1312
+ className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1313
+ onClick: () => setMode("redact"),
1314
+ children: "Redact"
1315
+ }
1316
+ )
1317
+ ] }),
1318
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-hint", children: mode === "crop" ? "Drag to crop to a region" : "Drag to black out sensitive data" }),
1319
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1320
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: () => setView("form"), children: "Skip" }),
1321
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleEditorReset, children: "Reset" }),
1322
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary", onClick: handleEditorDone, children: "Done" })
1323
+ ] })
1324
+ ] }),
1325
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-canvas-wrap", children: /* @__PURE__ */ jsx6(
1326
+ "canvas",
1327
+ {
1328
+ ref: displayCanvasRef,
1329
+ className: "cfi-sh-bug-canvas",
1330
+ onPointerDown: handlePointerDown,
1331
+ onPointerMove: handlePointerMove,
1332
+ onPointerUp: handlePointerUp
1333
+ }
1334
+ ) })
1335
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1336
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-title-input", children: "Title" }),
1219
1337
  /* @__PURE__ */ jsx6(
1220
- "button",
1338
+ "input",
1221
1339
  {
1222
- type: "button",
1223
- className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1224
- onClick: () => setMode("crop"),
1225
- children: "Crop"
1340
+ id: "cfi-sh-bug-title-input",
1341
+ className: "cfi-sh-bug-input",
1342
+ value: title,
1343
+ onChange: (e) => setTitle(e.target.value),
1344
+ placeholder: "What went wrong?",
1345
+ maxLength: 200,
1346
+ autoFocus: true
1226
1347
  }
1227
1348
  ),
1349
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-desc-input", children: "Details" }),
1228
1350
  /* @__PURE__ */ jsx6(
1229
- "button",
1351
+ "textarea",
1230
1352
  {
1231
- type: "button",
1232
- className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1233
- onClick: () => setMode("redact"),
1234
- children: "Redact"
1353
+ id: "cfi-sh-bug-desc-input",
1354
+ className: "cfi-sh-bug-textarea",
1355
+ value: description,
1356
+ onChange: (e) => setDescription(e.target.value),
1357
+ placeholder: "What did you expect? What happened instead? Steps to reproduce\u2026",
1358
+ rows: 4,
1359
+ maxLength: 1e4
1235
1360
  }
1236
- )
1237
- ] }),
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" })
1361
+ ),
1362
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot", children: [
1363
+ screenshot ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1364
+ /* @__PURE__ */ jsx6("img", { className: "cfi-sh-bug-thumb", src: screenshot.previewUrl, alt: "Screenshot preview" }),
1365
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot-actions", children: [
1366
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Retake" }),
1367
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleRemoveScreenshot, children: "Remove" })
1368
+ ] })
1369
+ ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Capture screenshot" }),
1370
+ captureError && /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-error", children: captureError })
1371
+ ] }),
1372
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-files", children: [
1373
+ attachments.map((file, i) => /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-file-row", children: [
1374
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-name", title: file.name, children: file.name }),
1375
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-size", children: formatFileSize(file.size) }),
1376
+ /* @__PURE__ */ jsx6(
1377
+ "button",
1378
+ {
1379
+ type: "button",
1380
+ className: "cfi-sh-bug-file-remove",
1381
+ onClick: () => handleRemoveAttachment(i),
1382
+ "aria-label": `Remove ${file.name}`,
1383
+ children: "\xD7"
1384
+ }
1385
+ )
1386
+ ] }, `${file.name}-${file.size}-${i}`)),
1387
+ /* @__PURE__ */ jsx6(
1388
+ "button",
1389
+ {
1390
+ type: "button",
1391
+ className: "cfi-sh-bug-btn",
1392
+ onClick: () => fileInputRef.current?.click(),
1393
+ children: "Attach files\u2026"
1394
+ }
1395
+ ),
1396
+ /* @__PURE__ */ jsx6(
1397
+ "input",
1398
+ {
1399
+ ref: fileInputRef,
1400
+ type: "file",
1401
+ multiple: true,
1402
+ style: { display: "none" },
1403
+ onChange: (e) => handleFilesPicked(e.target.files)
1404
+ }
1405
+ )
1406
+ ] }),
1407
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-meta", children: [
1408
+ "Includes: URL, app version, recent console errors and failed requests.",
1409
+ config.context.issuesUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1410
+ " ",
1411
+ /* @__PURE__ */ jsx6(
1412
+ "a",
1413
+ {
1414
+ className: "cfi-sh-bug-issues-link",
1415
+ href: config.context.issuesUrl,
1416
+ target: "_blank",
1417
+ rel: "noreferrer",
1418
+ children: "View known issues"
1419
+ }
1420
+ )
1421
+ ] })
1288
1422
  ] })
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) }),
1423
+ ] }),
1424
+ view === "form" && submitState !== "success" && /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-footer", children: [
1425
+ submitState === "error" && submitError && /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-error", children: submitError }),
1426
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: closeUnlessBusy, children: "Cancel" }),
1296
1427
  /* @__PURE__ */ jsx6(
1297
1428
  "button",
1298
1429
  {
1299
1430
  type: "button",
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"
1431
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1432
+ onClick: handleSubmit,
1433
+ disabled: !title.trim() || submitState === "submitting",
1434
+ children: submitState === "submitting" ? "Submitting\u2026" : "Submit report"
1339
1435
  }
1340
1436
  )
1341
1437
  ] })
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
- ] })
1438
+ ]
1439
+ }
1440
+ )
1359
1441
  }
1360
1442
  );
1361
1443
  }
@@ -1394,14 +1476,16 @@ function useBugReportShortcut(enabled, onShortcut) {
1394
1476
  function handleKeyDown(e) {
1395
1477
  const platformMod = e.ctrlKey || e.metaKey;
1396
1478
  if (!platformMod || !e.altKey || e.shiftKey) return;
1397
- if (e.code !== "KeyB" && e.key.toLowerCase() !== "b") return;
1479
+ const isB = e.code === "KeyB" || e.key.toLowerCase() === "b";
1480
+ const isS = e.code === "KeyS" || e.key.toLowerCase() === "s";
1481
+ if (!isB && !isS) return;
1398
1482
  const target = e.target;
1399
1483
  if (target) {
1400
1484
  const tag = target.tagName;
1401
1485
  if (tag === "INPUT" || tag === "TEXTAREA" || target.isContentEditable) return;
1402
1486
  }
1403
1487
  e.preventDefault();
1404
- handlerRef.current();
1488
+ handlerRef.current(isS ? "screenshot" : "plain");
1405
1489
  }
1406
1490
  document.addEventListener("keydown", handleKeyDown);
1407
1491
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -1450,12 +1534,17 @@ function ShellHeader({
1450
1534
  const [bugOpen, setBugOpen] = useState7(false);
1451
1535
  const [bugShot, setBugShot] = useState7(null);
1452
1536
  const bugCaptureBusy = useRef6(false);
1453
- useBugReportShortcut(bugReport !== void 0, () => {
1537
+ useBugReportShortcut(bugReport !== void 0, (variant) => {
1454
1538
  if (bugOpen) {
1455
1539
  setBugOpen(false);
1456
1540
  return;
1457
1541
  }
1458
1542
  if (bugCaptureBusy.current) return;
1543
+ if (variant === "plain") {
1544
+ setBugShot(null);
1545
+ setBugOpen(true);
1546
+ return;
1547
+ }
1459
1548
  bugCaptureBusy.current = true;
1460
1549
  captureTabScreenshot().then((shot) => setBugShot(shot)).catch(() => setBugShot(null)).finally(() => {
1461
1550
  bugCaptureBusy.current = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campfire-interactive/shell-header",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "description": "Shared shell header with app switcher for Campfire Suite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",