@campfire-interactive/shell-header 0.16.1 → 0.18.0

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 +298 -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 = [
@@ -83,6 +83,16 @@ var appCatalog = [
83
83
  group: "planning",
84
84
  localPort: 3200
85
85
  },
86
+ {
87
+ id: "pm",
88
+ name: "Program Management",
89
+ icon: "GanttChart",
90
+ letter: "P",
91
+ color: "#e11d48",
92
+ byline: "Run won business from award to launch \u2014 milestones, changes, and financial performance against the quote.",
93
+ group: "planning",
94
+ localPort: 3130
95
+ },
86
96
  // ── Engineering ─────────────────────────────────────────────────────────
87
97
  {
88
98
  id: "bom",
@@ -95,6 +105,16 @@ var appCatalog = [
95
105
  group: "engineering",
96
106
  localPort: 3700
97
107
  },
108
+ {
109
+ id: "plant",
110
+ name: "Plant Management",
111
+ icon: "Factory",
112
+ letter: "P",
113
+ color: "#65a30d",
114
+ byline: "The operational home for your plants \u2014 footprint, capacity, and the plant records every app reads.",
115
+ group: "engineering",
116
+ localPort: 3140
117
+ },
98
118
  // ── Platform services (available to everyone; no role gate) ─────────────
99
119
  {
100
120
  // Header title uses `name`; DMS wants its full name there, and it's hidden
@@ -133,6 +153,22 @@ var appCatalog = [
133
153
  requiresRole: ["admin", "owner"],
134
154
  localPort: 3600,
135
155
  path: "/admin/users"
156
+ },
157
+ {
158
+ // Cool charcoal (slate-700). Every other tile here is a saturated hue
159
+ // standing for a persona's product; integrations is plumbing that carries
160
+ // other apps' data, and a neutral says that better than a colour competing
161
+ // with them. Cool rather than warm so it sits with the blues above instead
162
+ // of reading as a dimmed orange.
163
+ id: "integrations",
164
+ name: "Integrations",
165
+ icon: "Cable",
166
+ letter: "I",
167
+ color: "#334155",
168
+ byline: "Connect Campfire to your ERP, PLM, and data-lake systems \u2014 inbound feeds and outbound extracts.",
169
+ group: "admin",
170
+ requiresRole: ["admin", "owner"],
171
+ localPort: 3110
136
172
  }
137
173
  ];
138
174
  var BASE_DOMAIN = "campfiresuite.com";
@@ -164,8 +200,11 @@ import {
164
200
  TrendingUp,
165
201
  BarChart3,
166
202
  Calculator,
203
+ Cable,
204
+ Factory,
167
205
  FileText,
168
206
  Files,
207
+ GanttChart,
169
208
  Shield,
170
209
  GripHorizontal,
171
210
  Bell,
@@ -183,8 +222,11 @@ var iconMap = {
183
222
  TrendingUp,
184
223
  BarChart3,
185
224
  Calculator,
225
+ Cable,
226
+ Factory,
186
227
  FileText,
187
228
  Files,
229
+ GanttChart,
188
230
  Shield
189
231
  };
190
232
  function getIcon(name) {
@@ -843,8 +885,13 @@ async function captureTabScreenshot() {
843
885
  var MAX_ENTRIES = 50;
844
886
  var MAX_MESSAGE_CHARS = 500;
845
887
  var consoleBuffer = [];
846
- var networkBuffer = [];
888
+ var networkFailureBuffer = [];
889
+ var networkSuccessBuffer = [];
847
890
  var installed = false;
891
+ function stripQuery(url) {
892
+ const i = url.indexOf("?");
893
+ return i === -1 ? url : url.slice(0, i);
894
+ }
848
895
  function push(buffer, entry) {
849
896
  buffer.push(entry);
850
897
  if (buffer.length > MAX_ENTRIES) buffer.shift();
@@ -893,7 +940,7 @@ function installBugReportCapture() {
893
940
  try {
894
941
  const res = await originalFetch(input, init);
895
942
  if (!res.ok) {
896
- push(networkBuffer, {
943
+ push(networkFailureBuffer, {
897
944
  at: (/* @__PURE__ */ new Date()).toISOString(),
898
945
  method: method.toUpperCase(),
899
946
  url,
@@ -901,10 +948,19 @@ function installBugReportCapture() {
901
948
  durationMs: Math.round(performance.now() - started),
902
949
  requestId: res.headers.get("x-request-id") ?? void 0
903
950
  });
951
+ } else {
952
+ push(networkSuccessBuffer, {
953
+ at: (/* @__PURE__ */ new Date()).toISOString(),
954
+ method: method.toUpperCase(),
955
+ url: stripQuery(url),
956
+ status: res.status,
957
+ durationMs: Math.round(performance.now() - started),
958
+ requestId: res.headers.get("x-request-id") ?? void 0
959
+ });
904
960
  }
905
961
  return res;
906
962
  } catch (err) {
907
- push(networkBuffer, {
963
+ push(networkFailureBuffer, {
908
964
  at: (/* @__PURE__ */ new Date()).toISOString(),
909
965
  method: method.toUpperCase(),
910
966
  url,
@@ -916,7 +972,10 @@ function installBugReportCapture() {
916
972
  };
917
973
  }
918
974
  function snapshotBugReportCapture() {
919
- return { console: [...consoleBuffer], network: [...networkBuffer] };
975
+ const network = [...networkFailureBuffer, ...networkSuccessBuffer].sort(
976
+ (a, b) => a.at.localeCompare(b.at)
977
+ );
978
+ return { console: [...consoleBuffer], network };
920
979
  }
921
980
 
922
981
  // src/BugReportModal.tsx
@@ -942,6 +1001,40 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
942
1001
  const [submitError, setSubmitError] = useState6(null);
943
1002
  const [issueUrl, setIssueUrl] = useState6(null);
944
1003
  const [linkCopied, setLinkCopied] = useState6(false);
1004
+ const cardRef = useRef4(null);
1005
+ const [cardOffset, setCardOffset] = useState6({ x: 0, y: 0 });
1006
+ const headerDragRef = useRef4(null);
1007
+ function onHeaderPointerDown(e) {
1008
+ if (e.target.closest("button")) return;
1009
+ const card = cardRef.current;
1010
+ if (!card) return;
1011
+ e.currentTarget.setPointerCapture(e.pointerId);
1012
+ headerDragRef.current = {
1013
+ pointerId: e.pointerId,
1014
+ startX: e.clientX,
1015
+ startY: e.clientY,
1016
+ baseX: cardOffset.x,
1017
+ baseY: cardOffset.y,
1018
+ rect: card.getBoundingClientRect()
1019
+ };
1020
+ }
1021
+ function onHeaderPointerMove(e) {
1022
+ const d = headerDragRef.current;
1023
+ if (!d || d.pointerId !== e.pointerId) return;
1024
+ let dx = e.clientX - d.startX;
1025
+ let dy = e.clientY - d.startY;
1026
+ const minX = 80 - d.rect.right;
1027
+ const maxX = window.innerWidth - 80 - d.rect.left;
1028
+ const minY = -d.rect.top;
1029
+ const maxY = window.innerHeight - 48 - d.rect.top;
1030
+ dx = Math.min(Math.max(dx, minX), maxX);
1031
+ dy = Math.min(Math.max(dy, minY), maxY);
1032
+ setCardOffset({ x: d.baseX + dx, y: d.baseY + dy });
1033
+ }
1034
+ function onHeaderPointerEnd(e) {
1035
+ if (headerDragRef.current?.pointerId !== e.pointerId) return;
1036
+ headerDragRef.current = null;
1037
+ }
945
1038
  const workCanvasRef = useRef4(null);
946
1039
  const originalRef = useRef4(null);
947
1040
  const displayCanvasRef = useRef4(null);
@@ -1165,197 +1258,215 @@ function BugReportModal({ config, initialScreenshot, onClose }) {
1165
1258
  "aria-label": "Report an issue",
1166
1259
  onPointerDown: (e) => e.stopPropagation(),
1167
1260
  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(
1261
+ children: /* @__PURE__ */ jsxs6(
1262
+ "div",
1263
+ {
1264
+ ref: cardRef,
1265
+ className: `cfi-sh-bug-card${view === "editor" ? " cfi-sh-bug-card-wide" : ""}`,
1266
+ style: cardOffset.x !== 0 || cardOffset.y !== 0 ? { transform: `translate(${cardOffset.x}px, ${cardOffset.y}px)` } : void 0,
1267
+ children: [
1268
+ /* @__PURE__ */ jsxs6(
1269
+ "div",
1270
+ {
1271
+ className: "cfi-sh-bug-header",
1272
+ onPointerDown: onHeaderPointerDown,
1273
+ onPointerMove: onHeaderPointerMove,
1274
+ onPointerUp: onHeaderPointerEnd,
1275
+ onPointerCancel: onHeaderPointerEnd,
1276
+ children: [
1277
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-title", children: submitState === "success" ? "Issue submitted" : view === "editor" ? "New Issue - Attach Screenshot (Optional)" : "Report an issue" }),
1278
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-env", children: config.context.environment }),
1279
+ /* @__PURE__ */ jsx6("button", { className: "cfi-sh-bug-close", type: "button", onClick: closeUnlessBusy, "aria-label": "Close", children: "\xD7" })
1280
+ ]
1281
+ }
1282
+ ),
1283
+ submitState === "success" ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1284
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body cfi-sh-bug-done", children: [
1285
+ /* @__PURE__ */ jsx6("p", { className: "cfi-sh-bug-done-msg", children: "Thanks for submitting this issue." }),
1286
+ issueUrl ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-done-link", children: [
1287
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-done-label", children: "You can view it here:" }),
1288
+ /* @__PURE__ */ jsx6(
1289
+ "a",
1290
+ {
1291
+ href: issueUrl,
1292
+ target: "_blank",
1293
+ rel: "noreferrer",
1294
+ className: "cfi-sh-bug-done-url",
1295
+ children: issueUrl
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ jsx6(
1299
+ "button",
1300
+ {
1301
+ type: "button",
1302
+ className: "cfi-sh-bug-copy-btn",
1303
+ title: "Copy link",
1304
+ "aria-label": "Copy link",
1305
+ onClick: () => {
1306
+ void navigator.clipboard.writeText(issueUrl).then(() => {
1307
+ setLinkCopied(true);
1308
+ window.setTimeout(() => setLinkCopied(false), 1500);
1309
+ });
1310
+ },
1311
+ children: linkCopied ? /* @__PURE__ */ jsx6(Check2, { size: 16 }) : /* @__PURE__ */ jsx6(Copy, { size: 16 })
1312
+ }
1313
+ )
1314
+ ] }) : null
1315
+ ] }),
1316
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-footer", children: /* @__PURE__ */ jsx6(
1190
1317
  "button",
1191
1318
  {
1192
1319
  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
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1321
+ onClick: closeUnlessBusy,
1322
+ children: "OK"
1203
1323
  }
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
+ ) })
1325
+ ] }) : view === "editor" ? /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1326
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-toolbar", children: [
1327
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1328
+ /* @__PURE__ */ jsx6(
1329
+ "button",
1330
+ {
1331
+ type: "button",
1332
+ className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1333
+ onClick: () => setMode("crop"),
1334
+ children: "Crop"
1335
+ }
1336
+ ),
1337
+ /* @__PURE__ */ jsx6(
1338
+ "button",
1339
+ {
1340
+ type: "button",
1341
+ className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1342
+ onClick: () => setMode("redact"),
1343
+ children: "Redact"
1344
+ }
1345
+ )
1346
+ ] }),
1347
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-hint", children: mode === "crop" ? "Drag to crop to a region" : "Drag to black out sensitive data" }),
1348
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-modes", children: [
1349
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: () => setView("form"), children: "Skip" }),
1350
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleEditorReset, children: "Reset" }),
1351
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary", onClick: handleEditorDone, children: "Done" })
1352
+ ] })
1353
+ ] }),
1354
+ /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-canvas-wrap", children: /* @__PURE__ */ jsx6(
1355
+ "canvas",
1356
+ {
1357
+ ref: displayCanvasRef,
1358
+ className: "cfi-sh-bug-canvas",
1359
+ onPointerDown: handlePointerDown,
1360
+ onPointerMove: handlePointerMove,
1361
+ onPointerUp: handlePointerUp
1362
+ }
1363
+ ) })
1364
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-body", children: [
1365
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-title-input", children: "Title" }),
1219
1366
  /* @__PURE__ */ jsx6(
1220
- "button",
1367
+ "input",
1221
1368
  {
1222
- type: "button",
1223
- className: `cfi-sh-bug-btn${mode === "crop" ? " cfi-sh-bug-btn-active" : ""}`,
1224
- onClick: () => setMode("crop"),
1225
- children: "Crop"
1369
+ id: "cfi-sh-bug-title-input",
1370
+ className: "cfi-sh-bug-input",
1371
+ value: title,
1372
+ onChange: (e) => setTitle(e.target.value),
1373
+ placeholder: "What went wrong?",
1374
+ maxLength: 200,
1375
+ autoFocus: true
1226
1376
  }
1227
1377
  ),
