@red-hat-developer-hub/backstage-plugin-intelligent-assistant 5.1.0 → 5.3.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.
- package/CHANGELOG.md +24 -0
- package/README.md +40 -5
- package/dist/alpha.d.ts +61 -0
- package/dist/api/LightspeedApiClient.esm.js +57 -0
- package/dist/api/LightspeedApiClient.esm.js.map +1 -1
- package/dist/api/api.esm.js.map +1 -1
- package/dist/components/DeleteModal.esm.js +2 -0
- package/dist/components/DeleteModal.esm.js.map +1 -1
- package/dist/components/DeleteSavedPromptModal.esm.js +75 -0
- package/dist/components/DeleteSavedPromptModal.esm.js.map +1 -0
- package/dist/components/LightSpeedChat.esm.js +276 -134
- package/dist/components/LightSpeedChat.esm.js.map +1 -1
- package/dist/components/LightspeedChatBoxHeader.esm.js +57 -2
- package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
- package/dist/components/LightspeedChatContainer.esm.js +2 -1
- package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
- package/dist/components/LightspeedDrawerContext.esm.js.map +1 -1
- package/dist/components/LightspeedDrawerProvider.esm.js +1 -0
- package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
- package/dist/components/McpServersSettings.esm.js +9 -22
- package/dist/components/McpServersSettings.esm.js.map +1 -1
- package/dist/components/MessageBarModelSelector.esm.js +104 -12
- package/dist/components/MessageBarModelSelector.esm.js.map +1 -1
- package/dist/components/SavedPromptCard.esm.js +100 -0
- package/dist/components/SavedPromptCard.esm.js.map +1 -0
- package/dist/components/SavedPromptKebabMenu.esm.js +87 -0
- package/dist/components/SavedPromptKebabMenu.esm.js.map +1 -0
- package/dist/components/SavedPromptMenuItems.esm.js +52 -0
- package/dist/components/SavedPromptMenuItems.esm.js.map +1 -0
- package/dist/components/SavedPromptsEmptyState.esm.js +63 -0
- package/dist/components/SavedPromptsEmptyState.esm.js.map +1 -0
- package/dist/components/SavedPromptsList.esm.js +63 -0
- package/dist/components/SavedPromptsList.esm.js.map +1 -0
- package/dist/components/SavedPromptsSettings.esm.js +285 -0
- package/dist/components/SavedPromptsSettings.esm.js.map +1 -0
- package/dist/components/ScreenContextChip.esm.js +172 -0
- package/dist/components/ScreenContextChip.esm.js.map +1 -0
- package/dist/components/SettingsPanel.esm.js +135 -0
- package/dist/components/SettingsPanel.esm.js.map +1 -0
- package/dist/hooks/screenContextLabelSubscription.esm.js +113 -0
- package/dist/hooks/screenContextLabelSubscription.esm.js.map +1 -0
- package/dist/hooks/useConversationHistoryGroups.esm.js +71 -0
- package/dist/hooks/useConversationHistoryGroups.esm.js.map +1 -0
- package/dist/hooks/useFeedbackActions.esm.js.map +1 -1
- package/dist/hooks/useLightspeedProviderState.esm.js +19 -5
- package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
- package/dist/hooks/useSavedPromptActions.esm.js +64 -0
- package/dist/hooks/useSavedPromptActions.esm.js.map +1 -0
- package/dist/hooks/useSavedPrompts.esm.js +75 -0
- package/dist/hooks/useSavedPrompts.esm.js.map +1 -0
- package/dist/hooks/useSavedPromptsSettings.esm.js +65 -0
- package/dist/hooks/useSavedPromptsSettings.esm.js.map +1 -0
- package/dist/hooks/useScreenContextLabelRevision.esm.js +13 -0
- package/dist/hooks/useScreenContextLabelRevision.esm.js.map +1 -0
- package/dist/hooks/useScreenContextSettings.esm.js +110 -0
- package/dist/hooks/useScreenContextSettings.esm.js.map +1 -0
- package/dist/hooks/useSettingsPanelUrlState.esm.js +95 -0
- package/dist/hooks/useSettingsPanelUrlState.esm.js.map +1 -0
- package/dist/hooks/useWelcomePrompts.esm.js +16 -5
- package/dist/hooks/useWelcomePrompts.esm.js.map +1 -1
- package/dist/hooks/useWindowLocation.esm.js +28 -0
- package/dist/hooks/useWindowLocation.esm.js.map +1 -0
- package/dist/translations/de.esm.js +64 -1
- package/dist/translations/de.esm.js.map +1 -1
- package/dist/translations/es.esm.js +64 -1
- package/dist/translations/es.esm.js.map +1 -1
- package/dist/translations/fr.esm.js +64 -1
- package/dist/translations/fr.esm.js.map +1 -1
- package/dist/translations/it.esm.js +64 -1
- package/dist/translations/it.esm.js.map +1 -1
- package/dist/translations/ja.esm.js +64 -1
- package/dist/translations/ja.esm.js.map +1 -1
- package/dist/translations/ref.esm.js +64 -2
- package/dist/translations/ref.esm.js.map +1 -1
- package/dist/utils/buildConversationHistoryGroups.esm.js +241 -0
- package/dist/utils/buildConversationHistoryGroups.esm.js.map +1 -0
- package/dist/utils/dom-selectors.esm.js +22 -2
- package/dist/utils/dom-selectors.esm.js.map +1 -1
- package/dist/utils/prompt-utils.esm.js +18 -1
- package/dist/utils/prompt-utils.esm.js.map +1 -1
- package/dist/utils/screen-capture.esm.js +116 -0
- package/dist/utils/screen-capture.esm.js.map +1 -0
- package/dist/utils/screen-context-utils.esm.js +343 -0
- package/dist/utils/screen-context-utils.esm.js.map +1 -0
- package/dist/utils/sensitive-data-redactor.esm.js +39 -1
- package/dist/utils/sensitive-data-redactor.esm.js.map +1 -1
- package/package.json +5 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-selectors.esm.js","sources":["../../src/utils/dom-selectors.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Centralized CSS selectors for DOM extraction.\n * Library-specific selectors (MUI, BUI, PF) are grouped here so they can be\n * updated in one place when BUI equivalents land upstream.\n *\n * MUI selectors use [class*=\"...\"] because JSS emits hashed class names\n * (e.g. MuiFormControl-root-12213) that do not match exact class selectors.\n */\n\n// ── Noise / exclusion ──\n\nexport const NOISE_SELECTORS = [\n '.pf-chatbot',\n 'script',\n 'style',\n 'noscript',\n 'svg',\n 'canvas',\n 'img',\n 'video',\n 'iframe',\n '[aria-hidden=\"true\"]:not([aria-label^=\"Status\"])',\n '[data-screen-capture-exclude]',\n '[hidden]',\n 'footer',\n '[class*=\"MuiCollapse-hidden\"]',\n '[data-testid=\"techdocs-native-shadowroot\"]',\n] as const;\n\nexport const NOISE_SELECTOR = NOISE_SELECTORS.join(', ');\n\n// ── Header ──\n\nexport const PLUGIN_NAME_SELECTOR = '.bui-PluginHeaderToolbarName';\nexport const PAGE_TITLE_SELECTOR = '.bui-HeaderTitle';\nexport const ACTIVE_TAB_SELECTOR = '[role=\"tab\"][aria-selected=\"true\"]';\nexport const BREADCRUMB_SELECTOR =\n '[aria-label=\"breadcrumb\"], [class*=\"MuiBreadcrumbs-root\"], [class*=\"bui-HeaderBreadcrumb\"]';\nexport const ACTIVE_NAV_SELECTOR = '[aria-current=\"page\"]';\n\n// ── Tabs ──\n\nexport const TAB_LIST_SELECTOR = '[role=\"tablist\"]';\nexport const TAB_SELECTOR = '[role=\"tab\"]';\nexport const BUI_NAV_SELECTOR =\n 'nav[aria-label*=\"navigation\" i], nav[class*=\"bui-HeaderNav\"]';\nexport const BUI_NAV_ITEM_SELECTOR = 'a[class*=\"NavItem\"], a[href]';\n\n// ── Dialog / overlay ──\n\nexport const MODAL_DIALOG_SELECTOR = '[role=\"dialog\"][aria-modal=\"true\"]';\nexport const DIALOG_TITLE_SELECTOR =\n '[class*=\"MuiDialogTitle\"], [class*=\"bui-DialogHeaderTitle\"]';\nexport const DIALOG_CONTENT_SELECTOR =\n '[class*=\"MuiDialogContent\"], [class*=\"bui-DialogBody\"]';\nexport const DRAWER_PAPER_SELECTOR = '[class*=\"MuiDrawer-paper\"]';\nexport const DOCKED_DRAWER_BODY_CLASS = 'docked-drawer-open';\n\n// ── Alerts ──\n\nexport const ALERT_SELECTOR =\n '[role=\"alert\"], [aria-live=\"assertive\"], [class*=\"MuiAlert-root\"], [class*=\"bui-Alert\"]';\n\n// ── Stepper ──\n\nexport const STEPPER_ROOT_SELECTOR = '[class*=\"MuiStepper-root\"]';\nexport const STEPPER_LABEL_SELECTOR =\n '[class*=\"MuiStepLabel-label\"]:not([class*=\"labelContainer\"])';\nexport const STEPPER_ACTIVE_CLASS = 'Mui-active';\nexport const STEPPER_COMPLETED_CLASS = 'Mui-completed';\n\n// ── Status ──\n\nexport const STATUS_INDICATOR_SELECTOR = '[aria-label^=\"Status\"]';\n\n// ── Forms ──\n\nexport const RJSF_FORM_SELECTOR = 'form.rjsf, form[class*=\"rjsf\"]';\nexport const FORM_CONTROL_SELECTOR =\n '[class*=\"MuiFormControl-root\"], [class*=\"MuiTextField-root\"], [class*=\"bui-TextField\"]';\nexport const FORM_HELPER_SELECTOR =\n '[class*=\"MuiFormHelperText-root\"], [slot=\"description\"]';\nexport const AUTOCOMPLETE_ROOT_SELECTOR = '[class*=\"MuiAutocomplete-root\"]';\nexport const TYPOGRAPHY_SELECTOR = '[class*=\"MuiTypography\"]';\nexport const CHIP_LABEL_SELECTOR = '[class*=\"MuiChip-label\"]';\nexport const SELECT_DISPLAY_SELECTOR =\n '[class*=\"MuiSelect-select\"]:not([class*=\"MuiTablePagination-select\"])';\nexport const SELECT_NATIVE_INPUT_SELECTOR = '[class*=\"MuiSelect-nativeInput\"]';\nexport const FORM_CONTROL_LABEL_SELECTOR =\n '[class*=\"MuiFormControlLabel-label\"]';\nexport const FORM_LABEL_SELECTOR =\n '[class*=\"MuiFormLabel-root\"], [class*=\"MuiInputLabel-root\"]';\n\n/** Nodes stripped when serializing form controls to compact HTML */\nexport const FORM_HTML_STRIP_SELECTOR = [\n 'script',\n 'style',\n 'svg',\n 'input[aria-hidden=\"true\"]',\n '[aria-hidden=\"true\"]',\n '[class*=\"MuiTouchRipple-root\"]',\n '[class*=\"MuiInputAdornment-root\"]',\n 'fieldset',\n 'button',\n].join(', ');\n\n// ── Search ──\n\nexport const SEARCH_INPUT_SELECTOR =\n 'input[placeholder*=\"earch\" i], input[aria-label*=\"earch\" i], [aria-label=\"search\" i] input';\n\n// ── Filters ──\n\nexport const FILTER_CONTAINER_SELECTOR = '[data-testid*=\"filter\"]';\nexport const FILTER_NAV_SELECTOR =\n 'nav[aria-label*=\"filter\"], [class*=\"MuiAccordionDetails-root\"] nav';\nexport const ACCORDION_ROOT_SELECTOR = '[class*=\"MuiAccordion-root\"]';\nexport const ACCORDION_DETAILS_SELECTOR = '[class*=\"MuiAccordionDetails-root\"]';\nexport const ACCORDION_SUMMARY_CONTENT_SELECTOR =\n '[class*=\"MuiAccordionSummary-content\"]';\nexport const FILTER_BOX_SELECTOR = '[class*=\"MuiBox-root\"]';\nexport const SELECTED_ITEM_SELECTOR = '[class*=\"Mui-selected\"]';\nexport const LIST_ITEM_TEXT_PRIMARY_SELECTOR =\n '[class*=\"MuiListItemText-primary\"]';\nexport const LIST_ITEM_TEXT_SECONDARY_SELECTOR =\n '[class*=\"MuiListItemText-secondary\"]';\nexport const MENU_SELECTOR = 'ul[role=\"menu\"]';\nexport const CARD_ROOT_SELECTOR = '[class*=\"MuiCard-root\"]';\nexport const LIST_ITEM_TEXT_ROOT_SELECTOR = '[class*=\"MuiListItemText-root\"]';\n\n// ── Pagination ──\n\nexport const PAGINATION_ROOT_SELECTOR =\n '[class*=\"MuiTablePagination-root\"], [class*=\"TablePagination\"], [class*=\"bui-TablePagination\"]';\nexport const PAGINATION_ACTIONS_SELECTOR =\n '[class*=\"MuiTablePagination-actions\"] button:not([disabled]), [class*=\"PaginationActions\"] button:not([disabled])';\n\n// ── Cards / Dashboard layout ──\n\nexport const CARD_LAYOUT_SELECTOR = [\n '[class*=\"MuiMasonry-root\"]',\n '[class*=\"MuiGrid-container\"]',\n '[class*=\"pf-l-gallery\"]',\n '[class*=\"bui-Grid\"]',\n].join(', ');\n\nexport const CARD_ITEM_SELECTOR = [\n '[class*=\"MuiPaper-root\"]',\n '[class*=\"MuiCard-root\"]',\n '.pf-v5-c-card, [class*=\"pf-c-card\"]',\n '[class*=\"bui-Card\"]',\n].join(', ');\n\nexport const CARD_TITLE_SELECTOR =\n 'h3, h4, h5, h6, [class*=\"CardHeader-title\"]';\n\nexport const RECHARTS_LEGEND_SELECTOR =\n '.recharts-legend-wrapper p, .recharts-legend-wrapper span[class*=\"Text\"]';\n\n// ── Empty state ──\n\nexport const EMPTY_STATE_SELECTOR = '[class*=\"BackstageEmptyState\"]';\nexport const EMPTY_STATE_BODY_SELECTOR = '[class*=\"body1\"]';\n\n// ── Shadow DOM (TechDocs) ──\n\nexport const SHADOW_HOST_SELECTOR =\n '[data-testid=\"techdocs-native-shadowroot\"], [class*=\"shadowDom\" i]';\nexport const SHADOW_NOISE_SELECTOR =\n 'style, script, link, noscript, svg, img, template';\n"],"names":[],"mappings":"AA2BO,MAAM,eAAA,GAAkB;AAAA,EAC7B,aAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,kDAAA;AAAA,EACA,+BAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,+BAAA;AAAA,EACA;AACF;AAEO,MAAM,cAAA,GAAiB,eAAA,CAAgB,IAAA,CAAK,IAAI;AAIhD,MAAM,oBAAA,GAAuB;AAC7B,MAAM,mBAAA,GAAsB;AAC5B,MAAM,mBAAA,GAAsB;AAC5B,MAAM,mBAAA,GACX;AACK,MAAM,mBAAA,GAAsB;AAI5B,MAAM,iBAAA,GAAoB;AAC1B,MAAM,YAAA,GAAe;AACrB,MAAM,gBAAA,GACX;AACK,MAAM,qBAAA,GAAwB;AAI9B,MAAM,qBAAA,GAAwB;AAC9B,MAAM,qBAAA,GACX;AACK,MAAM,uBAAA,GACX;AACK,MAAM,qBAAA,GAAwB;AAC9B,MAAM,wBAAA,GAA2B;AAIjC,MAAM,cAAA,GACX;AAIK,MAAM,qBAAA,GAAwB;AAC9B,MAAM,sBAAA,GACX;AACK,MAAM,oBAAA,GAAuB;AAC7B,MAAM,uBAAA,GAA0B;AAIhC,MAAM,yBAAA,GAA4B;AAIlC,MAAM,kBAAA,GAAqB;AAC3B,MAAM,qBAAA,GACX;AAKK,MAAM,mBAAA,GAAsB;AAC5B,MAAM,uBAAA,GACX;AAEK,MAAM,2BAAA,GACX;AACK,MAAM,mBAAA,GACX;AAGK,MAAM,wBAAA,GAA2B;AAAA,EACtC,QAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,2BAAA;AAAA,EACA,sBAAA;AAAA,EACA,gCAAA;AAAA,EACA,mCAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,IAAI;AAIJ,MAAM,qBAAA,GACX;AAIK,MAAM,yBAAA,GAA4B;AAClC,MAAM,mBAAA,GACX;AACK,MAAM,uBAAA,GAA0B;AAChC,MAAM,0BAAA,GAA6B;AACnC,MAAM,kCAAA,GACX;AACK,MAAM,mBAAA,GAAsB;AAC5B,MAAM,sBAAA,GAAyB;AAC/B,MAAM,+BAAA,GACX;AACK,MAAM,iCAAA,GACX;AACK,MAAM,aAAA,GAAgB;AACtB,MAAM,kBAAA,GAAqB;AAC3B,MAAM,4BAAA,GAA+B;AAIrC,MAAM,wBAAA,GACX;AACK,MAAM,2BAAA,GACX;AAIK,MAAM,oBAAA,GAAuB;AAAA,EAClC,4BAAA;AAAA,EACA,8BAAA;AAAA,EACA,yBAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,IAAI;AAEJ,MAAM,kBAAA,GAAqB;AAAA,EAChC,0BAAA;AAAA,EACA,yBAAA;AAAA,EACA,qCAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,IAAI;AAEJ,MAAM,mBAAA,GACX;AAEK,MAAM,wBAAA,GACX;AAIK,MAAM,oBAAA,GAAuB;AAC7B,MAAM,yBAAA,GAA4B;AAIlC,MAAM,oBAAA,GACX;AACK,MAAM,qBAAA,GACX;;;;"}
|
|
1
|
+
{"version":3,"file":"dom-selectors.esm.js","sources":["../../src/utils/dom-selectors.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Centralized CSS selectors for DOM extraction.\n * Library-specific selectors (MUI, BUI, PF) are grouped here so they can be\n * updated in one place when BUI equivalents land upstream.\n *\n * MUI selectors use [class*=\"...\"] because JSS emits hashed class names\n * (e.g. MuiFormControl-root-12213) that do not match exact class selectors.\n */\n\n// ── Noise / exclusion ──\n\nexport const NOISE_SELECTORS = [\n '.pf-chatbot',\n 'script',\n 'style',\n 'noscript',\n 'svg',\n 'canvas',\n 'img',\n 'video',\n 'iframe',\n '[aria-hidden=\"true\"]:not([aria-label^=\"Status\"])',\n '[data-screen-capture-exclude]',\n '[hidden]',\n 'footer',\n '[class*=\"MuiCollapse-hidden\"]',\n '[data-testid=\"techdocs-native-shadowroot\"]',\n] as const;\n\nexport const NOISE_SELECTOR = NOISE_SELECTORS.join(', ');\n\n// ── Header ──\n\nexport const PLUGIN_NAME_SELECTOR = '.bui-PluginHeaderToolbarName';\n/** BUI NFS plugin header name area (hashed class names). */\nexport const PLUGIN_HEADER_TOOLBAR_NAME_SELECTOR =\n '.bui-PluginHeaderToolbarName, [class*=\"bui-PluginHeaderToolbarName\"]';\n/** Breadcrumbs inside BUI plugin header toolbar (Settings → General, etc.). */\nexport const PLUGIN_HEADER_BREADCRUMBS_SELECTOR =\n '#Breadcrumbs, nav[aria-label=\"Breadcrumbs\"], nav[aria-label=\"breadcrumb\" i], [class*=\"PluginHeaderBreadcrumbs\"]';\nexport const PAGE_TITLE_SELECTOR = '.bui-HeaderTitle';\n\n/** BUI / NFS header title (class may be hashed). */\nexport const PAGE_TITLE_SCOPED_SELECTORS = [\n PAGE_TITLE_SELECTOR,\n '[class*=\"bui-HeaderTitle\"]',\n] as const;\n\n/** Software template entity page (scaffolder) info card title. */\nexport const TEMPLATE_INFO_CARD_TITLE_SELECTORS = [\n '[class*=\"BackstageInfoCard-headerTitle\"]',\n '[class*=\"MuiCardHeader-title\"]',\n] as const;\n\n/** Legacy Backstage core-components Header title. */\nexport const LEGACY_PAGE_TITLE_SELECTORS = [\n '[data-testid=\"header-title\"]',\n '[class*=\"Header-title\"]',\n 'header h1',\n] as const;\n\n/** Roots to search for page chrome (exclude Lightspeed overlay). */\nexport const PAGE_CHROME_ROOT_SELECTORS = [\n '#root main',\n 'main[class*=\"BackstagePage-root\"]',\n '#root',\n] as const;\nexport const ACTIVE_TAB_SELECTOR = '[role=\"tab\"][aria-selected=\"true\"]';\nexport const BREADCRUMB_SELECTOR =\n '[aria-label=\"Breadcrumbs\"], [aria-label=\"breadcrumb\" i], [class*=\"MuiBreadcrumbs-root\"], [class*=\"bui-HeaderBreadcrumb\"], [class*=\"PluginHeaderBreadcrumbs\"], #Breadcrumbs';\nexport const ACTIVE_NAV_SELECTOR = '[aria-current=\"page\"]';\n\n// ── Tabs ──\n\nexport const TAB_LIST_SELECTOR = '[role=\"tablist\"]';\nexport const TAB_SELECTOR = '[role=\"tab\"]';\nexport const BUI_NAV_SELECTOR =\n 'nav[aria-label*=\"navigation\" i], nav[class*=\"bui-HeaderNav\"]';\nexport const BUI_NAV_ITEM_SELECTOR = 'a[class*=\"NavItem\"], a[href]';\n\n// ── Dialog / overlay ──\n\nexport const MODAL_DIALOG_SELECTOR = '[role=\"dialog\"][aria-modal=\"true\"]';\nexport const DIALOG_TITLE_SELECTOR =\n '[class*=\"MuiDialogTitle\"], [class*=\"bui-DialogHeaderTitle\"]';\nexport const DIALOG_CONTENT_SELECTOR =\n '[class*=\"MuiDialogContent\"], [class*=\"bui-DialogBody\"]';\nexport const DRAWER_PAPER_SELECTOR = '[class*=\"MuiDrawer-paper\"]';\nexport const DOCKED_DRAWER_BODY_CLASS = 'docked-drawer-open';\n\n// ── Alerts ──\n\nexport const ALERT_SELECTOR =\n '[role=\"alert\"], [aria-live=\"assertive\"], [class*=\"MuiAlert-root\"], [class*=\"bui-Alert\"]';\n\n// ── Stepper ──\n\nexport const STEPPER_ROOT_SELECTOR = '[class*=\"MuiStepper-root\"]';\nexport const STEPPER_LABEL_SELECTOR =\n '[class*=\"MuiStepLabel-label\"]:not([class*=\"labelContainer\"])';\nexport const STEPPER_ACTIVE_CLASS = 'Mui-active';\nexport const STEPPER_COMPLETED_CLASS = 'Mui-completed';\n\n// ── Status ──\n\nexport const STATUS_INDICATOR_SELECTOR = '[aria-label^=\"Status\"]';\n\n// ── Forms ──\n\nexport const RJSF_FORM_SELECTOR = 'form.rjsf, form[class*=\"rjsf\"]';\nexport const FORM_CONTROL_SELECTOR =\n '[class*=\"MuiFormControl-root\"], [class*=\"MuiTextField-root\"], [class*=\"bui-TextField\"]';\nexport const FORM_HELPER_SELECTOR =\n '[class*=\"MuiFormHelperText-root\"], [slot=\"description\"]';\nexport const AUTOCOMPLETE_ROOT_SELECTOR = '[class*=\"MuiAutocomplete-root\"]';\nexport const TYPOGRAPHY_SELECTOR = '[class*=\"MuiTypography\"]';\nexport const CHIP_LABEL_SELECTOR = '[class*=\"MuiChip-label\"]';\nexport const SELECT_DISPLAY_SELECTOR =\n '[class*=\"MuiSelect-select\"]:not([class*=\"MuiTablePagination-select\"])';\nexport const SELECT_NATIVE_INPUT_SELECTOR = '[class*=\"MuiSelect-nativeInput\"]';\nexport const FORM_CONTROL_LABEL_SELECTOR =\n '[class*=\"MuiFormControlLabel-label\"]';\nexport const FORM_LABEL_SELECTOR =\n '[class*=\"MuiFormLabel-root\"], [class*=\"MuiInputLabel-root\"]';\n\n/** Nodes stripped when serializing form controls to compact HTML */\nexport const FORM_HTML_STRIP_SELECTOR = [\n 'script',\n 'style',\n 'svg',\n 'input[aria-hidden=\"true\"]',\n '[aria-hidden=\"true\"]',\n '[class*=\"MuiTouchRipple-root\"]',\n '[class*=\"MuiInputAdornment-root\"]',\n 'fieldset',\n 'button',\n].join(', ');\n\n// ── Search ──\n\nexport const SEARCH_INPUT_SELECTOR =\n 'input[placeholder*=\"earch\" i], input[aria-label*=\"earch\" i], [aria-label=\"search\" i] input';\n\n// ── Filters ──\n\nexport const FILTER_CONTAINER_SELECTOR = '[data-testid*=\"filter\"]';\nexport const FILTER_NAV_SELECTOR =\n 'nav[aria-label*=\"filter\"], [class*=\"MuiAccordionDetails-root\"] nav';\nexport const ACCORDION_ROOT_SELECTOR = '[class*=\"MuiAccordion-root\"]';\nexport const ACCORDION_DETAILS_SELECTOR = '[class*=\"MuiAccordionDetails-root\"]';\nexport const ACCORDION_SUMMARY_CONTENT_SELECTOR =\n '[class*=\"MuiAccordionSummary-content\"]';\nexport const FILTER_BOX_SELECTOR = '[class*=\"MuiBox-root\"]';\nexport const SELECTED_ITEM_SELECTOR = '[class*=\"Mui-selected\"]';\nexport const LIST_ITEM_TEXT_PRIMARY_SELECTOR =\n '[class*=\"MuiListItemText-primary\"]';\nexport const LIST_ITEM_TEXT_SECONDARY_SELECTOR =\n '[class*=\"MuiListItemText-secondary\"]';\nexport const MENU_SELECTOR = 'ul[role=\"menu\"]';\nexport const CARD_ROOT_SELECTOR = '[class*=\"MuiCard-root\"]';\nexport const LIST_ITEM_TEXT_ROOT_SELECTOR = '[class*=\"MuiListItemText-root\"]';\n\n// ── Pagination ──\n\nexport const PAGINATION_ROOT_SELECTOR =\n '[class*=\"MuiTablePagination-root\"], [class*=\"TablePagination\"], [class*=\"bui-TablePagination\"]';\nexport const PAGINATION_ACTIONS_SELECTOR =\n '[class*=\"MuiTablePagination-actions\"] button:not([disabled]), [class*=\"PaginationActions\"] button:not([disabled])';\n\n// ── Cards / Dashboard layout ──\n\nexport const CARD_LAYOUT_SELECTOR = [\n '[class*=\"MuiMasonry-root\"]',\n '[class*=\"MuiGrid-container\"]',\n '[class*=\"pf-l-gallery\"]',\n '[class*=\"bui-Grid\"]',\n].join(', ');\n\nexport const CARD_ITEM_SELECTOR = [\n '[class*=\"MuiPaper-root\"]',\n '[class*=\"MuiCard-root\"]',\n '.pf-v5-c-card, [class*=\"pf-c-card\"]',\n '[class*=\"bui-Card\"]',\n].join(', ');\n\nexport const CARD_TITLE_SELECTOR =\n 'h3, h4, h5, h6, [class*=\"CardHeader-title\"]';\n\nexport const RECHARTS_LEGEND_SELECTOR =\n '.recharts-legend-wrapper p, .recharts-legend-wrapper span[class*=\"Text\"]';\n\n// ── Empty state ──\n\nexport const EMPTY_STATE_SELECTOR = '[class*=\"BackstageEmptyState\"]';\nexport const EMPTY_STATE_BODY_SELECTOR = '[class*=\"body1\"]';\n\n// ── Shadow DOM (TechDocs) ──\n\nexport const SHADOW_HOST_SELECTOR =\n '[data-testid=\"techdocs-native-shadowroot\"], [class*=\"shadowDom\" i]';\nexport const SHADOW_NOISE_SELECTOR =\n 'style, script, link, noscript, svg, img, template';\n"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,23 @@ const getRandomSamplePrompts = (userPrompts = [], defaultPrompts = [], numberOfP
|
|
|
12
12
|
const defaultSelected = getRandomPrompts(defaultPrompts, remaining);
|
|
13
13
|
return [...userSelected, ...defaultSelected];
|
|
14
14
|
};
|
|
15
|
+
const getPriorityBasedPrompts = (appConfigPrompts, savedPrompts, hardcodedDefaults, numberOfPrompts = 3) => {
|
|
16
|
+
const result = [];
|
|
17
|
+
for (const prompt of appConfigPrompts) {
|
|
18
|
+
if (result.length >= numberOfPrompts) break;
|
|
19
|
+
result.push(prompt);
|
|
20
|
+
}
|
|
21
|
+
for (const prompt of savedPrompts) {
|
|
22
|
+
if (result.length >= numberOfPrompts) break;
|
|
23
|
+
result.push(prompt);
|
|
24
|
+
}
|
|
25
|
+
if (result.length < numberOfPrompts) {
|
|
26
|
+
const remaining = numberOfPrompts - result.length;
|
|
27
|
+
const defaults = getRandomPrompts(hardcodedDefaults, remaining);
|
|
28
|
+
result.push(...defaults);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
15
32
|
|
|
16
|
-
export { getRandomSamplePrompts };
|
|
33
|
+
export { getPriorityBasedPrompts, getRandomSamplePrompts };
|
|
17
34
|
//# sourceMappingURL=prompt-utils.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-utils.esm.js","sources":["../../src/utils/prompt-utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { SamplePrompts } from '../types';\n\nconst getRandomPrompts = (prompts: SamplePrompts, n: number) => {\n const shuffled = prompts.slice().sort(() => 0.5 - Math.random()); // NOSONAR\n return shuffled.slice(0, Math.min(n, prompts.length));\n};\n\nexport const getRandomSamplePrompts = (\n userPrompts: SamplePrompts = [],\n defaultPrompts: SamplePrompts = [],\n numberOfPrompts: number = 3,\n) => {\n const userCount = userPrompts.length;\n\n if (userCount >= numberOfPrompts) {\n return getRandomPrompts(userPrompts, numberOfPrompts);\n }\n const userSelected = getRandomPrompts(userPrompts, userCount);\n const remaining = numberOfPrompts - userSelected.length;\n const defaultSelected = getRandomPrompts(defaultPrompts, remaining);\n return [...userSelected, ...defaultSelected];\n};\n"],"names":[],"mappings":"AAiBA,MAAM,gBAAA,GAAmB,CAAC,OAAA,EAAwB,CAAA,KAAc;AAC9D,EAAA,MAAM,QAAA,GAAW,QAAQ,KAAA,EAAM,CAAE,KAAK,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,EAAQ,CAAA;AAC/D,EAAA,OAAO,QAAA,CAAS,MAAM,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAC,CAAA;AACtD,CAAA;AAEO,MAAM,sBAAA,GAAyB,CACpC,WAAA,GAA6B,IAC7B,cAAA,GAAgC,EAAC,EACjC,eAAA,GAA0B,CAAA,KACvB;AACH,EAAA,MAAM,YAAY,WAAA,CAAY,MAAA;AAE9B,EAAA,IAAI,aAAa,eAAA,EAAiB;AAChC,IAAA,OAAO,gBAAA,CAAiB,aAAa,eAAe,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,YAAA,GAAe,gBAAA,CAAiB,WAAA,EAAa,SAAS,CAAA;AAC5D,EAAA,MAAM,SAAA,GAAY,kBAAkB,YAAA,CAAa,MAAA;AACjD,EAAA,MAAM,eAAA,GAAkB,gBAAA,CAAiB,cAAA,EAAgB,SAAS,CAAA;AAClE,EAAA,OAAO,CAAC,GAAG,YAAA,EAAc,GAAG,eAAe,CAAA;AAC7C;;;;"}
|
|
1
|
+
{"version":3,"file":"prompt-utils.esm.js","sources":["../../src/utils/prompt-utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { SamplePrompts } from '../types';\n\nconst getRandomPrompts = (prompts: SamplePrompts, n: number) => {\n const shuffled = prompts.slice().sort(() => 0.5 - Math.random()); // NOSONAR\n return shuffled.slice(0, Math.min(n, prompts.length));\n};\n\nexport const getRandomSamplePrompts = (\n userPrompts: SamplePrompts = [],\n defaultPrompts: SamplePrompts = [],\n numberOfPrompts: number = 3,\n) => {\n const userCount = userPrompts.length;\n\n if (userCount >= numberOfPrompts) {\n return getRandomPrompts(userPrompts, numberOfPrompts);\n }\n const userSelected = getRandomPrompts(userPrompts, userCount);\n const remaining = numberOfPrompts - userSelected.length;\n const defaultSelected = getRandomPrompts(defaultPrompts, remaining);\n return [...userSelected, ...defaultSelected];\n};\n\n/**\n * Priority-based prompt selection for the welcome screen.\n * Fills slots sequentially: app-config > saved prompts (by order) > hardcoded defaults.\n * No random sampling when saved prompts are available.\n */\nexport const getPriorityBasedPrompts = (\n appConfigPrompts: SamplePrompts,\n savedPrompts: SamplePrompts,\n hardcodedDefaults: SamplePrompts,\n numberOfPrompts: number = 3,\n): SamplePrompts => {\n const result: SamplePrompts = [];\n\n for (const prompt of appConfigPrompts) {\n if (result.length >= numberOfPrompts) break;\n result.push(prompt);\n }\n\n for (const prompt of savedPrompts) {\n if (result.length >= numberOfPrompts) break;\n result.push(prompt);\n }\n\n if (result.length < numberOfPrompts) {\n const remaining = numberOfPrompts - result.length;\n const defaults = getRandomPrompts(hardcodedDefaults, remaining);\n result.push(...defaults);\n }\n\n return result;\n};\n"],"names":[],"mappings":"AAiBA,MAAM,gBAAA,GAAmB,CAAC,OAAA,EAAwB,CAAA,KAAc;AAC9D,EAAA,MAAM,QAAA,GAAW,QAAQ,KAAA,EAAM,CAAE,KAAK,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,EAAQ,CAAA;AAC/D,EAAA,OAAO,QAAA,CAAS,MAAM,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAC,CAAA;AACtD,CAAA;AAEO,MAAM,sBAAA,GAAyB,CACpC,WAAA,GAA6B,IAC7B,cAAA,GAAgC,EAAC,EACjC,eAAA,GAA0B,CAAA,KACvB;AACH,EAAA,MAAM,YAAY,WAAA,CAAY,MAAA;AAE9B,EAAA,IAAI,aAAa,eAAA,EAAiB;AAChC,IAAA,OAAO,gBAAA,CAAiB,aAAa,eAAe,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,YAAA,GAAe,gBAAA,CAAiB,WAAA,EAAa,SAAS,CAAA;AAC5D,EAAA,MAAM,SAAA,GAAY,kBAAkB,YAAA,CAAa,MAAA;AACjD,EAAA,MAAM,eAAA,GAAkB,gBAAA,CAAiB,cAAA,EAAgB,SAAS,CAAA;AAClE,EAAA,OAAO,CAAC,GAAG,YAAA,EAAc,GAAG,eAAe,CAAA;AAC7C;AAOO,MAAM,0BAA0B,CACrC,gBAAA,EACA,YAAA,EACA,iBAAA,EACA,kBAA0B,CAAA,KACR;AAClB,EAAA,MAAM,SAAwB,EAAC;AAE/B,EAAA,KAAA,MAAW,UAAU,gBAAA,EAAkB;AACrC,IAAA,IAAI,MAAA,CAAO,UAAU,eAAA,EAAiB;AACtC,IAAA,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA,EACpB;AAEA,EAAA,KAAA,MAAW,UAAU,YAAA,EAAc;AACjC,IAAA,IAAI,MAAA,CAAO,UAAU,eAAA,EAAiB;AACtC,IAAA,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA,EACpB;AAEA,EAAA,IAAI,MAAA,CAAO,SAAS,eAAA,EAAiB;AACnC,IAAA,MAAM,SAAA,GAAY,kBAAkB,MAAA,CAAO,MAAA;AAC3C,IAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,iBAAA,EAAmB,SAAS,CAAA;AAC9D,IAAA,MAAA,CAAO,IAAA,CAAK,GAAG,QAAQ,CAAA;AAAA,EACzB;AAEA,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { sanitizeClonedDom } from './sensitive-data-redactor.esm.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_QUALITY = 0.7;
|
|
4
|
+
const DEFAULT_MAX_WIDTH = 1280;
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 1e3;
|
|
6
|
+
function stripDataUriPrefix(dataUri) {
|
|
7
|
+
const commaIdx = dataUri.indexOf(",");
|
|
8
|
+
return commaIdx !== -1 ? dataUri.slice(commaIdx + 1) : dataUri;
|
|
9
|
+
}
|
|
10
|
+
function waitForIdle() {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
if ("requestIdleCallback" in window) {
|
|
13
|
+
window.requestIdleCallback(() => resolve(), { timeout: 100 });
|
|
14
|
+
} else {
|
|
15
|
+
setTimeout(resolve, 0);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function scaleCanvas(canvas, maxWidth) {
|
|
20
|
+
if (canvas.width <= maxWidth) {
|
|
21
|
+
return canvas;
|
|
22
|
+
}
|
|
23
|
+
const scaleFactor = maxWidth / canvas.width;
|
|
24
|
+
const scaledWidth = maxWidth;
|
|
25
|
+
const scaledHeight = Math.round(canvas.height * scaleFactor);
|
|
26
|
+
const scaledCanvas = document.createElement("canvas");
|
|
27
|
+
scaledCanvas.width = scaledWidth;
|
|
28
|
+
scaledCanvas.height = scaledHeight;
|
|
29
|
+
const ctx = scaledCanvas.getContext("2d");
|
|
30
|
+
if (!ctx) {
|
|
31
|
+
return canvas;
|
|
32
|
+
}
|
|
33
|
+
ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);
|
|
34
|
+
return scaledCanvas;
|
|
35
|
+
}
|
|
36
|
+
async function doCapture(options) {
|
|
37
|
+
const targetElement = document.querySelector("#root");
|
|
38
|
+
if (!targetElement) {
|
|
39
|
+
return { success: false, error: "Root element (#root) not found" };
|
|
40
|
+
}
|
|
41
|
+
const { default: html2canvas } = await import('html2canvas-pro');
|
|
42
|
+
const start = window.performance.now();
|
|
43
|
+
const canvas = await html2canvas(targetElement, {
|
|
44
|
+
useCORS: true,
|
|
45
|
+
allowTaint: false,
|
|
46
|
+
logging: false,
|
|
47
|
+
onclone: (clonedDocument) => {
|
|
48
|
+
sanitizeClonedDom(clonedDocument);
|
|
49
|
+
},
|
|
50
|
+
ignoreElements: (element) => {
|
|
51
|
+
if (element.hasAttribute("data-screen-capture-exclude")) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (element.classList.contains("pf-chatbot")) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
let scaledCanvas;
|
|
61
|
+
try {
|
|
62
|
+
scaledCanvas = scaleCanvas(canvas, options.maxWidth);
|
|
63
|
+
const dataUri = scaledCanvas.toDataURL("image/jpeg", options.quality);
|
|
64
|
+
const captureTimeMs = Math.round(window.performance.now() - start);
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
base64: stripDataUriPrefix(dataUri),
|
|
68
|
+
contentType: "image/jpeg",
|
|
69
|
+
width: scaledCanvas.width,
|
|
70
|
+
height: scaledCanvas.height,
|
|
71
|
+
captureTimeMs
|
|
72
|
+
};
|
|
73
|
+
} finally {
|
|
74
|
+
canvas.width = 0;
|
|
75
|
+
canvas.height = 0;
|
|
76
|
+
if (scaledCanvas && scaledCanvas !== canvas) {
|
|
77
|
+
scaledCanvas.width = 0;
|
|
78
|
+
scaledCanvas.height = 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function captureScreenshot(options) {
|
|
83
|
+
if (document.visibilityState === "hidden") {
|
|
84
|
+
return { success: false, error: "Tab not visible" };
|
|
85
|
+
}
|
|
86
|
+
await waitForIdle();
|
|
87
|
+
const resolvedOptions = {
|
|
88
|
+
quality: options?.quality ?? DEFAULT_QUALITY,
|
|
89
|
+
excludeSelector: options?.excludeSelector,
|
|
90
|
+
maxWidth: options?.maxWidth ?? DEFAULT_MAX_WIDTH,
|
|
91
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
92
|
+
};
|
|
93
|
+
let timeoutId;
|
|
94
|
+
try {
|
|
95
|
+
const result = await Promise.race([
|
|
96
|
+
doCapture(resolvedOptions),
|
|
97
|
+
new Promise((resolve) => {
|
|
98
|
+
timeoutId = setTimeout(
|
|
99
|
+
() => resolve({ success: false, error: "Capture timeout exceeded" }),
|
|
100
|
+
resolvedOptions.timeoutMs
|
|
101
|
+
);
|
|
102
|
+
})
|
|
103
|
+
]);
|
|
104
|
+
return result;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: err instanceof Error ? err.message : "Unknown capture error"
|
|
109
|
+
};
|
|
110
|
+
} finally {
|
|
111
|
+
clearTimeout(timeoutId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { captureScreenshot };
|
|
116
|
+
//# sourceMappingURL=screen-capture.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-capture.esm.js","sources":["../../src/utils/screen-capture.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { sanitizeClonedDom } from './sensitive-data-redactor';\n\nconst DEFAULT_QUALITY = 0.7;\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_TIMEOUT_MS = 1000;\nconst DEFAULT_EXCLUDE_SELECTOR = '[data-screen-capture-exclude]';\n\nexport interface CaptureOptions {\n /** Image quality 0-1 (default: 0.7) */\n quality?: number;\n /** CSS selector for elements to exclude (default: '[data-screen-capture-exclude]') */\n excludeSelector?: string;\n /** Max output width in pixels for compression (default: 1280) */\n maxWidth?: number;\n /** Maximum time allowed for capture in ms (default: 1000) */\n timeoutMs?: number;\n}\n\nexport interface CaptureResult {\n success: true;\n /** Base64-encoded image without the data URI prefix */\n base64: string;\n /** MIME type: 'image/jpeg' */\n contentType: string;\n width: number;\n height: number;\n /** Time taken for the capture in milliseconds */\n captureTimeMs: number;\n}\n\nexport interface CaptureError {\n success: false;\n error: string;\n}\n\nexport type CaptureResponse = CaptureResult | CaptureError;\n\nfunction stripDataUriPrefix(dataUri: string): string {\n const commaIdx = dataUri.indexOf(',');\n return commaIdx !== -1 ? dataUri.slice(commaIdx + 1) : dataUri;\n}\n\nfunction waitForIdle(): Promise<void> {\n return new Promise(resolve => {\n if ('requestIdleCallback' in window) {\n (window as Window).requestIdleCallback(() => resolve(), { timeout: 100 });\n } else {\n setTimeout(resolve, 0);\n }\n });\n}\n\nfunction scaleCanvas(\n canvas: HTMLCanvasElement,\n maxWidth: number,\n): HTMLCanvasElement {\n if (canvas.width <= maxWidth) {\n return canvas;\n }\n\n const scaleFactor = maxWidth / canvas.width;\n const scaledWidth = maxWidth;\n const scaledHeight = Math.round(canvas.height * scaleFactor);\n\n const scaledCanvas = document.createElement('canvas');\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n\n const ctx = scaledCanvas.getContext('2d');\n if (!ctx) {\n return canvas;\n }\n ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n\n return scaledCanvas;\n}\n\nasync function doCapture(\n options: Required<CaptureOptions>,\n): Promise<CaptureResponse> {\n const targetElement = document.querySelector('#root');\n if (!targetElement) {\n return { success: false, error: 'Root element (#root) not found' };\n }\n\n const { default: html2canvas } = await import('html2canvas-pro');\n\n const start = window.performance.now();\n\n const canvas = await html2canvas(targetElement as HTMLElement, {\n useCORS: true,\n allowTaint: false,\n logging: false,\n onclone: (clonedDocument: Document) => {\n sanitizeClonedDom(clonedDocument);\n },\n ignoreElements: (element: Element) => {\n if (element.hasAttribute('data-screen-capture-exclude')) {\n return true;\n }\n if (element.classList.contains('pf-chatbot')) {\n return true;\n }\n if (options.excludeSelector !== DEFAULT_EXCLUDE_SELECTOR) {\n try {\n if (element.matches(options.excludeSelector)) {\n return true;\n }\n } catch {\n // Invalid selector — skip rather than crash capture\n }\n }\n return false;\n },\n });\n\n let scaledCanvas: HTMLCanvasElement | undefined;\n try {\n scaledCanvas = scaleCanvas(canvas, options.maxWidth);\n const dataUri = scaledCanvas.toDataURL('image/jpeg', options.quality);\n\n const captureTimeMs = Math.round(window.performance.now() - start);\n\n return {\n success: true,\n base64: stripDataUriPrefix(dataUri),\n contentType: 'image/jpeg',\n width: scaledCanvas.width,\n height: scaledCanvas.height,\n captureTimeMs,\n };\n } finally {\n canvas.width = 0;\n canvas.height = 0;\n if (scaledCanvas && scaledCanvas !== canvas) {\n scaledCanvas.width = 0;\n scaledCanvas.height = 0;\n }\n }\n}\n\n/**\n * Captures a screenshot of the current RHDH viewport, excluding the\n * Lightspeed chat panel and redacting sensitive data.\n *\n * Outputs JPEG format.\n * Includes performance guardrails: visibility check, idle deferral, and timeout.\n */\nexport async function captureScreenshot(\n options?: CaptureOptions,\n): Promise<CaptureResponse> {\n if (document.visibilityState === 'hidden') {\n return { success: false, error: 'Tab not visible' };\n }\n\n await waitForIdle();\n\n const resolvedOptions: Required<CaptureOptions> = {\n quality: options?.quality ?? DEFAULT_QUALITY,\n excludeSelector: options?.excludeSelector ?? DEFAULT_EXCLUDE_SELECTOR,\n maxWidth: options?.maxWidth ?? DEFAULT_MAX_WIDTH,\n timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n\n // When the timeout wins the race, doCapture continues running in the background\n // since html2canvas-pro does not support AbortController-based cancellation.\n // The timeout prevents the caller from waiting indefinitely; background work\n // completes harmlessly and its result is discarded.\n let timeoutId: ReturnType<typeof setTimeout>;\n try {\n const result = await Promise.race<CaptureResponse>([\n doCapture(resolvedOptions),\n new Promise<CaptureError>(resolve => {\n timeoutId = setTimeout(\n () => resolve({ success: false, error: 'Capture timeout exceeded' }),\n resolvedOptions.timeoutMs,\n );\n }),\n ]);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Unknown capture error',\n };\n } finally {\n clearTimeout(timeoutId!);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { extractPageContext } from './dom-extractor.esm.js';
|
|
2
|
+
import { SEARCH_INPUT_SELECTOR, MODAL_DIALOG_SELECTOR, DIALOG_TITLE_SELECTOR, PAGE_CHROME_ROOT_SELECTORS, PAGE_TITLE_SCOPED_SELECTORS, PLUGIN_HEADER_TOOLBAR_NAME_SELECTOR, PLUGIN_HEADER_BREADCRUMBS_SELECTOR, LEGACY_PAGE_TITLE_SELECTORS, TEMPLATE_INFO_CARD_TITLE_SELECTORS } from './dom-selectors.esm.js';
|
|
3
|
+
import { captureScreenshot } from './screen-capture.esm.js';
|
|
4
|
+
|
|
5
|
+
const SCREEN_CONTEXT_CHIP_MAX_LABEL_LENGTH = 40;
|
|
6
|
+
const SCREEN_CONTEXT_EXCLUDE_SELECTOR = "[data-screen-capture-exclude], .pf-chatbot";
|
|
7
|
+
const SEARCH_QUERY_PARAM_KEYS = ["query", "term", "q"];
|
|
8
|
+
function truncateChipLabel(label, maxLength = SCREEN_CONTEXT_CHIP_MAX_LABEL_LENGTH) {
|
|
9
|
+
const trimmed = label.trim();
|
|
10
|
+
if (trimmed.length <= maxLength) {
|
|
11
|
+
return trimmed;
|
|
12
|
+
}
|
|
13
|
+
return `${trimmed.slice(0, maxLength - 1).trimEnd()}\u2026`;
|
|
14
|
+
}
|
|
15
|
+
function normalizeDocumentTitle(title) {
|
|
16
|
+
const trimmed = title.trim();
|
|
17
|
+
const pipeIndex = trimmed.indexOf(" | ");
|
|
18
|
+
if (pipeIndex > 0) {
|
|
19
|
+
return trimmed.slice(0, pipeIndex).trim();
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|
|
23
|
+
function isElementVisible(el) {
|
|
24
|
+
if (!(el instanceof HTMLElement)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (el.closest(SCREEN_CONTEXT_EXCLUDE_SELECTOR)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const style = window.getComputedStyle(el);
|
|
31
|
+
if (el.hasAttribute("hidden")) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (style.display === "none" || style.visibility === "hidden" || Number.parseFloat(style.opacity) === 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
function queryFirstVisibleTitle(root, selectors) {
|
|
40
|
+
for (const selector of selectors) {
|
|
41
|
+
const elements = root.querySelectorAll(selector);
|
|
42
|
+
for (const el of elements) {
|
|
43
|
+
if (!isElementVisible(el)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const text = el.textContent?.trim();
|
|
47
|
+
if (text) {
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
function getChromeRoots(doc) {
|
|
55
|
+
const roots = [];
|
|
56
|
+
for (const selector of PAGE_CHROME_ROOT_SELECTORS) {
|
|
57
|
+
const node = doc.querySelector(selector);
|
|
58
|
+
if (node && !roots.includes(node)) {
|
|
59
|
+
roots.push(node);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return roots.length > 0 ? roots : [doc];
|
|
63
|
+
}
|
|
64
|
+
function getPluginHeaderToolbarChipLabel(root = document) {
|
|
65
|
+
const toolbarName = root.querySelector(PLUGIN_HEADER_TOOLBAR_NAME_SELECTOR);
|
|
66
|
+
if (!(toolbarName instanceof Element)) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
if (toolbarName.closest(SCREEN_CONTEXT_EXCLUDE_SELECTOR)) {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
const breadcrumbs = toolbarName.querySelector(
|
|
73
|
+
PLUGIN_HEADER_BREADCRUMBS_SELECTOR
|
|
74
|
+
);
|
|
75
|
+
if (!breadcrumbs) {
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
const breadcrumbItems = breadcrumbs.querySelectorAll(
|
|
79
|
+
'li.react-aria-Breadcrumb, li[class*="Breadcrumb"]'
|
|
80
|
+
);
|
|
81
|
+
const hasNestedCrumbs = breadcrumbItems.length >= 2;
|
|
82
|
+
const h1Text = toolbarName.querySelector("h1")?.textContent?.trim();
|
|
83
|
+
if (h1Text) {
|
|
84
|
+
return h1Text;
|
|
85
|
+
}
|
|
86
|
+
if (hasNestedCrumbs) {
|
|
87
|
+
const firstLink = breadcrumbs.querySelector("a[href]");
|
|
88
|
+
const linkText = firstLink?.textContent?.trim();
|
|
89
|
+
if (linkText) {
|
|
90
|
+
return linkText;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const currentCrumb = breadcrumbs.querySelector(
|
|
94
|
+
'[data-current="true"], [aria-current="page"]'
|
|
95
|
+
);
|
|
96
|
+
const currentText = currentCrumb?.textContent?.trim();
|
|
97
|
+
if (currentText) {
|
|
98
|
+
return currentText;
|
|
99
|
+
}
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
function getPageTitleFromDom(doc = document) {
|
|
103
|
+
const dialog = doc.querySelector(MODAL_DIALOG_SELECTOR);
|
|
104
|
+
if (dialog) {
|
|
105
|
+
const dialogTitle = dialog.querySelector(DIALOG_TITLE_SELECTOR);
|
|
106
|
+
if (dialogTitle && isElementVisible(dialogTitle)) {
|
|
107
|
+
const text = dialogTitle.textContent?.trim();
|
|
108
|
+
if (text) {
|
|
109
|
+
return text;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const roots = getChromeRoots(doc);
|
|
114
|
+
for (const root of roots) {
|
|
115
|
+
const buiTitle = queryFirstVisibleTitle(root, PAGE_TITLE_SCOPED_SELECTORS);
|
|
116
|
+
if (buiTitle) {
|
|
117
|
+
return buiTitle;
|
|
118
|
+
}
|
|
119
|
+
const pluginToolbarLabel = getPluginHeaderToolbarChipLabel(root);
|
|
120
|
+
if (pluginToolbarLabel) {
|
|
121
|
+
return pluginToolbarLabel;
|
|
122
|
+
}
|
|
123
|
+
const legacyTitle = queryFirstVisibleTitle(
|
|
124
|
+
root,
|
|
125
|
+
LEGACY_PAGE_TITLE_SELECTORS
|
|
126
|
+
);
|
|
127
|
+
if (legacyTitle) {
|
|
128
|
+
return legacyTitle;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const normalized = normalizeDocumentTitle(doc.title);
|
|
132
|
+
return normalized || "Current page";
|
|
133
|
+
}
|
|
134
|
+
function getScreenContextRouteKind(pathname, search) {
|
|
135
|
+
if (pathname.includes("/create/") || pathname.includes("/template/") || /\/template\/[^/]+/.test(pathname) || pathname.includes("/edit/") && pathname.includes("template")) {
|
|
136
|
+
return "template";
|
|
137
|
+
}
|
|
138
|
+
if (pathname.includes("/search") || SEARCH_QUERY_PARAM_KEYS.some((key) => new URLSearchParams(search).has(key)) || search.includes("filters[") || pathname.includes("/catalog") && /[?&](query|term|filters\[)/.test(search)) {
|
|
139
|
+
return "search";
|
|
140
|
+
}
|
|
141
|
+
return "default";
|
|
142
|
+
}
|
|
143
|
+
function getSearchQueryFromLocation(search, doc = document) {
|
|
144
|
+
const params = new URLSearchParams(
|
|
145
|
+
search.startsWith("?") ? search : `?${search}`
|
|
146
|
+
);
|
|
147
|
+
for (const key of SEARCH_QUERY_PARAM_KEYS) {
|
|
148
|
+
const fromUrl = params.get(key)?.trim();
|
|
149
|
+
if (fromUrl) {
|
|
150
|
+
return fromUrl;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const inputs = doc.querySelectorAll(SEARCH_INPUT_SELECTOR);
|
|
154
|
+
for (const input of inputs) {
|
|
155
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (!isElementVisible(input)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const fromInput = input.value?.trim();
|
|
162
|
+
if (fromInput) {
|
|
163
|
+
return fromInput;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
const SOFTWARE_TEMPLATES_LIST_CHIP_LABEL = "Software templates";
|
|
169
|
+
const SOFTWARE_TEMPLATES_GALLERY_PATH = "/create/templates";
|
|
170
|
+
function normalizePathname(pathname) {
|
|
171
|
+
const trimmed = pathname.trim();
|
|
172
|
+
if (!trimmed || trimmed === "/") {
|
|
173
|
+
return "/";
|
|
174
|
+
}
|
|
175
|
+
return trimmed.replace(/\/+$/, "") || "/";
|
|
176
|
+
}
|
|
177
|
+
function isSoftwareTemplatesListPath(pathname) {
|
|
178
|
+
return normalizePathname(pathname) === SOFTWARE_TEMPLATES_GALLERY_PATH;
|
|
179
|
+
}
|
|
180
|
+
function parseSoftwareTemplateDetailSlug(pathname) {
|
|
181
|
+
const normalized = normalizePathname(pathname);
|
|
182
|
+
const prefix = `${SOFTWARE_TEMPLATES_GALLERY_PATH}/`;
|
|
183
|
+
if (!normalized.startsWith(prefix)) {
|
|
184
|
+
return void 0;
|
|
185
|
+
}
|
|
186
|
+
const segments = normalized.slice(prefix.length).split("/").filter(Boolean);
|
|
187
|
+
if (segments.length < 2) {
|
|
188
|
+
return void 0;
|
|
189
|
+
}
|
|
190
|
+
return segments[segments.length - 1];
|
|
191
|
+
}
|
|
192
|
+
function getSoftwareTemplateTitleFromDom(doc = document) {
|
|
193
|
+
const roots = getChromeRoots(doc);
|
|
194
|
+
for (const root of roots) {
|
|
195
|
+
const title = queryFirstVisibleTitle(
|
|
196
|
+
root,
|
|
197
|
+
TEMPLATE_INFO_CARD_TITLE_SELECTORS
|
|
198
|
+
);
|
|
199
|
+
if (title) {
|
|
200
|
+
return title;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
function resolveSoftwareTemplateChipLabel(pathname, doc = document) {
|
|
206
|
+
if (isSoftwareTemplatesListPath(pathname)) {
|
|
207
|
+
return SOFTWARE_TEMPLATES_LIST_CHIP_LABEL;
|
|
208
|
+
}
|
|
209
|
+
const slug = parseSoftwareTemplateDetailSlug(pathname);
|
|
210
|
+
if (!slug) {
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
return getSoftwareTemplateTitleFromDom(doc) ?? slug;
|
|
214
|
+
}
|
|
215
|
+
function resolveScreenContextChipLabel(options) {
|
|
216
|
+
const doc = options.document ?? document;
|
|
217
|
+
const routeKind = getScreenContextRouteKind(options.pathname, options.search);
|
|
218
|
+
if (routeKind === "search") {
|
|
219
|
+
const searchQuery = getSearchQueryFromLocation(options.search, doc);
|
|
220
|
+
if (searchQuery) {
|
|
221
|
+
return {
|
|
222
|
+
label: truncateChipLabel(searchQuery),
|
|
223
|
+
routeKind,
|
|
224
|
+
searchQuery
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (routeKind === "template") {
|
|
229
|
+
const softwareTemplateLabel = resolveSoftwareTemplateChipLabel(
|
|
230
|
+
options.pathname,
|
|
231
|
+
doc
|
|
232
|
+
);
|
|
233
|
+
if (softwareTemplateLabel) {
|
|
234
|
+
return {
|
|
235
|
+
label: truncateChipLabel(softwareTemplateLabel),
|
|
236
|
+
routeKind
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const pageTitle = getPageTitleFromDom(doc);
|
|
241
|
+
return {
|
|
242
|
+
label: truncateChipLabel(pageTitle),
|
|
243
|
+
routeKind,
|
|
244
|
+
searchQuery: routeKind === "search" ? getSearchQueryFromLocation(options.search, doc) : void 0
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function getScreenContextTooltipLine1Key(routeKind, pathname) {
|
|
248
|
+
switch (routeKind) {
|
|
249
|
+
case "template":
|
|
250
|
+
if (pathname && isSoftwareTemplatesListPath(pathname)) {
|
|
251
|
+
return "contextChip.tooltip.askAbout";
|
|
252
|
+
}
|
|
253
|
+
return "contextChip.tooltip.template";
|
|
254
|
+
case "search":
|
|
255
|
+
return "contextChip.tooltip.search";
|
|
256
|
+
default:
|
|
257
|
+
return "contextChip.tooltip.askAbout";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function getScreenContextTooltipLine1Text(params, t) {
|
|
261
|
+
const label = params.chipLabel;
|
|
262
|
+
const key = getScreenContextTooltipLine1Key(
|
|
263
|
+
params.routeKind,
|
|
264
|
+
params.pathname
|
|
265
|
+
);
|
|
266
|
+
return t(key, { label });
|
|
267
|
+
}
|
|
268
|
+
function getScreenContextTooltipLine2Key(input) {
|
|
269
|
+
const { domEnabled, screenshotsEnabled, supportsVision } = input;
|
|
270
|
+
const willSendDom = domEnabled;
|
|
271
|
+
const willSendScreenshot = screenshotsEnabled && supportsVision;
|
|
272
|
+
if (willSendDom && willSendScreenshot) {
|
|
273
|
+
return "fullContext";
|
|
274
|
+
}
|
|
275
|
+
if (!domEnabled && !screenshotsEnabled) {
|
|
276
|
+
return "adminLimited";
|
|
277
|
+
}
|
|
278
|
+
if (!domEnabled && screenshotsEnabled && supportsVision) {
|
|
279
|
+
return "screenshotOnly";
|
|
280
|
+
}
|
|
281
|
+
if (willSendDom) {
|
|
282
|
+
const adminOff = !screenshotsEnabled;
|
|
283
|
+
const noVision = !supportsVision;
|
|
284
|
+
if (adminOff && noVision) {
|
|
285
|
+
return "textOnlyCombined";
|
|
286
|
+
}
|
|
287
|
+
if (noVision) {
|
|
288
|
+
return "textOnlyNoVision";
|
|
289
|
+
}
|
|
290
|
+
if (adminOff) {
|
|
291
|
+
return "textOnlyAdminScreenshotsOff";
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!domEnabled && screenshotsEnabled && !supportsVision) {
|
|
295
|
+
return "domOffNoVision";
|
|
296
|
+
}
|
|
297
|
+
return "adminLimited";
|
|
298
|
+
}
|
|
299
|
+
function shouldAttachScreenContext(input) {
|
|
300
|
+
const baseGate = input.adminEnabled && input.sharingEnabled && !input.paused && !input.isFullscreen;
|
|
301
|
+
if (!baseGate) {
|
|
302
|
+
return { attachDom: false, attachScreenshot: false };
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
attachDom: input.domEnabled,
|
|
306
|
+
attachScreenshot: input.screenshotsEnabled && input.supportsVision
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async function buildScreenContextAttachments(input) {
|
|
310
|
+
const { attachDom, attachScreenshot } = shouldAttachScreenContext(input);
|
|
311
|
+
const attachments = [];
|
|
312
|
+
if (attachDom) {
|
|
313
|
+
try {
|
|
314
|
+
const domContext = extractPageContext({
|
|
315
|
+
maxChars: input.domExtractionMaxChars
|
|
316
|
+
});
|
|
317
|
+
if (domContext) {
|
|
318
|
+
attachments.push({
|
|
319
|
+
attachment_type: "configuration",
|
|
320
|
+
content_type: "text/plain",
|
|
321
|
+
content: domContext
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (attachScreenshot) {
|
|
328
|
+
const result = await captureScreenshot({
|
|
329
|
+
excludeSelector: "[data-screen-capture-exclude]"
|
|
330
|
+
});
|
|
331
|
+
if (result.success) {
|
|
332
|
+
attachments.push({
|
|
333
|
+
attachment_type: "image",
|
|
334
|
+
content_type: result.contentType,
|
|
335
|
+
content: result.base64
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return attachments;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export { SCREEN_CONTEXT_CHIP_MAX_LABEL_LENGTH, SOFTWARE_TEMPLATES_LIST_CHIP_LABEL, buildScreenContextAttachments, getPageTitleFromDom, getPluginHeaderToolbarChipLabel, getScreenContextRouteKind, getScreenContextTooltipLine1Key, getScreenContextTooltipLine1Text, getScreenContextTooltipLine2Key, getSearchQueryFromLocation, getSoftwareTemplateTitleFromDom, isElementVisible, isSoftwareTemplatesListPath, normalizeDocumentTitle, parseSoftwareTemplateDetailSlug, resolveScreenContextChipLabel, resolveSoftwareTemplateChipLabel, shouldAttachScreenContext, truncateChipLabel };
|
|
343
|
+
//# sourceMappingURL=screen-context-utils.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-context-utils.esm.js","sources":["../../src/utils/screen-context-utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Attachment } from '../types';\nimport { extractPageContext } from './dom-extractor';\nimport {\n DIALOG_TITLE_SELECTOR,\n LEGACY_PAGE_TITLE_SELECTORS,\n MODAL_DIALOG_SELECTOR,\n PAGE_CHROME_ROOT_SELECTORS,\n PAGE_TITLE_SCOPED_SELECTORS,\n PLUGIN_HEADER_BREADCRUMBS_SELECTOR,\n PLUGIN_HEADER_TOOLBAR_NAME_SELECTOR,\n SEARCH_INPUT_SELECTOR,\n TEMPLATE_INFO_CARD_TITLE_SELECTORS,\n} from './dom-selectors';\nimport { captureScreenshot } from './screen-capture';\n\nexport const SCREEN_CONTEXT_CHIP_MAX_LABEL_LENGTH = 40;\n\nconst SCREEN_CONTEXT_EXCLUDE_SELECTOR =\n '[data-screen-capture-exclude], .pf-chatbot';\n\nconst SEARCH_QUERY_PARAM_KEYS = ['query', 'term', 'q'] as const;\n\nexport type ScreenContextRouteKind = 'default' | 'template' | 'search';\n\nexport type ScreenContextTooltipLine2Key =\n | 'fullContext'\n | 'adminLimited'\n | 'screenshotOnly'\n | 'domOffNoVision'\n | 'textOnlyNoVision'\n | 'textOnlyAdminScreenshotsOff'\n | 'textOnlyCombined';\n\nexport type ShouldAttachScreenContextInput = {\n adminEnabled: boolean;\n sharingEnabled: boolean;\n paused: boolean;\n isFullscreen: boolean;\n domEnabled: boolean;\n screenshotsEnabled: boolean;\n supportsVision: boolean;\n};\n\nexport type ShouldAttachScreenContextResult = {\n attachDom: boolean;\n attachScreenshot: boolean;\n};\n\nexport type ResolvedScreenContextLabel = {\n label: string;\n routeKind: ScreenContextRouteKind;\n searchQuery?: string;\n};\n\nexport function truncateChipLabel(\n label: string,\n maxLength = SCREEN_CONTEXT_CHIP_MAX_LABEL_LENGTH,\n): string {\n const trimmed = label.trim();\n if (trimmed.length <= maxLength) {\n return trimmed;\n }\n return `${trimmed.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\nexport function normalizeDocumentTitle(title: string): string {\n const trimmed = title.trim();\n const pipeIndex = trimmed.indexOf(' | ');\n if (pipeIndex > 0) {\n return trimmed.slice(0, pipeIndex).trim();\n }\n return trimmed;\n}\n\nexport function isElementVisible(el: Element): boolean {\n if (!(el instanceof HTMLElement)) {\n return false;\n }\n if (el.closest(SCREEN_CONTEXT_EXCLUDE_SELECTOR)) {\n return false;\n }\n const style = window.getComputedStyle(el);\n if (el.hasAttribute('hidden')) {\n return false;\n }\n if (\n style.display === 'none' ||\n style.visibility === 'hidden' ||\n Number.parseFloat(style.opacity) === 0\n ) {\n return false;\n }\n return true;\n}\n\nfunction queryFirstVisibleTitle(\n root: ParentNode,\n selectors: readonly string[],\n): string | undefined {\n for (const selector of selectors) {\n const elements = root.querySelectorAll(selector);\n for (const el of elements) {\n if (!isElementVisible(el)) {\n continue;\n }\n const text = el.textContent?.trim();\n if (text) {\n return text;\n }\n }\n }\n return undefined;\n}\n\nfunction getChromeRoots(doc: Document): ParentNode[] {\n const roots: ParentNode[] = [];\n for (const selector of PAGE_CHROME_ROOT_SELECTORS) {\n const node = doc.querySelector(selector);\n if (node && !roots.includes(node)) {\n roots.push(node);\n }\n }\n return roots.length > 0 ? roots : [doc];\n}\n\n/**\n * BUI plugin pages (e.g. Settings → General) use toolbar breadcrumbs instead of\n * HeaderTitle. Prefer the plugin name (visually hidden h1 or first crumb link).\n */\nexport function getPluginHeaderToolbarChipLabel(\n root: ParentNode = document,\n): string | undefined {\n const toolbarName = root.querySelector(PLUGIN_HEADER_TOOLBAR_NAME_SELECTOR);\n if (!(toolbarName instanceof Element)) {\n return undefined;\n }\n if (toolbarName.closest(SCREEN_CONTEXT_EXCLUDE_SELECTOR)) {\n return undefined;\n }\n\n const breadcrumbs = toolbarName.querySelector(\n PLUGIN_HEADER_BREADCRUMBS_SELECTOR,\n );\n if (!breadcrumbs) {\n return undefined;\n }\n\n const breadcrumbItems = breadcrumbs.querySelectorAll(\n 'li.react-aria-Breadcrumb, li[class*=\"Breadcrumb\"]',\n );\n const hasNestedCrumbs = breadcrumbItems.length >= 2;\n\n const h1Text = toolbarName.querySelector('h1')?.textContent?.trim();\n if (h1Text) {\n return h1Text;\n }\n\n if (hasNestedCrumbs) {\n const firstLink = breadcrumbs.querySelector('a[href]');\n const linkText = firstLink?.textContent?.trim();\n if (linkText) {\n return linkText;\n }\n }\n\n const currentCrumb = breadcrumbs.querySelector(\n '[data-current=\"true\"], [aria-current=\"page\"]',\n );\n const currentText = currentCrumb?.textContent?.trim();\n if (currentText) {\n return currentText;\n }\n\n return undefined;\n}\n\nexport function getPageTitleFromDom(doc: Document = document): string {\n const dialog = doc.querySelector(MODAL_DIALOG_SELECTOR);\n if (dialog) {\n const dialogTitle = dialog.querySelector(DIALOG_TITLE_SELECTOR);\n if (dialogTitle && isElementVisible(dialogTitle)) {\n const text = dialogTitle.textContent?.trim();\n if (text) {\n return text;\n }\n }\n }\n\n const roots = getChromeRoots(doc);\n for (const root of roots) {\n const buiTitle = queryFirstVisibleTitle(root, PAGE_TITLE_SCOPED_SELECTORS);\n if (buiTitle) {\n return buiTitle;\n }\n const pluginToolbarLabel = getPluginHeaderToolbarChipLabel(root);\n if (pluginToolbarLabel) {\n return pluginToolbarLabel;\n }\n const legacyTitle = queryFirstVisibleTitle(\n root,\n LEGACY_PAGE_TITLE_SELECTORS,\n );\n if (legacyTitle) {\n return legacyTitle;\n }\n }\n\n const normalized = normalizeDocumentTitle(doc.title);\n return normalized || 'Current page';\n}\n\nexport function getScreenContextRouteKind(\n pathname: string,\n search: string,\n): ScreenContextRouteKind {\n if (\n pathname.includes('/create/') ||\n pathname.includes('/template/') ||\n /\\/template\\/[^/]+/.test(pathname) ||\n (pathname.includes('/edit/') && pathname.includes('template'))\n ) {\n return 'template';\n }\n if (\n pathname.includes('/search') ||\n SEARCH_QUERY_PARAM_KEYS.some(key => new URLSearchParams(search).has(key)) ||\n search.includes('filters[') ||\n (pathname.includes('/catalog') && /[?&](query|term|filters\\[)/.test(search))\n ) {\n return 'search';\n }\n return 'default';\n}\n\nexport function getSearchQueryFromLocation(\n search: string,\n doc: Document = document,\n): string | undefined {\n const params = new URLSearchParams(\n search.startsWith('?') ? search : `?${search}`,\n );\n for (const key of SEARCH_QUERY_PARAM_KEYS) {\n const fromUrl = params.get(key)?.trim();\n if (fromUrl) {\n return fromUrl;\n }\n }\n\n const inputs = doc.querySelectorAll(SEARCH_INPUT_SELECTOR);\n for (const input of inputs) {\n if (!(input instanceof HTMLInputElement)) {\n continue;\n }\n if (!isElementVisible(input)) {\n continue;\n }\n const fromInput = input.value?.trim();\n if (fromInput) {\n return fromInput;\n }\n }\n\n return undefined;\n}\n\n/** @deprecated Prefer resolveScreenContextChipLabel */\nexport function getSearchQueryFromPage(): string | undefined {\n return getSearchQueryFromLocation(window.location.search);\n}\n\n/** Chip label for `/create/templates` gallery (filters in query string). */\nexport const SOFTWARE_TEMPLATES_LIST_CHIP_LABEL = 'Software templates';\n\nconst SOFTWARE_TEMPLATES_GALLERY_PATH = '/create/templates';\n\nfunction normalizePathname(pathname: string): string {\n const trimmed = pathname.trim();\n if (!trimmed || trimmed === '/') {\n return '/';\n }\n return trimmed.replace(/\\/+$/, '') || '/';\n}\n\nexport function isSoftwareTemplatesListPath(pathname: string): boolean {\n return normalizePathname(pathname) === SOFTWARE_TEMPLATES_GALLERY_PATH;\n}\n\nexport function parseSoftwareTemplateDetailSlug(\n pathname: string,\n): string | undefined {\n const normalized = normalizePathname(pathname);\n const prefix = `${SOFTWARE_TEMPLATES_GALLERY_PATH}/`;\n if (!normalized.startsWith(prefix)) {\n return undefined;\n }\n const segments = normalized.slice(prefix.length).split('/').filter(Boolean);\n if (segments.length < 2) {\n return undefined;\n }\n return segments[segments.length - 1];\n}\n\nexport function getSoftwareTemplateTitleFromDom(\n doc: Document = document,\n): string | undefined {\n const roots = getChromeRoots(doc);\n for (const root of roots) {\n const title = queryFirstVisibleTitle(\n root,\n TEMPLATE_INFO_CARD_TITLE_SELECTORS,\n );\n if (title) {\n return title;\n }\n }\n return undefined;\n}\n\n/**\n * Resolves chip text for software template gallery and template entity pages.\n */\nexport function resolveSoftwareTemplateChipLabel(\n pathname: string,\n doc: Document = document,\n): string | undefined {\n if (isSoftwareTemplatesListPath(pathname)) {\n return SOFTWARE_TEMPLATES_LIST_CHIP_LABEL;\n }\n const slug = parseSoftwareTemplateDetailSlug(pathname);\n if (!slug) {\n return undefined;\n }\n return getSoftwareTemplateTitleFromDom(doc) ?? slug;\n}\n\nexport function resolveScreenContextChipLabel(options: {\n pathname: string;\n search: string;\n document?: Document;\n}): ResolvedScreenContextLabel {\n const doc = options.document ?? document;\n const routeKind = getScreenContextRouteKind(options.pathname, options.search);\n\n if (routeKind === 'search') {\n const searchQuery = getSearchQueryFromLocation(options.search, doc);\n if (searchQuery) {\n return {\n label: truncateChipLabel(searchQuery),\n routeKind,\n searchQuery,\n };\n }\n }\n\n if (routeKind === 'template') {\n const softwareTemplateLabel = resolveSoftwareTemplateChipLabel(\n options.pathname,\n doc,\n );\n if (softwareTemplateLabel) {\n return {\n label: truncateChipLabel(softwareTemplateLabel),\n routeKind,\n };\n }\n }\n\n const pageTitle = getPageTitleFromDom(doc);\n return {\n label: truncateChipLabel(pageTitle),\n routeKind,\n searchQuery:\n routeKind === 'search'\n ? getSearchQueryFromLocation(options.search, doc)\n : undefined,\n };\n}\n\nexport function getScreenContextChipLabel(): string {\n return resolveScreenContextChipLabel({\n pathname: window.location.pathname,\n search: window.location.search,\n }).label;\n}\n\nexport type TooltipLine1Params = {\n chipLabel: string;\n routeKind: ScreenContextRouteKind;\n pathname?: string;\n searchQuery?: string;\n};\n\nexport function getScreenContextTooltipLine1Key(\n routeKind: ScreenContextRouteKind,\n pathname?: string,\n):\n | 'contextChip.tooltip.askAbout'\n | 'contextChip.tooltip.template'\n | 'contextChip.tooltip.search' {\n switch (routeKind) {\n case 'template':\n if (pathname && isSoftwareTemplatesListPath(pathname)) {\n return 'contextChip.tooltip.askAbout';\n }\n return 'contextChip.tooltip.template';\n case 'search':\n return 'contextChip.tooltip.search';\n default:\n return 'contextChip.tooltip.askAbout';\n }\n}\n\nexport function getScreenContextTooltipLine1Text(\n params: TooltipLine1Params,\n t: (key: string, options?: Record<string, string>) => string,\n): string {\n const label = params.chipLabel;\n const key = getScreenContextTooltipLine1Key(\n params.routeKind,\n params.pathname,\n );\n return t(key, { label });\n}\n\nexport type TooltipLine2Input = {\n domEnabled: boolean;\n screenshotsEnabled: boolean;\n supportsVision: boolean;\n};\n\nexport function getScreenContextTooltipLine2Key(\n input: TooltipLine2Input,\n): ScreenContextTooltipLine2Key {\n const { domEnabled, screenshotsEnabled, supportsVision } = input;\n\n const willSendDom = domEnabled;\n const willSendScreenshot = screenshotsEnabled && supportsVision;\n\n if (willSendDom && willSendScreenshot) {\n return 'fullContext';\n }\n\n if (!domEnabled && !screenshotsEnabled) {\n return 'adminLimited';\n }\n\n if (!domEnabled && screenshotsEnabled && supportsVision) {\n return 'screenshotOnly';\n }\n\n if (willSendDom) {\n const adminOff = !screenshotsEnabled;\n const noVision = !supportsVision;\n if (adminOff && noVision) {\n return 'textOnlyCombined';\n }\n if (noVision) {\n return 'textOnlyNoVision';\n }\n if (adminOff) {\n return 'textOnlyAdminScreenshotsOff';\n }\n }\n\n // DOM off, screenshots on, but model lacks vision — nothing attaches.\n if (!domEnabled && screenshotsEnabled && !supportsVision) {\n return 'domOffNoVision';\n }\n\n return 'adminLimited';\n}\n\nexport function shouldAttachScreenContext(\n input: ShouldAttachScreenContextInput,\n): ShouldAttachScreenContextResult {\n const baseGate =\n input.adminEnabled &&\n input.sharingEnabled &&\n !input.paused &&\n !input.isFullscreen;\n\n if (!baseGate) {\n return { attachDom: false, attachScreenshot: false };\n }\n\n return {\n attachDom: input.domEnabled,\n attachScreenshot: input.screenshotsEnabled && input.supportsVision,\n };\n}\n\nexport type BuildScreenContextAttachmentsInput =\n ShouldAttachScreenContextInput & {\n domExtractionMaxChars: number;\n };\n\nexport async function buildScreenContextAttachments(\n input: BuildScreenContextAttachmentsInput,\n): Promise<Attachment[]> {\n const { attachDom, attachScreenshot } = shouldAttachScreenContext(input);\n const attachments: Attachment[] = [];\n\n if (attachDom) {\n try {\n const domContext = extractPageContext({\n maxChars: input.domExtractionMaxChars,\n });\n if (domContext) {\n attachments.push({\n attachment_type: 'configuration',\n content_type: 'text/plain',\n content: domContext,\n });\n }\n } catch {\n // non-fatal\n }\n }\n\n if (attachScreenshot) {\n const result = await captureScreenshot({\n excludeSelector: '[data-screen-capture-exclude]',\n });\n if (result.success) {\n attachments.push({\n attachment_type: 'image',\n content_type: result.contentType,\n content: result.base64,\n });\n }\n }\n\n return attachments;\n}\n"],"names":[],"mappings":"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"}
|