1378
+ /* @__PURE__ */ jsx6("label", { className: "cfi-sh-bug-label", htmlFor: "cfi-sh-bug-desc-input", children: "Details" }),
1228
1379
  /* @__PURE__ */ jsx6(
1229
- "button",
1380
+ "textarea",
1230
1381
  {
1231
- type: "button",
1232
- className: `cfi-sh-bug-btn${mode === "redact" ? " cfi-sh-bug-btn-active" : ""}`,
1233
- onClick: () => setMode("redact"),
1234
- children: "Redact"
1382
+ id: "cfi-sh-bug-desc-input",
1383
+ className: "cfi-sh-bug-textarea",
1384
+ value: description,
1385
+ onChange: (e) => setDescription(e.target.value),
1386
+ placeholder: "What did you expect? What happened instead? Steps to reproduce\u2026",
1387
+ rows: 4,
1388
+ maxLength: 1e4
1235
1389
  }
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" })
1390
+ ),
1391
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot", children: [
1392
+ screenshot ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
1393
+ /* @__PURE__ */ jsx6("img", { className: "cfi-sh-bug-thumb", src: screenshot.previewUrl, alt: "Screenshot preview" }),
1394
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-shot-actions", children: [
1395
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Retake" }),
1396
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleRemoveScreenshot, children: "Remove" })
1397
+ ] })
1398
+ ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: handleCapture, children: "Capture screenshot" }),
1399
+ captureError && /* @__PURE__ */ jsx6("div", { className: "cfi-sh-bug-error", children: captureError })
1400
+ ] }),
1401
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-files", children: [
1402
+ attachments.map((file, i) => /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-file-row", children: [
1403
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-name", title: file.name, children: file.name }),
1404
+ /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-file-size", children: formatFileSize(file.size) }),
1405
+ /* @__PURE__ */ jsx6(
1406
+ "button",
1407
+ {
1408
+ type: "button",
1409
+ className: "cfi-sh-bug-file-remove",
1410
+ onClick: () => handleRemoveAttachment(i),
1411
+ "aria-label": `Remove ${file.name}`,
1412
+ children: "\xD7"
1413
+ }
1414
+ )
1415
+ ] }, `${file.name}-${file.size}-${i}`)),
1416
+ /* @__PURE__ */ jsx6(
1417
+ "button",
1418
+ {
1419
+ type: "button",
1420
+ className: "cfi-sh-bug-btn",
1421
+ onClick: () => fileInputRef.current?.click(),
1422
+ children: "Attach files\u2026"
1423
+ }
1424
+ ),
1425
+ /* @__PURE__ */ jsx6(
1426
+ "input",
1427
+ {
1428
+ ref: fileInputRef,
1429
+ type: "file",
1430
+ multiple: true,
1431
+ style: { display: "none" },
1432
+ onChange: (e) => handleFilesPicked(e.target.files)
1433
+ }
1434
+ )
1435
+ ] }),
1436
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-meta", children: [
1437
+ "Includes: URL, app version, recent console errors and failed requests.",
1438
+ config.context.issuesUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1439
+ " ",
1440
+ /* @__PURE__ */ jsx6(
1441
+ "a",
1442
+ {
1443
+ className: "cfi-sh-bug-issues-link",
1444
+ href: config.context.issuesUrl,
1445
+ target: "_blank",
1446
+ rel: "noreferrer",
1447
+ children: "View known issues"
1448
+ }
1449
+ )
1450
+ ] })
1288
1451
  ] })
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) }),
1452
+ ] }),
1453
+ view === "form" && submitState !== "success" && /* @__PURE__ */ jsxs6("div", { className: "cfi-sh-bug-footer", children: [
1454
+ submitState === "error" && submitError && /* @__PURE__ */ jsx6("span", { className: "cfi-sh-bug-error", children: submitError }),
1455
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-sh-bug-btn", onClick: closeUnlessBusy, children: "Cancel" }),
1296
1456
  /* @__PURE__ */ jsx6(
1297
1457
  "button",
1298
1458
  {
1299
1459
  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"
1460
+ className: "cfi-sh-bug-btn cfi-sh-bug-btn-primary",
1461
+ onClick: handleSubmit,
1462
+ disabled: !title.trim() || submitState === "submitting",
1463
+ children: submitState === "submitting" ? "Submitting\u2026" : "Submit report"
1339
1464
  }
1340
1465
  )
1341
1466
  ] })
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
- ] })
1467
+ ]
1468
+ }
1469
+ )
1359
1470
  }
1360
1471
  );
1361
1472
  }
@@ -1394,14 +1505,16 @@ function useBugReportShortcut(enabled, onShortcut) {
1394
1505
  function handleKeyDown(e) {
1395
1506
  const platformMod = e.ctrlKey || e.metaKey;
1396
1507
  if (!platformMod || !e.altKey || e.shiftKey) return;
1397
- if (e.code !== "KeyB" && e.key.toLowerCase() !== "b") return;
1508
+ const isB = e.code === "KeyB" || e.key.toLowerCase() === "b";
1509
+ const isS = e.code === "KeyS" || e.key.toLowerCase() === "s";
1510
+ if (!isB && !isS) return;
1398
1511
  const target = e.target;
1399
1512
  if (target) {
1400
1513
  const tag = target.tagName;
1401
1514
  if (tag === "INPUT" || tag === "TEXTAREA" || target.isContentEditable) return;
1402
1515
  }
1403
1516
  e.preventDefault();
1404
- handlerRef.current();
1517
+ handlerRef.current(isS ? "screenshot" : "plain");
1405
1518
  }
1406
1519
  document.addEventListener("keydown", handleKeyDown);
1407
1520
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -1450,12 +1563,17 @@ function ShellHeader({
1450
1563
  const [bugOpen, setBugOpen] = useState7(false);
1451
1564
  const [bugShot, setBugShot] = useState7(null);
1452
1565
  const bugCaptureBusy = useRef6(false);
1453
- useBugReportShortcut(bugReport !== void 0, () => {
1566
+ useBugReportShortcut(bugReport !== void 0, (variant) => {
1454
1567
  if (bugOpen) {
1455
1568
  setBugOpen(false);
1456
1569
  return;
1457
1570
  }
1458
1571
  if (bugCaptureBusy.current) return;
1572
+ if (variant === "plain") {
1573
+ setBugShot(null);
1574
+ setBugOpen(true);
1575
+ return;
1576
+ }
1459
1577
  bugCaptureBusy.current = true;
1460
1578
  captureTabScreenshot().then((shot) => setBugShot(shot)).catch(() => setBugShot(null)).finally(() => {
1461
1579
  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.18.0",
4
4
  "description": "Shared shell header with app switcher for Campfire Suite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",