diff --git a/packages/utilities/src/activeTab.js b/packages/utilities/src/activeTab.js
index d139ef11..09cf8653 100644
--- a/packages/utilities/src/activeTab.js
+++ b/packages/utilities/src/activeTab.js
@@ -1,11 +1,11 @@
function activeTab(callback) {
- chrome.tabs.query({ currentWindow: true, active: true }, (tabs) =>
+ chrome.tabs.query({ active: true }, (tabs) =>
callback(tabs[0])
);
}
async function activeTabAsync() {
- const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
+ const tabs = await chrome.tabs.query({ active: true });
return tabs[0];
}
diff --git a/packages/webext/.gitignore b/packages/webext/.gitignore
new file mode 100644
index 00000000..49053f3e
--- /dev/null
+++ b/packages/webext/.gitignore
@@ -0,0 +1,28 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# Config files
+.webextrc
+.webextrc.*
diff --git a/packages/webext/package.json b/packages/webext/package.json
new file mode 100644
index 00000000..e433877c
--- /dev/null
+++ b/packages/webext/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "carbon-devtools",
+ "private": true,
+ "version": "3.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ },
+ "dependencies": {
+ "@carbon/ibm-products": "^2.48.0",
+ "@carbon/ibm-security": "^2.15.0",
+ "@carbon/ibmdotcom-utilities": "^2.12.0",
+ "@carbon/ibmdotcom-web-components": "^2.12.0",
+ "@carbon/icons-react": "^10.49.5",
+ "@carbon/react": "^1.65.0",
+ "@carbon/web-components": "^2.12.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.2.1",
+ "sass": "^1.78.0",
+ "vite": "^5.0.0",
+ "vite-plugin-web-extension": "^4.0.0",
+ "webextension-polyfill": "^0.10.0"
+ }
+}
diff --git a/packages/webext/public/icon/128.png b/packages/webext/public/icon/128.png
new file mode 100644
index 00000000..53e9353a
Binary files /dev/null and b/packages/webext/public/icon/128.png differ
diff --git a/packages/webext/public/icon/16.png b/packages/webext/public/icon/16.png
new file mode 100644
index 00000000..3fe36745
Binary files /dev/null and b/packages/webext/public/icon/16.png differ
diff --git a/packages/webext/public/icon/32.png b/packages/webext/public/icon/32.png
new file mode 100644
index 00000000..d1063e5b
Binary files /dev/null and b/packages/webext/public/icon/32.png differ
diff --git a/packages/webext/public/icon/48.png b/packages/webext/public/icon/48.png
new file mode 100644
index 00000000..6c5e5e1c
Binary files /dev/null and b/packages/webext/public/icon/48.png differ
diff --git a/packages/webext/public/icon/96.png b/packages/webext/public/icon/96.png
new file mode 100644
index 00000000..c71c80e0
Binary files /dev/null and b/packages/webext/public/icon/96.png differ
diff --git a/packages/webext/src/background.js b/packages/webext/src/background.js
new file mode 100644
index 00000000..4fee7ac7
--- /dev/null
+++ b/packages/webext/src/background.js
@@ -0,0 +1,7 @@
+import browser from "webextension-polyfill";
+
+console.log("Hello from the background!");
+
+browser.runtime.onInstalled.addListener((details) => {
+ console.log("Extension installed:", details);
+});
diff --git a/packages/webext/src/background/index.js b/packages/webext/src/background/index.js
new file mode 100644
index 00000000..01473342
--- /dev/null
+++ b/packages/webext/src/background/index.js
@@ -0,0 +1,8 @@
+import { validatePage, injectGrid, setBadge } from './tasks';
+import { setStorage } from '@carbon/devtools-utilities/src/setStorage';
+import packageJSON from '../../package.json';
+
+setStorage({ version: packageJSON.version });
+setBadge();
+validatePage();
+injectGrid();
diff --git a/packages/webext/src/background/tasks/index.js b/packages/webext/src/background/tasks/index.js
new file mode 100644
index 00000000..dc7c0b01
--- /dev/null
+++ b/packages/webext/src/background/tasks/index.js
@@ -0,0 +1,3 @@
+export * from './validatePage';
+export * from './injectGrid';
+export * from './setBadge';
diff --git a/packages/webext/src/background/tasks/injectGrid.js b/packages/webext/src/background/tasks/injectGrid.js
new file mode 100644
index 00000000..e8b77321
--- /dev/null
+++ b/packages/webext/src/background/tasks/injectGrid.js
@@ -0,0 +1,33 @@
+import { getMessage } from '@carbon/devtools-utilities/src/getMessage';
+import { insertScriptManifestV3 } from '@carbon/devtools-utilities/src/insertScript';
+import { insertCSSManifestV3 } from '@carbon/devtools-utilities/src/insertCSS';
+import { activeTabAsync } from '@carbon/devtools-utilities/src/activeTab';
+
+function injectGrid() {
+ getMessage(async (msg, sender) => {
+ /* only inject if carbon is found
+ and we haven't injected before */
+ if (msg.runningCarbon && !msg.carbonDevtoolsInjected) {
+ const frameId = msg.ignoreValidation ? 0 : sender.frameId;
+
+ const { id: tabId } = await activeTabAsync();
+ insertScriptManifestV3({
+ files: ['src/inject/index.js'],
+ target: {
+ frameIds: [frameId],
+ tabId,
+ },
+ });
+
+ insertCSSManifestV3({
+ files: ['src/inject/index.css'],
+ target: {
+ frameIds: [frameId],
+ tabId,
+ },
+ });
+ }
+ });
+}
+
+export { injectGrid };
diff --git a/packages/webext/src/background/tasks/setBadge.js b/packages/webext/src/background/tasks/setBadge.js
new file mode 100644
index 00000000..3e415a60
--- /dev/null
+++ b/packages/webext/src/background/tasks/setBadge.js
@@ -0,0 +1,72 @@
+import {
+ experimentalStatusChanged,
+ getExperimentalStatus,
+} from '@carbon/devtools-utilities/src/experimental';
+import { magenta } from '@carbon/colors';
+import { defaults } from '../../globals/defaults';
+
+let iconColor = '#888D94';
+let experimental = defaults.generalSettings.experimental;
+
+function setBadge() {
+ manageIcon();
+ experimentalStatusChanged(manageExperimentalBadge);
+ getExperimentalStatus(manageExperimentalBadge);
+}
+
+function manageExperimentalBadge(status) {
+ experimental = status;
+ updateBadgeByRules();
+}
+
+function updateBadgeByRules() {
+ const dev = process.env.NODE_ENV === 'development';
+
+ if (experimental && dev) {
+ createBadge('DEV', magenta[50]);
+ } else if (experimental) {
+ createBadge('EXP', magenta[50]);
+ } else if (dev) {
+ createBadge('DEV', iconColor);
+ } else {
+ createBadge('', iconColor);
+ }
+}
+
+function createBadge(text, color) {
+ chrome.action.setBadgeText({ text: text });
+ chrome.action.setBadgeBackgroundColor({ color: color });
+}
+
+// https://github.com/w3c/ServiceWorker/issues/1577
+function manageIcon() {
+ if (self.matchMedia) {
+ let manualListener;
+
+ setIcon(self.matchMedia('(prefers-color-scheme: dark)').matches);
+
+ self
+ .matchMedia('(prefers-color-scheme: dark)')
+ .addEventListener('change', (e) => {
+ window.clearInterval(manualListener);
+ manualListener = true;
+ setIcon(e.matches);
+ });
+
+ if (!manualListener) {
+ manualListener = window.setInterval(() => {
+ setIcon(window.matchMedia('(prefers-color-scheme: dark)').matches);
+ }, 1000);
+ }
+ }
+}
+
+function setIcon(darkTheme) {
+ darkTheme
+ ? chrome.action.setIcon({ path: '/media/16x16-dark.png' })
+ : chrome.action.setIcon({ path: '/media/16x16-light.png' });
+
+ updateBadgeByRules();
+}
+
+export { setBadge, createBadge };
diff --git a/packages/webext/src/background/tasks/validatePage.js b/packages/webext/src/background/tasks/validatePage.js
new file mode 100644
index 00000000..1834fe08
--- /dev/null
+++ b/packages/webext/src/background/tasks/validatePage.js
@@ -0,0 +1,43 @@
+import { getMessage } from '@carbon/devtools-utilities/src/getMessage';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { insertScriptManifestV3 } from '@carbon/devtools-utilities/src/insertScript';
+import { activeTabAsync } from '@carbon/devtools-utilities/src/activeTab';
+
+/// WHEN POP UP IS OPENED VALIDATE PAGE
+function validatePage() {
+ getMessage((msg) => {
+ if (msg.popup) {
+ console.log('popup open detected.');
+
+ insertValidation();
+ }
+ });
+}
+
+function insertValidation(callback) {
+ getStorage(['generalNonCarbon'], async ({ generalNonCarbon }) => {
+ const tab = await activeTabAsync();
+
+ if (tab) {
+ insertScriptManifestV3(
+ {
+ files: ['/src/validate/index.js'],
+ target: {
+ allFrames: !generalNonCarbon, // only top level injection if non carbon option is selected
+ tabId: tab.id,
+ },
+ },
+ () => {
+ if (chrome.runtime.lastError) {
+ console.log(chrome.runtime.lastError.message);
+ } else if (typeof callback === 'function') {
+ callback();
+ }
+ }
+ );
+ }
+
+ });
+}
+
+export { validatePage };
diff --git a/packages/webext/src/globals/componentList.js b/packages/webext/src/globals/componentList.js
new file mode 100644
index 00000000..4df75939
--- /dev/null
+++ b/packages/webext/src/globals/componentList.js
@@ -0,0 +1,13 @@
+import {
+ libraries,
+ _results,
+} from '@carbon/devtools-component-list/dist/index.json';
+
+const libraryKeys = Object.keys(libraries);
+const allComponents = {};
+
+libraryKeys.forEach((key) => {
+ Object.assign(allComponents, libraries[key]);
+});
+
+export { allComponents };
diff --git a/packages/webext/src/globals/defaults.js b/packages/webext/src/globals/defaults.js
new file mode 100644
index 00000000..7dc12b6a
--- /dev/null
+++ b/packages/webext/src/globals/defaults.js
@@ -0,0 +1,56 @@
+import { gridVersions } from './options';
+
+const defaults = {};
+
+defaults.global = {
+ gridoverlay: true,
+};
+
+defaults.popup = {
+ carbonStatus: 'loading',
+ panelState: {
+ open: false,
+ children: null,
+ },
+};
+
+defaults.grid = {
+ toggle2xGrid: true,
+};
+
+defaults.toggle2x = {
+ toggle2xColumns: true,
+ toggle2xBreakpoints: true,
+ toggle2xPosition: 'Center',
+ toggle2xLeftInfluencer: 0,
+ toggle2xRightLeftInfluencer: 0,
+};
+
+defaults.miniUnit = {
+ miniUnitVerticalBorders: true,
+ miniUnitHorizontalBorders: true,
+};
+
+defaults.specs = {
+ type: 'typography',
+ outline: true,
+};
+
+defaults.gridVersion = Object.keys(gridVersions)[0];
+
+defaults.generalSettings = {
+ theme: 'g90',
+ experimental: false,
+ nonCarbon: false,
+};
+
+defaults.ga = {
+ isIBMer: 'unknown',
+ clientId: 5555,
+ gridVersion: defaults.gridVersion,
+ generalTheme: defaults.generalSettings.theme,
+ generalExperimental: defaults.generalSettings.experimental,
+ generalNonCarbon: defaults.generalSettings.nonCarbon,
+};
+
+export { defaults };
diff --git a/packages/webext/src/globals/index.js b/packages/webext/src/globals/index.js
new file mode 100644
index 00000000..37f30dfc
--- /dev/null
+++ b/packages/webext/src/globals/index.js
@@ -0,0 +1,3 @@
+export * from './prefixSelectors';
+export * from './options';
+export * from './defaults';
diff --git a/packages/webext/src/globals/options.js b/packages/webext/src/globals/options.js
new file mode 100644
index 00000000..3e77487b
--- /dev/null
+++ b/packages/webext/src/globals/options.js
@@ -0,0 +1,37 @@
+import {
+ AlignHorizontalLeft32,
+ AlignHorizontalCenter32,
+ AlignHorizontalRight32,
+ FitToWidth32,
+} from '@carbon/icons-react';
+
+const positions = {
+ 'Full width': FitToWidth32,
+ Left: AlignHorizontalLeft32,
+ Center: AlignHorizontalCenter32,
+ Right: AlignHorizontalRight32,
+};
+
+const gridVersions = {
+ 'carbon-v10': '2x grid',
+ 'carbon-v9': 'v9 grid',
+ // "northstar-fluid": 'Northstar fluid',
+ // "northstar-v19a": 'Northstar fluid v19a',
+ // "northstar-adaptive": 'Northstar adaptive'
+};
+
+const aspectRatios = [
+ '16:9',
+ '9:16',
+ '2:1',
+ '1:2',
+ '4:3',
+ '3:4',
+ '3:2',
+ '2:3',
+ '1:1',
+];
+
+const svgMarkup = ['svg', 'g', 'path', 'rect', 'polygon', 'circle'];
+
+export { positions, gridVersions, aspectRatios, svgMarkup };
diff --git a/packages/webext/src/globals/prefixSelectors.js b/packages/webext/src/globals/prefixSelectors.js
new file mode 100644
index 00000000..e09672a8
--- /dev/null
+++ b/packages/webext/src/globals/prefixSelectors.js
@@ -0,0 +1,43 @@
+import pkg from '@carbon/ibm-products/es/global/js/package-settings';
+import dotcomSettings from '@carbon/ibmdotcom-utilities/es/utilities/settings/settings';
+import { getComponentNamespace as getSecurityPrefix } from '@carbon/ibm-security/es/globals/namespace';
+import * as carbonSettings from '@carbon/web-components/es/globals/settings.js';
+
+const {
+ devtoolsAttribute: cloudCognitiveDevtoolsAttribute,
+ getDevtoolsId: getCloudCognitiveDevtoolsId,
+} = pkg;
+
+/*
+ note on carbonPrefix
+ in setPrefix.js we set the prefix to carbonPrefix. This gets run on the first pass so the grid gets a prefix unique to devtools. This gets overwritten on the second pass which is why we go back to prefix if it's undefined. Maybe there is a solution for this in the future, but adding this note here so we don't forget for now why it's like this...
+*/
+
+const carbonPrefix = carbonSettings.carbonPrefix || carbonSettings.prefix;
+const { stablePrefix: dotcomPrefix } = dotcomSettings;
+const securityPrefix = getSecurityPrefix('');
+const cloudPalPrefix = 'pal'; // static hardcoded
+const cloudCognitiveDevtoolsId = getCloudCognitiveDevtoolsId('');
+
+const prefixSelectors = [
+ `class*="${carbonPrefix}--"`, // Carbon
+ `${cloudCognitiveDevtoolsAttribute}*="${cloudCognitiveDevtoolsId}"`, // Cloud & Cognitive — https://github.com/carbon-design-system/ibm-cloud-cognitive
+ `data-autoid*="${dotcomPrefix}--"`, // IBM.com
+ `data-auto-id*="${dotcomPrefix}--"`, // IBM.com
+ `class*="${securityPrefix}"`, // Security
+ `class*="${cloudPalPrefix}--"`, // Cloud PAL
+].reduce(
+ (prefixSelectors, prefixSelector, index) =>
+ `${index > 0 ? `${prefixSelectors}, ` : ''}[${prefixSelector}]`,
+ ''
+);
+
+export {
+ prefixSelectors,
+ carbonPrefix,
+ dotcomPrefix,
+ securityPrefix,
+ cloudPalPrefix,
+ getSecurityPrefix,
+ cloudCognitiveDevtoolsId,
+};
diff --git a/packages/webext/src/globals/setPrefix.js b/packages/webext/src/globals/setPrefix.js
new file mode 100644
index 00000000..6654a2e6
--- /dev/null
+++ b/packages/webext/src/globals/setPrefix.js
@@ -0,0 +1,4 @@
+/* eslint-disable no-import-assign */
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+// settings.carbonPrefix = settings.prefix;
+// settings.prefix = `${settings.prefix}-dev`;
diff --git a/packages/webext/src/inject/components/BreakpointLabel/index.js b/packages/webext/src/inject/components/BreakpointLabel/index.js
new file mode 100644
index 00000000..03996009
--- /dev/null
+++ b/packages/webext/src/inject/components/BreakpointLabel/index.js
@@ -0,0 +1,26 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { storageItemChanged } from '@carbon/devtools-utilities/src/storageItemChanged';
+// import { defaults } from '../../../globals/defaults';
+
+const { prefix } = settings;
+
+function initBreakpointLabel() {
+ getStorage('toggleBreakpointLabel', ({ toggleBreakpointLabel }) =>
+ manageToggleBreakpointLabel(toggleBreakpointLabel)
+ ); // set based on defaults
+ storageItemChanged('toggleBreakpointLabel', manageToggleBreakpointLabel); // update ui if options change
+}
+
+function manageToggleBreakpointLabel(toggleBreakpointLabel) {
+ const html = document.querySelector('html');
+
+ // hide or show breakpoint label
+ if (toggleBreakpointLabel) {
+ html.classList.add(`${prefix}--breakpoint-label`);
+ } else {
+ html.classList.remove(`${prefix}--breakpoint-label`);
+ }
+}
+
+export { initBreakpointLabel };
diff --git a/packages/webext/src/inject/components/BreakpointLabel/index.scss b/packages/webext/src/inject/components/BreakpointLabel/index.scss
new file mode 100644
index 00000000..67631829
--- /dev/null
+++ b/packages/webext/src/inject/components/BreakpointLabel/index.scss
@@ -0,0 +1,52 @@
+/* stylelint-disable a11y/media-prefers-reduced-motion */
+@use 'sass:map';
+@use 'sass:math';
+@use '@carbon/react/scss/breakpoint';
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+@use '../../vars.scss' as *;
+
+@mixin label-maker($breakpoints) {
+ &::after {
+ @each $breakpoint in map-keys($breakpoints) {
+ @include breakpoint.breakpoint($breakpoint) {
+ content: 'breakpoint: #{$breakpoint}';
+ }
+ }
+ }
+}
+
+@mixin breakpoint-label--theme {
+ // breakpoint label theme
+ $border-style: 1px solid theme.$border-subtle-01;
+
+ border-top: $border-style;
+ border-left: $border-style;
+ background: theme.$layer-01;
+ color: theme.$text-primary;
+}
+
+.#{$prefix}--devtools {
+ @include label-maker(grid.$grid-breakpoints);
+
+ // breakpoint label
+ &::after {
+ @include type.type-style('code-01');
+ @include type.font-family('mono');
+ @include breakpoint-label--theme;
+
+ position: fixed;
+ z-index: 4;
+ right: 0;
+ bottom: -33px;
+ padding: spacing.$spacing-05 math.div(grid.$grid-gutter, 2);
+ border-bottom: 0;
+ transition: bottom $transition-in;
+
+ .#{$prefix}--breakpoint-label & {
+ bottom: 0;
+ }
+ }
+}
diff --git a/packages/webext/src/inject/components/Grid/2x/index.js b/packages/webext/src/inject/components/Grid/2x/index.js
new file mode 100644
index 00000000..21ae3a00
--- /dev/null
+++ b/packages/webext/src/inject/components/Grid/2x/index.js
@@ -0,0 +1,79 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { storageItemChanged } from '@carbon/devtools-utilities/src/storageItemChanged';
+import { positions } from '../../../../globals/options';
+
+const { prefix } = settings;
+
+function manage2xGrid() {
+ getStorage('toggle2xGridOptions', ({ toggle2xGridOptions }) =>
+ manage2xGridOptions(toggle2xGridOptions)
+ ); // set based on defaults
+ storageItemChanged('toggle2xGridOptions', manage2xGridOptions); // update ui if options change
+}
+
+function manage2xGridOptions({
+ toggle2xColumns,
+ toggle2xGutters,
+ toggle2xBorders,
+ toggle2xPosition,
+ toggle2xLeftInfluencer,
+ toggle2xRightInfluencer,
+}) {
+ const html = document.querySelector('html');
+ const grid2x = html.querySelector(`.${prefix}--grid-2x`);
+ const gridRow = grid2x.querySelector(`.${prefix}--row`);
+
+ // set grid influencers
+ gridRow.style.paddingLeft = `${toggle2xLeftInfluencer || 0}px`;
+ gridRow.style.paddingRight = `${toggle2xRightInfluencer || 0}px`;
+
+ // hide or show columns
+ if (toggle2xColumns) {
+ grid2x.classList.add(`${prefix}--grid-2x--inner`);
+ } else {
+ grid2x.classList.remove(`${prefix}--grid-2x--inner`);
+ }
+
+ // hide or show gutters
+ if (toggle2xGutters) {
+ grid2x.classList.add(`${prefix}--grid-2x--outer`);
+ } else {
+ grid2x.classList.remove(`${prefix}--grid-2x--outer`);
+ }
+
+ // hide or show borders/dividers
+ if (toggle2xBorders) {
+ grid2x.classList.add(
+ `${prefix}--grid-2x--inner-border`,
+ `${prefix}--grid-2x--outer-border`
+ );
+ } else {
+ grid2x.classList.remove(
+ `${prefix}--grid-2x--inner-border`,
+ `${prefix}--grid-2x--outer-border`
+ );
+ }
+
+ // toggle between different position options
+ if (toggle2xPosition) {
+ const grid = resetPosition(grid2x);
+ grid.classList.add(
+ `${prefix}--grid--${toggle2xPosition.toLowerCase().replace(/ /g, '-')}`
+ );
+ }
+}
+
+function resetPosition(grid2x) {
+ const grid = grid2x.querySelector(`.${prefix}--grid`);
+
+ Object.keys(positions).forEach((position) => {
+ grid.classList.remove(
+ `${prefix}--grid--${position.toLowerCase().replace(/ /g, '-')}`
+ );
+ });
+
+ return grid;
+}
+
+export { manage2xGrid };
diff --git a/packages/webext/src/inject/components/Grid/index.js b/packages/webext/src/inject/components/Grid/index.js
new file mode 100644
index 00000000..4701c7c6
--- /dev/null
+++ b/packages/webext/src/inject/components/Grid/index.js
@@ -0,0 +1,126 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { storageItemChanged } from '@carbon/devtools-utilities/src/storageItemChanged';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { gridVersions } from '../../../globals/options';
+import { defaults } from '../../../globals/defaults';
+import { manage2xGrid } from './2x';
+import {
+ manageMiniUnitGrid,
+ showMiniUnitGrid,
+ hideMiniUnitGrid,
+} from './mini-unit';
+import { themes } from '@carbon/themes';
+
+const { prefix } = settings;
+const html = document.querySelector('html');
+const gridVersionsList = Object.keys(gridVersions);
+const themeList = [...Object.keys(themes), 'system'];
+
+let lastTheme = '';
+let lastGridVersion = '';
+
+function initGrid() {
+ const body = html.querySelector('body');
+ const devtoolContainerClass = `${prefix}--devtools`;
+ const numOfColumns = 16;
+ const columns = [];
+
+ let gridContainer = body.querySelector(`.${devtoolContainerClass}`);
+
+ html.classList.add(`${prefix}--grid--hide`);
+
+ for (let i = 0; i < numOfColumns; i++) {
+ columns.push(
+ `
`
+ );
+ }
+
+ const GRID_HTML = `
+
+
+
+ ${columns.join('')}
+
+
+
+
`;
+
+ if (!gridContainer) {
+ gridContainer = document.createElement('div');
+ gridContainer.classList.add(devtoolContainerClass);
+ gridContainer.innerHTML = GRID_HTML;
+ body.appendChild(gridContainer);
+ }
+
+ // updates if storage changes
+ manageGlobals();
+ manage2xGrid();
+ manageMiniUnitGrid();
+}
+
+function manageGlobals() {
+ const grid2x = html.querySelector(`.${prefix}--grid-2x`);
+
+ getStorage('globalToggleStates', ({ globalToggleStates }) =>
+ manageGlobalToggle(globalToggleStates)
+ );
+ storageItemChanged('globalToggleStates', manageGlobalToggle);
+
+ getStorage('toggleGrids', ({ toggleGrids }) => manageGrids(toggleGrids));
+ storageItemChanged('toggleGrids', manageGrids);
+
+ getStorage('generalTheme', ({ generalTheme }) =>
+ manageGeneralTheme(generalTheme)
+ );
+ storageItemChanged('generalTheme', manageGeneralTheme);
+
+ getStorage('gridVersion', ({ gridVersion }) =>
+ manageGridVersion(gridVersion)
+ );
+ storageItemChanged('gridVersion', manageGridVersion);
+
+ function manageGlobalToggle({ gridoverlay }) {
+ // this may not belong here?
+ if (gridoverlay) {
+ html.classList.remove(`${prefix}--grid--hide`);
+ } else {
+ html.classList.add(`${prefix}--grid--hide`);
+ }
+ }
+
+ function manageGrids({ toggle2xGrid, toggleMiniUnitGrid }) {
+ // hide and show 2x grid
+ if (toggle2xGrid) {
+ grid2x.classList.remove(`${prefix}--grid-2x--hide`);
+ } else {
+ grid2x.classList.add(`${prefix}--grid-2x--hide`);
+ }
+
+ // show or hide mini unit grid
+ if (toggleMiniUnitGrid) {
+ showMiniUnitGrid();
+ } else {
+ hideMiniUnitGrid();
+ }
+ }
+
+ function manageGeneralTheme(generalTheme = defaults.generalSettings.theme) {
+ if (generalTheme !== lastTheme) {
+ html.classList.remove(...themeList.map((theme) => `${prefix}--${theme}`)); // remove any first
+ html.classList.add(`${prefix}--${generalTheme}`); // set updated theme
+ lastTheme = generalTheme;
+ }
+ }
+
+ function manageGridVersion(gridVersion = defaults.gridVersion) {
+ if (gridVersion !== lastGridVersion) {
+ html.classList.remove(
+ ...gridVersionsList.map((version) => `${prefix}--grid--${version}`)
+ ); // remove any first
+ html.classList.add(`${prefix}--grid--${gridVersion}`); // set updated theme
+ lastTheme = gridVersion;
+ }
+ }
+}
+
+export { initGrid };
diff --git a/packages/webext/src/inject/components/Grid/index.scss b/packages/webext/src/inject/components/Grid/index.scss
new file mode 100644
index 00000000..f20ca1c4
--- /dev/null
+++ b/packages/webext/src/inject/components/Grid/index.scss
@@ -0,0 +1,253 @@
+/* stylelint-disable a11y/media-prefers-reduced-motion */
+@use 'sass:math';
+@use '../../vars.scss' as *;
+@use '@carbon/react/scss/colors';
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/type';
+@use '@carbon/react/scss/spacing';
+
+.#{$prefix}--devtools {
+ position: absolute;
+ z-index: $zindex;
+ top: 0;
+ right: 0;
+ // bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ opacity: 1;
+ pointer-events: none;
+ transition: opacity $transition-fade;
+
+ * {
+ box-sizing: border-box;
+ }
+
+ // hide grid on click
+ html:active &,
+ body:active & {
+ opacity: 0;
+ }
+
+ &--interact {
+ pointer-events: initial;
+
+ // hide grid on click
+ html:active &,
+ body:active & {
+ opacity: 1;
+ }
+ }
+}
+
+.#{$prefix}--grid {
+ &.#{$prefix}--grid--left {
+ .#{$prefix}--devtools & {
+ margin-left: 0;
+ }
+ }
+
+ &.#{$prefix}--grid--right {
+ .#{$prefix}--devtools & {
+ margin-right: 0;
+ }
+ }
+
+ &-2x {
+ position: fixed;
+ z-index: 3;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ transition: height $transition-in, padding $transition-fade;
+
+ &--hide,
+ .#{$prefix}--grid--hide & {
+ top: auto;
+ bottom: 0;
+ height: 0;
+
+ // minimized column and how it's hidden
+ .#{$prefix}--col--minimize {
+ visibility: hidden;
+ }
+ }
+
+ .#{$prefix}--grid,
+ .#{$prefix}--row,
+ [class*='#{$prefix}--col-'],
+ [class*='#{$prefix}--col-']::before {
+ height: 100%;
+ }
+
+ [class*='#{$prefix}--col-'] {
+ margin-bottom: 100vh;
+
+ &,
+ &::before {
+ transition: height $transition-in, top $transition-in,
+ box-shadow $transition-in, opacity $transition-fade,
+ background-color $transition-fade;
+ }
+
+ &::before {
+ display: block;
+ content: '';
+ }
+
+ // interactive columns
+ .#{$prefix}--devtools--interact &:hover {
+ cursor: pointer;
+ opacity: 0.8;
+ }
+
+ // minimized column
+ &.#{$prefix}--col--minimize {
+ height: spacing.$spacing-07;
+ }
+ }
+
+ // column
+ &--inner {
+ [class*='#{$prefix}--col-']::before {
+ background-color: rgba($grid-inner-color, $grid-inner-opacity);
+ }
+ }
+ &:not(.#{$prefix}--grid-2x--inner) {
+ [class*='#{$prefix}--col-']::before {
+ background-color: rgba($grid-inner-color, 0);
+ }
+ }
+
+ // gutter
+ &--outer {
+ [class*='#{$prefix}--col-']::before {
+ box-shadow: math.div(grid.$grid-gutter, 2) 0 0
+ rgba($grid-outer-color, $grid-outer-opacity),
+ math.div(grid.$grid-gutter, -2) 0 0
+ rgba($grid-outer-color, $grid-outer-opacity);
+ }
+ }
+
+ // column border
+ &--inner-border {
+ [class*='#{$prefix}--col-']::before {
+ box-shadow: $grid-inner-border-size 0 0
+ rgba($grid-inner-border-color, $grid-inner-border-opacity),
+ ($grid-inner-border-size * -1) 0 0
+ rgba($grid-inner-border-color, $grid-inner-border-opacity);
+ }
+ }
+
+ // gutter + column border
+ &--outer.#{$prefix}--grid-2x--inner-border {
+ [class*='#{$prefix}--col-']::before {
+ box-shadow: math.div(grid.$grid-gutter, 2) 0 0
+ rgba($grid-outer-color, $grid-outer-opacity),
+ math.div(grid.$grid-gutter, -2) 0 0
+ rgba($grid-outer-color, $grid-outer-opacity),
+ $grid-inner-border-size 0 0
+ rgba($grid-inner-border-color, $grid-inner-border-opacity),
+ ($grid-inner-border-size * -1) 0 0
+ rgba($grid-inner-border-color, $grid-inner-border-opacity);
+ }
+ }
+
+ // gutter divider
+ &--outer-border {
+ [class*='#{$prefix}--col-'] {
+ box-shadow: $grid-outer-border-size 0 0
+ rgba($grid-outer-border-color, $grid-outer-border-opacity),
+ inset $grid-outer-border-size 0 0
+ rgba($grid-outer-border-color, $grid-outer-border-opacity);
+ }
+ }
+ }
+}
+
+.#{$prefix}--grid-mini-unit {
+ $miniUnitSVG: "data:image/svg+xml;charset=UTF-8,%3csvg width='8px' height='8px' viewBox='0 0 8 8' xmlns='http://www.w3.org/2000/svg'%3e%3crect x='1' y='1' fill='#{$grid-mini-unit-color}' fill-opacity='#{$grid-mini-unit-opacity/2}' width='7' height='7'%3e%3c/rect%3e%3c/svg%3e";
+ $miniUnitGradient: rgba(
+ $grid-mini-unit-border-color,
+ $grid-mini-unit-border-opacity
+ )
+ $grid-mini-unit-border-size,
+ transparent $grid-mini-unit-border-size;
+
+ // mini unit grid
+ &::before,
+ &::after {
+ position: absolute;
+ z-index: 2;
+ top: 0;
+ left: 0;
+ display: block;
+ width: 100%;
+ height: 100%;
+ min-height: 100vh;
+ background-attachment: scroll;
+ background-size: 0.5rem 0.5rem;
+ content: '';
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity $transition-fade;
+ }
+
+ &--show {
+ position: relative;
+ height: initial;
+
+ .#{$prefix}--grid-mini-unit {
+ &::before,
+ &::after {
+ opacity: 1;
+ }
+ }
+ }
+
+ // hide mini-unit grid
+ // &--hide,
+ .#{$prefix}--grid--hide &,
+ &:active {
+ &::before,
+ &::after {
+ opacity: 0;
+ }
+ }
+
+ // mini unit fixed when scrolling
+ &--fixed {
+ &::before,
+ &::after {
+ background-attachment: fixed;
+ }
+ }
+
+ // mini unit cell definition
+ &--cell::before {
+ // opacity: 1;
+ background-image: url($miniUnitSVG);
+ }
+
+ // show horizontal border
+ &--horizontal::after {
+ // opacity: 1;
+ background-image: linear-gradient(to bottom, $miniUnitGradient);
+ }
+
+ // show vertical border
+ &--vertical::after {
+ // opacity: 1;
+ background-image: linear-gradient(to right, $miniUnitGradient);
+ }
+
+ // show both vertical and horizontal border
+ &--horizontal.#{$prefix}--grid-mini-unit--vertical {
+ &::after {
+ // opacity: 1;
+ background-image: linear-gradient(to right, $miniUnitGradient),
+ linear-gradient(to bottom, $miniUnitGradient);
+ }
+ }
+}
diff --git a/packages/webext/src/inject/components/Grid/mini-unit/index.js b/packages/webext/src/inject/components/Grid/mini-unit/index.js
new file mode 100644
index 00000000..68224f83
--- /dev/null
+++ b/packages/webext/src/inject/components/Grid/mini-unit/index.js
@@ -0,0 +1,81 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { storageItemChanged } from '@carbon/devtools-utilities/src/storageItemChanged';
+
+const { prefix } = settings;
+
+const html = document.querySelector('html');
+const body = document.body;
+const miniUnitGridClass = `${prefix}--grid-mini-unit`;
+
+let miniUnitGrid = document.querySelector('.' + miniUnitGridClass);
+
+function manageMiniUnitGrid() {
+ getStorage('toggleMiniUnitGridOptions', ({ toggleMiniUnitGridOptions }) => {
+ manageMiniUnitGridOptions(toggleMiniUnitGridOptions);
+ setMiniUnitHeight();
+ }); // set based on defaults
+ storageItemChanged('toggleMiniUnitGridOptions', manageMiniUnitGridOptions); // update ui if options change
+}
+
+function setMiniUnitHeight() {
+ const scrollHeight = body.scrollHeight;
+ miniUnitGrid =
+ miniUnitGrid || document.querySelector('.' + miniUnitGridClass);
+
+ if (scrollHeight !== miniUnitGrid.offsetHeight) {
+ miniUnitGrid.style.minHeight = scrollHeight + 'px';
+ }
+}
+
+function showMiniUnitGrid() {
+ html.classList.add(`${miniUnitGridClass}--show`);
+ window.addEventListener('resize', setMiniUnitHeight);
+ window.addEventListener('scroll', setMiniUnitHeight);
+}
+
+function hideMiniUnitGrid() {
+ html.classList.remove(`${miniUnitGridClass}--show`);
+ window.removeEventListener('resize', setMiniUnitHeight);
+ window.removeEventListener('scroll', setMiniUnitHeight);
+}
+
+function manageMiniUnitGridOptions({
+ miniUnitCells,
+ miniUnitFix,
+ miniUnitVerticalBorders,
+ miniUnitHorizontalBorders,
+}) {
+ miniUnitGrid =
+ miniUnitGrid || document.querySelector('.' + miniUnitGridClass);
+
+ // ** show or hide mini unit cells
+ if (miniUnitCells) {
+ miniUnitGrid.classList.add(`${miniUnitGridClass}--cell`);
+ } else {
+ miniUnitGrid.classList.remove(`${miniUnitGridClass}--cell`);
+ }
+
+ // Toggle between fixing or scrolling mini unit grid
+ if (miniUnitFix) {
+ miniUnitGrid.classList.add(`${miniUnitGridClass}--fixed`);
+ } else {
+ miniUnitGrid.classList.remove(`${miniUnitGridClass}--fixed`);
+ }
+
+ // show or hide mini unit vertical borders
+ if (miniUnitVerticalBorders) {
+ miniUnitGrid.classList.add(`${miniUnitGridClass}--vertical`);
+ } else {
+ miniUnitGrid.classList.remove(`${miniUnitGridClass}--vertical`);
+ }
+
+ // show or hide mini unit horizontal borders
+ if (miniUnitHorizontalBorders) {
+ miniUnitGrid.classList.add(`${miniUnitGridClass}--horizontal`);
+ } else {
+ miniUnitGrid.classList.remove(`${miniUnitGridClass}--horizontal`);
+ }
+}
+
+export { manageMiniUnitGrid, showMiniUnitGrid, hideMiniUnitGrid };
diff --git a/packages/webext/src/inject/components/Highlight/index.js b/packages/webext/src/inject/components/Highlight/index.js
new file mode 100644
index 00000000..d0f7dbdf
--- /dev/null
+++ b/packages/webext/src/inject/components/Highlight/index.js
@@ -0,0 +1,129 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { findAllDomShadow } from '@carbon/devtools-utilities/src/shadowDom';
+import { randomId } from '@carbon/devtools-utilities/src/randomId';
+
+const { prefix } = settings;
+
+const devtoolsClass = `${prefix}--devtools`;
+const highlightsContainerClass = `${prefix}--highlights`;
+const highlightClass = `${prefix}--highlight`;
+const outlineClass = `${highlightClass}--outline`;
+
+function injectHighlights() {
+ let highlightContainer = document.querySelector(
+ '.' + highlightsContainerClass
+ );
+
+ if (!highlightContainer) {
+ const devtools = document.querySelector('.' + devtoolsClass);
+
+ if (devtools) {
+ highlightContainer = document.createElement('div');
+ highlightContainer.classList.add(highlightsContainerClass);
+
+ devtools.prepend(highlightContainer);
+ }
+ }
+
+ return highlightContainer;
+}
+
+function addHighlight(component, options = {}) {
+ const highlightContainer = injectHighlights();
+ const matchingHighlight = highlightContainer.querySelector(
+ `[data-highlightid="${component.dataset.highlightid}"]`
+ );
+
+ if (!matchingHighlight) {
+ // if the component is already highlight ignore adding another one
+
+ let highlight, type;
+ const contentMax = 40;
+ const highlightID = randomId();
+ const comp = component.getBoundingClientRect();
+ const reuseHighlight = highlightContainer.querySelector(
+ `div:not(.${highlightClass})`
+ );
+
+ if (reuseHighlight) {
+ highlight = reuseHighlight;
+ } else {
+ highlight = document.createElement('div');
+ }
+
+ if (options.type) {
+ type = '--' + options.type; // "specs" type becomes "--specs"
+ }
+
+ if (options.outline) {
+ highlight.classList.add(outlineClass);
+ }
+
+ highlight.classList.add(highlightClass);
+ highlight.classList.add(highlightClass + type);
+
+ highlight.style.top = comp.top + window.scrollY + 'px';
+ highlight.style.left = comp.left + window.scrollX + 'px';
+ highlight.style.width = comp.width + 'px';
+ highlight.style.height = comp.height + 'px';
+
+ highlight.dataset.highlightid = highlightID;
+ component.dataset.highlightid = highlightID;
+
+ // new styling
+ if (!reuseHighlight) {
+ highlightContainer.append(highlight);
+ }
+
+ if (
+ options.content &&
+ comp.width >= contentMax &&
+ comp.height >= contentMax
+ ) {
+ highlight.innerHTML = `${options.content} `;
+ setContenSize(highlight, highlight.querySelector('span'));
+ }
+ }
+}
+
+function setContenSize(highligh, content) {
+ // resizes content to 50% of highlight box
+ const match = 0.5;
+ const maxWidth = 640; // per content guidelines max width standards
+ let contentSize = content.offsetWidth;
+ let containerSize = highligh.clientWidth;
+
+ if (content.offsetHeight > contentSize) {
+ // if the content height is larger than the width than we'll adjust things based on height so not to exceed to the container's boundaries
+ contentSize = content.offsetHeight;
+ containerSize = highligh.clientHeight;
+ }
+
+ if (containerSize > maxWidth) {
+ // this keeps this text from becoming too much on larger images for now
+ containerSize = maxWidth;
+ }
+
+ content.style.transform = `scale(${
+ (containerSize * match * 1) / contentSize
+ })`;
+}
+
+function removeHighlight(component) {
+ component.setAttribute('class', '');
+ component.setAttribute('style', '');
+ component.setAttribute('data-highlightid', '');
+ component.innerHTML = '';
+}
+
+function removeAllHighlights() {
+ const comps = findAllDomShadow('.' + highlightClass);
+
+ if (comps.length > 0) {
+ for (let i = 0; i < comps.length; i += 1) {
+ removeHighlight(comps[i]);
+ }
+ }
+}
+
+export { addHighlight, removeHighlight, removeAllHighlights, injectHighlights };
diff --git a/packages/webext/src/inject/components/Highlight/index.scss b/packages/webext/src/inject/components/Highlight/index.scss
new file mode 100644
index 00000000..88acadd1
--- /dev/null
+++ b/packages/webext/src/inject/components/Highlight/index.scss
@@ -0,0 +1,60 @@
+@use 'sass:map';
+@use '../../vars.scss' as *;
+@use '@carbon/react/scss/colors';
+@use '@carbon/react/scss/type';
+
+$highlight-opacity: 0.5;
+
+.#{$prefix}--highlight {
+ position: absolute;
+ opacity: 0;
+ transition: opacity $transition-fade, background-color $transition-fade,
+ box-shadow $transition-fade;
+
+ &--grid,
+ &--specs,
+ &--inventory {
+ --highlight-color-type-border: transparent;
+ --highlight-color-type-background: transparent;
+
+ display: grid;
+ background-color: var(--highlight-color-type-background);
+ opacity: 1;
+
+ span {
+ @include type.type-style('fluid-paragraph-01', true);
+
+ display: inline-block;
+ margin: auto;
+ color: colors.$magenta-10;
+ }
+ }
+
+ &--outline {
+ background: none;
+ box-shadow: inset 0 0 0 1px var(--highlight-color-type-border);
+ opacity: 1;
+ }
+
+ &--inventory {
+ --highlight-color-type-border: #{$inventory-color};
+ --highlight-color-type-background: #{rgba(
+ $inventory-color,
+ $highlight-opacity
+ )};
+ }
+
+ &--specs {
+ --highlight-color-type-border: #{$specs-color};
+ --highlight-color-type-background: #{rgba($specs-color, $highlight-opacity)};
+ }
+
+ &--grid {
+ --highlight-color-type-border: #{$grid-color};
+ --highlight-color-type-background: #{rgba($grid-color, $highlight-opacity)};
+ }
+
+ body:active & {
+ opacity: 0;
+ }
+}
diff --git a/packages/webext/src/inject/components/Inventory/index.js b/packages/webext/src/inject/components/Inventory/index.js
new file mode 100644
index 00000000..8d3156d1
--- /dev/null
+++ b/packages/webext/src/inject/components/Inventory/index.js
@@ -0,0 +1,292 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+ addHighlight,
+ removeAllHighlights,
+} from '../';
+import { sendMessage } from '@carbon/devtools-utilities/src/sendMessage';
+import { getMessage } from '@carbon/devtools-utilities/src/getMessage';
+import { randomId } from '@carbon/devtools-utilities/src/randomId';
+import {
+ shadowCollection,
+ shadowDomCollector,
+ findAllDomShadow,
+ findSingleDomShadow,
+ shadowContent,
+} from '@carbon/devtools-utilities/src/shadowDom';
+import {
+ libraries,
+ _results as _totals,
+} from '@carbon/devtools-component-list/dist/index.json';
+
+const { prefix } = settings;
+const idselector = 'bxdevid'; // should this be in prefix selector file?
+
+let DOMwatch;
+let waitForObserverToEnd;
+let inventory;
+
+function initInventory() {
+ getMessage((msg) => {
+ if (msg.requestInventory) {
+ const inventoryData = getInventory(libraries);
+ sendMessage({ inventoryData: inventoryData });
+
+ // re-render if page updates
+ if (!DOMwatch) {
+ // don't add more than once
+ DOMwatch = new MutationObserver((mutationList) => {
+ const isSpecs = document.querySelector(`html.${prefix}--specs`);
+
+ mutationList.forEach((mutation) => {
+ if (mutation.type === 'childList' && isSpecs) {
+ clearTimeout(waitForObserverToEnd);
+
+ waitForObserverToEnd = setTimeout(() => {
+ getInventory(libraries, false);
+ // do we need to send a message back to pop up
+ // incase a component has been added or removed while open?
+ }, 100);
+ }
+ });
+ });
+
+ DOMwatch.observe(
+ document.querySelector(`body > *:not(.${prefix}--devtools)`),
+ {
+ childList: true,
+ attributes: false,
+ subtree: true,
+ }
+ );
+ }
+ }
+
+ if (msg.inventoryComponentMouseOut) {
+ // remove all highlights when mouse out
+ removeInventoryHighlights(msg.inventoryComponentMouseOut);
+ }
+
+ if (msg.inventoryComponentMouseOver) {
+ // highlight each in list
+ highlightInventoryItems(msg.inventoryComponentMouseOver);
+ }
+
+ if (msg.inventoryComponentClicked) {
+ // scroll component into view
+ scrollIntoView(msg.inventoryComponentClicked);
+ }
+ });
+}
+
+function scrollIntoView(id) {
+ const component = findSingleDomShadow(`[data-${idselector}*="${id}"]`);
+
+ if (component) {
+ const bodyRect = document.body.getBoundingClientRect();
+ const componentRect = component.getBoundingClientRect();
+ const windowHeight = window.innerHeight;
+ let scrollToPosition = componentRect.top - bodyRect.top;
+
+ if (componentRect.height < windowHeight) {
+ // center it on the page if component height is less than window height
+ scrollToPosition =
+ scrollToPosition -
+ windowHeight / 2 + // centers basic position top on screen
+ componentRect.height / 2; // centers component on screen
+ }
+
+ if (componentRect.top < 0 || componentRect.bottom > windowHeight) {
+ // avoid scrolling if fixed, or in view already
+ window.scrollTo({
+ top: scrollToPosition,
+ behavior: 'smooth',
+ });
+ }
+ }
+}
+
+function highlightInventoryItems(ids) {
+ doThisByIds(ids, highlightInventoryItem);
+}
+
+function removeInventoryHighlights() {
+ // doThisByIds(ids, (comp) => removeHighlight(comp, 'inventory'));
+ removeAllHighlights();
+ showHideTooltip(false);
+}
+
+function highlightInventoryItem(component, id, showTooltip) {
+ addHighlight(component, { type: 'inventory' });
+
+ if (showTooltip) {
+ const idPosition = component.dataset[idselector].split(',').indexOf(id);
+ const componentName =
+ component.dataset.componentname.split(',')[idPosition];
+
+ showHideTooltip(true);
+ positionTooltip(component);
+ updateTooltipContent(`
+ ${componentName}
+ ${component.offsetWidth}x${component.offsetHeight} `);
+ }
+}
+
+function doThisByIds(ids, callback) {
+ const beginning = `[data-${idselector}*="`;
+ const end = `"]`;
+ const selector = beginning + ids.join(end + ', ' + beginning) + end;
+
+ if (ids.length === 1) {
+ const component = findSingleDomShadow(selector);
+
+ if (component) {
+ callback(component, ids[0], true);
+ }
+ } else if (ids.length > 0) {
+ const components = findAllDomShadow(selector);
+
+ if (components.length) {
+ for (let i = 0; i < components.length; i++) {
+ callback(components[i], ids[i], false);
+ }
+ }
+ }
+}
+
+function resetInventory() {
+ shadowCollection.set = shadowDomCollector(document.body);
+
+ const components = findAllDomShadow(`[data-${idselector}]`);
+
+ inventory = {
+ _totals,
+ _results: {
+ totalCount: 0,
+ uniqueCount: 0,
+ },
+ libraries: {},
+ };
+
+ if (components.length > 0) {
+ for (let i = 0; i < components.length; i++) {
+ components[i].removeAttribute(`data-${idselector}`);
+ components[i].removeAttribute(`data-componentname`);
+ }
+ }
+}
+
+function getInventory(reset = true) {
+ if (reset) {
+ resetInventory();
+ }
+
+ const libraryKeys = Object.keys(libraries);
+
+ for (let i = 0; i < libraryKeys.length; i++) {
+ const libraryId = libraryKeys[i];
+ const libraryName = libraries[libraryId].name;
+ const librarySelectors = libraries[libraryId].components;
+
+ getInventoryCollection(libraryId, libraryName, librarySelectors);
+ }
+
+ return inventory;
+}
+
+function getInventoryCollection(libraryId, libraryName, componentList) {
+ // TODO: prefix check first?
+ // document.querySelector('.bx--');
+
+ const selectors = Object.keys(componentList);
+
+ for (let i = 0; i < selectors.length; i++) {
+ // loop through indidivual selectors/components
+ const selector = selectors[i];
+ const componentName = componentList[selector];
+ const components = findAllDomShadow(selector);
+ const inventoryData = updateInventory(
+ componentName,
+ libraryName,
+ components
+ );
+
+ if (inventoryData.length > 0) {
+ if (!inventory.libraries[libraryId]) {
+ // set the library in place if it doesn't exist yet
+ inventory.libraries[libraryId] = {
+ name: libraryName,
+ components: {},
+ };
+ inventory._results[libraryId] = {
+ unique: 0,
+ total: 0,
+ };
+ }
+
+ if (!inventory.libraries[libraryId].components[componentName]) {
+ inventory._results.uniqueCount += 1; // add to the unique count
+ inventory._results[libraryId].unique += 1; // add to the unique count for the library
+ inventory.libraries[libraryId].components[componentName] =
+ inventoryData;
+ } else {
+ inventory.libraries[libraryId].components[componentName].concat(
+ inventoryData
+ );
+ }
+
+ inventory._results.totalCount += inventoryData.length; // add to the total count
+ inventory._results[libraryId].total += inventoryData.length; // add to the total count for the library
+ }
+ }
+
+ return inventory;
+}
+
+function updateInventory(componentName, libraryName, components) {
+ const inventory = [];
+
+ if (components.length > 0) {
+ // if no components don't waste more time
+ for (let i = 0; i < components.length; i++) {
+ // loop through each component and build data, and assign ids
+ const component = components[i];
+ const uniqueID = randomId();
+ const componentNames = component.dataset['componentname'];
+ let push = false;
+
+ if (!componentNames) {
+ component.dataset[idselector] = uniqueID; // set a unique id to find later
+ component.dataset['componentname'] = componentName; // set a unique id to find later
+ push = true;
+ } else if (componentNames.indexOf(componentName) === -1) {
+ component.dataset[idselector] += ',' + uniqueID; // set a unique id to find later
+ component.dataset['componentname'] += ',' + componentName; // set a unique id to find later
+ push = true;
+ }
+
+ if (push) {
+ inventory.push({
+ library: libraryName,
+ name: componentName,
+ uniqueID: uniqueID,
+ outerHTML: component.outerHTML,
+ innerText: shadowContent(component.childNodes), // not really innertext anymore
+ tag: component.localName,
+ id: component.id,
+ classes: component.classList.value.split(' '),
+ autoid:
+ component.dataset['autoid'] || component.dataset['auto-id'] || '',
+ href: component.href,
+ src: component.src,
+ });
+ }
+ }
+ }
+
+ return inventory;
+}
+
+export { initInventory };
diff --git a/packages/webext/src/inject/components/Shortcuts/index.js b/packages/webext/src/inject/components/Shortcuts/index.js
new file mode 100644
index 00000000..236466d4
--- /dev/null
+++ b/packages/webext/src/inject/components/Shortcuts/index.js
@@ -0,0 +1,105 @@
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { setStorage } from '@carbon/devtools-utilities/src/setStorage';
+import {
+ initToggleColumns,
+ addGridInteraction,
+ removeGridInteraction,
+} from './toggleColumns';
+
+const holdingKeys = [];
+
+function manageShortcuts() {
+ const shift = 16;
+ const control = 17;
+
+ // shift + ctrl
+ if (holdingKeys.indexOf(shift) > -1 && holdingKeys.indexOf(control) > -1) {
+ // base conditions satified
+ toggleEverything(0);
+ toggle2xGrid(1);
+ toggleMiniUnitGrid(2);
+ toggleBreakpointLabel(3);
+ }
+}
+
+function toggleEverything(num) {
+ numberShortcut(num, () => {
+ toggleDataOptions('globalToggleStates', 'gridoverlay');
+ });
+}
+
+function toggle2xGrid(num) {
+ numberShortcut(num, () => toggleDataOptions('toggleGrids', 'toggle2xGrid'));
+}
+
+function toggleMiniUnitGrid(num) {
+ numberShortcut(num, () =>
+ toggleDataOptions('toggleGrids', 'toggleMiniUnitGrid')
+ );
+}
+
+function toggleBreakpointLabel(num) {
+ numberShortcut(num, () =>
+ toggleDataOptions('toggle2xGridOptions', 'toggle2xBreakpoints')
+ );
+}
+
+function toggleDataOptions(group, option) {
+ getStorage(group, (data) => {
+ const options = data[group];
+
+ if (options && Object.keys(options).indexOf(option) > -1) {
+ if (options[option]) {
+ options[option] = false;
+ } else {
+ options[option] = true;
+ }
+
+ setStorage(data);
+ }
+ });
+}
+
+function numberShortcut(num, callback) {
+ if (num >= 0 && num <= 9) {
+ const shortcutNum = 48 + num; // 1-9 = 48 - 57
+
+ if (
+ holdingKeys.length === 3 &&
+ holdingKeys.indexOf(shortcutNum) > -1 &&
+ typeof callback === 'function'
+ ) {
+ callback();
+ }
+ }
+}
+
+function addKey(key) {
+ if (holdingKeys.indexOf(key) < 0) {
+ holdingKeys.push(key);
+ }
+}
+
+function removeKey(key) {
+ const keyIndex = holdingKeys.indexOf(key);
+
+ if (keyIndex > -1) {
+ holdingKeys.splice(keyIndex, 1);
+ }
+}
+
+function initShortcuts() {
+ document.addEventListener('keydown', (e) => {
+ addKey(e.keyCode);
+ manageShortcuts();
+ addGridInteraction(holdingKeys);
+ });
+
+ document.addEventListener('keyup', (e) => {
+ removeGridInteraction(holdingKeys);
+ removeKey(e.keyCode);
+ });
+ initToggleColumns();
+}
+
+export { initShortcuts };
diff --git a/packages/webext/src/inject/components/Shortcuts/toggleColumns.js b/packages/webext/src/inject/components/Shortcuts/toggleColumns.js
new file mode 100644
index 00000000..e7eb2a22
--- /dev/null
+++ b/packages/webext/src/inject/components/Shortcuts/toggleColumns.js
@@ -0,0 +1,44 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+
+const { prefix } = settings;
+const devtoolClass = `${prefix}--devtools`;
+const interactionModifier = `${devtoolClass}--interact`;
+const shift = 16;
+const backquote = 192;
+
+function initToggleColumns() {
+ const columns = document.querySelectorAll(`[class*="${prefix}--col"]`);
+
+ columns.forEach((column) => {
+ column.addEventListener('click', toggleColumn);
+ });
+}
+
+function toggleColumn(e) {
+ const column = e.currentTarget;
+ column.classList.toggle(`${prefix}--col--minimize`);
+}
+
+function addGridInteraction(handleKeys) {
+ if (
+ handleKeys.length === 2 &&
+ handleKeys.indexOf(backquote) > -1 &&
+ handleKeys.indexOf(shift) > -1
+ ) {
+ const devtools = document.querySelector('.' + devtoolClass);
+ devtools.classList.add(interactionModifier);
+ }
+}
+
+function removeGridInteraction(handleKeys) {
+ if (
+ handleKeys.length === 2 &&
+ handleKeys.indexOf(backquote) > -1 &&
+ handleKeys.indexOf(shift) > -1
+ ) {
+ const devtools = document.querySelector('.' + devtoolClass);
+ devtools.classList.remove(interactionModifier);
+ }
+}
+
+export { initToggleColumns, addGridInteraction, removeGridInteraction };
diff --git a/packages/webext/src/inject/components/Specs/Color/getThemeName.js b/packages/webext/src/inject/components/Specs/Color/getThemeName.js
new file mode 100644
index 00000000..c2f0aa3d
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Color/getThemeName.js
@@ -0,0 +1,28 @@
+import { themes } from '@carbon/themes';
+import { themeKeys } from './themeKeys';
+import { searchCarbonTokens } from './searchCarbonTokens';
+
+function getThemeName(target) {
+ // check ui background because as of writing this it has a unique value within each theme.
+ const uiBackground = getComputedStyle(target)
+ .getPropertyValue('--cds-ui-background')
+ .replace(/ /g, '');
+ let theme = false;
+
+ if (uiBackground) {
+ // find the matching token among all theme tokens in carbon
+ let searchResults = searchCarbonTokens(
+ themes,
+ uiBackground,
+ ['uiBackground'].concat(themeKeys.theme)
+ );
+ if (searchResults) {
+ // parse out the theme name which should be the first segment
+ theme = [searchResults.name.split('-')[0]];
+ }
+ }
+
+ return theme;
+}
+
+export { getThemeName };
diff --git a/packages/webext/src/inject/components/Specs/Color/index.js b/packages/webext/src/inject/components/Specs/Color/index.js
new file mode 100644
index 00000000..fc6a34d1
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Color/index.js
@@ -0,0 +1,234 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { addHighlight } from '../../Highlight';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+ __specValueItem,
+ __specsContainer,
+} from '../../Tooltip';
+import { getComponentName } from '@carbon/devtools-utilities/src/getComponentName';
+import { doesItHaveText } from '@carbon/devtools-utilities/src/doesItHaveText';
+import { colors } from '@carbon/colors';
+import { themes } from '@carbon/themes';
+import { searchCarbonTokens } from './searchCarbonTokens';
+import { themeKeys } from './themeKeys';
+import { getThemeName } from './getThemeName';
+import Color from 'color';
+
+const { prefix } = settings;
+
+function highlightSpecsColor(target) {
+ if (target) {
+ const styles = window.getComputedStyle(target);
+ const themeName = getThemeName(target);
+ const themes = themeName || themeKeys.theme; // do we know the current theme? If not give them all of them.
+ const borderColorGroups = combineBorderColors(styles, themes);
+
+ let tooltipGroups = [];
+
+ if (doesItHaveText(target.childNodes, target)) {
+ tooltipGroups.push({
+ eyebrow: '',
+ title: 'Type color',
+ content: tooltipContent(styles.color, themeKeys.text.concat(themes)),
+ });
+ }
+
+ if (styles.backgroundColor !== 'rgba(0, 0, 0, 0)') {
+ tooltipGroups.push({
+ eyebrow: '',
+ title: 'Background color',
+ content: tooltipContent(
+ styles.backgroundColor,
+ themeKeys.background.concat(themes)
+ ),
+ });
+ }
+
+ if (target.nodeName === 'path') {
+ tooltipGroups.push({
+ eyebrow: '',
+ title: 'Fill',
+ content: tooltipContent(styles.fill, themeKeys.icon.concat(themes)),
+ });
+ }
+
+ if (borderColorGroups.length > 0) {
+ tooltipGroups = tooltipGroups.concat(borderColorGroups);
+ }
+
+ if (tooltipGroups.length > 0) {
+ updateTooltipContent(
+ __specsContainer(tooltipGroups).replace(
+ //g,
+ getComponentName(target)
+ )
+ );
+ addHighlight(target, { type: 'specs' });
+ positionTooltip(target); // mouse location?
+ showHideTooltip(true);
+ return tooltipGroups.length;
+ }
+ }
+}
+
+function combineBorderColors(styles, themes) {
+ let borderGroups = [];
+
+ const validate = {
+ color: {
+ all:
+ styles.borderTopColor === styles.borderBottomColor &&
+ styles.borderRightColor === styles.borderLeftColor &&
+ styles.borderTopColor === styles.borderLeftColor,
+ horizontal: styles.borderTopColor === styles.borderBottomColor,
+ vertical: styles.borderRightColor === styles.borderLeftColor,
+ },
+ width: {
+ all:
+ styles.borderTopWidth !== '0px' ||
+ styles.borderRightWidth !== '0px' ||
+ styles.borderBottomWidth !== '0px' ||
+ styles.borderLeftWidth !== '0px',
+ horizontal:
+ styles.borderTopWidth !== '0px' || styles.borderBottomWidth !== '0px',
+ vertical:
+ styles.borderRightWidth !== '0px' || styles.borderLeftWidth !== '0px',
+ top: styles.borderTopWidth !== '0px',
+ right: styles.borderRightWidth !== '0px',
+ bottom: styles.borderBottomWidth !== '0px',
+ left: styles.borderLeftWidth !== '0px',
+ },
+ };
+
+ if (validate.width.all && validate.color.all) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Border color',
+ content: tooltipContent(
+ styles.borderTopColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ } else {
+ if (validate.width.horizontal && validate.color.horizontal) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Horizontal borders color',
+ content: tooltipContent(
+ styles.borderTopColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ } else {
+ if (validate.width.top) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Border top color',
+ content: tooltipContent(
+ styles.borderTopColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ }
+
+ if (validate.width.bottom) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Border bottom color',
+ content: tooltipContent(
+ styles.borderBottomColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ }
+ }
+
+ if (validate.width.vertical && validate.color.vertical) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Vertical borders color',
+ content: tooltipContent(
+ styles.borderRightColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ } else {
+ if (validate.width.right) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Border right color',
+ content: tooltipContent(
+ styles.borderRightColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ }
+
+ if (validate.width.left) {
+ borderGroups.push({
+ eyebrow: '',
+ title: 'Border left color',
+ content: tooltipContent(
+ styles.borderLeftColor,
+ themeKeys.border.concat(themes)
+ ),
+ });
+ }
+ }
+ }
+
+ return borderGroups;
+}
+
+function tooltipContent(value, scopedKeys) {
+ let html = '';
+
+ if (value) {
+ // is this accross all browsers?
+ const computedColor = Color(value).hex().toLowerCase();
+ const idlToken = searchCarbonTokens(colors, computedColor);
+ const rgbValue = Color(value).rgb().toString().toLowerCase();
+
+ html += `
+
+
`;
+ html += ``;
+
+ if (idlToken) {
+ let carbonToken = searchCarbonTokens(themes, computedColor, scopedKeys);
+
+ if (carbonToken) {
+ // carbon theme token
+ carbonToken = carbonToken.name.split('-');
+ html += __specValueItem(
+ 'CDS:',
+ `$${carbonToken[1]} (${carbonToken[0]})`
+ );
+ }
+
+ // idl token
+ html += __specValueItem('IDL:', idlToken.name);
+ } else {
+ // warning message because no token was found
+ html += __specValueItem('warning', 'Token not found');
+ }
+
+ // hex
+ html += __specValueItem('HEX:', computedColor);
+
+ // rgb(a)
+ // is there a cleaner way to do this?
+ html += __specValueItem(
+ rgbValue.indexOf('rgb(') === 0 ? 'RGB:' : 'RGBA:',
+ rgbValue
+ );
+
+ html += ` `;
+ }
+
+ return html;
+}
+
+export { highlightSpecsColor };
diff --git a/packages/webext/src/inject/components/Specs/Color/index.scss b/packages/webext/src/inject/components/Specs/Color/index.scss
new file mode 100644
index 00000000..25bb9b92
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Color/index.scss
@@ -0,0 +1,36 @@
+@use '../../../vars.scss' as *;
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+
+.#{$specs-prefix} {
+ &-color-tooltip {
+ &__color {
+ display: inline-block;
+ overflow: hidden;
+ border-radius: 50%;
+ margin-bottom: spacing.$spacing-03;
+ background: repeating-linear-gradient(
+ 90deg,
+ #ffffff,
+ #ffffff 8px,
+ #000000 8px,
+ #000000 16px
+ );
+
+ span {
+ display: block;
+ width: spacing.$spacing-05;
+ height: spacing.$spacing-05;
+ }
+ }
+
+ // tooltip theme
+ @mixin color-tooltip--theme {
+ &__color {
+ border: 1px solid theme.$border-strong-01;
+ }
+ }
+
+ @include color-tooltip--theme;
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Color/searchCarbonTokens.js b/packages/webext/src/inject/components/Specs/Color/searchCarbonTokens.js
new file mode 100644
index 00000000..f2ac5a8c
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Color/searchCarbonTokens.js
@@ -0,0 +1,71 @@
+import { prioritizeThemes } from './themeKeys';
+
+/*
+ as of right I can't be 100% sure I'm getting the correct token here on the
+ theme side, but here are the steps I'm taking to make an educated guess.
+
+ IDL Color Token (100%)
+ 1. search all IDL tokens until I find a match
+
+ Carbon Theme
+ 0. only execute search if idl color is found first
+ 1. check target and see if it has custom properties, and grab the
+ used theme based on some checks around the unique list of keys.
+ search based on that single theme if found.
+ 2. search through all themes if no active theme is found.
+ 3. scope tokens checked for based on types (e.g. type, background, border, icon)
+ 4. if a unique token is match, grab it's theme, and reorder the master list too
+ prioritize that going forward. The thought process here is in general most pages use a single theme.
+ If a theme has been identified then we can assume the next element will have a high chance it's using
+ that same theme.
+*/
+
+function searchCarbonTokens(tokens, color, scopedKeys = [], base = '') {
+ let tokenValue = false;
+
+ if (typeof tokens === 'object' && !Array.isArray(tokens)) {
+ const keys = Object.keys(tokens);
+ let key, token;
+
+ for (let i = 0; i < keys.length; i += 1) {
+ key = keys[i];
+ token = tokens[key];
+
+ if (scopedKeys.length === 0 || scopedKeys.indexOf(key) > -1) {
+ // check if scopedkeys is defined.
+ // if it's not continue to search everything.
+ // if it is, only continue if key is part of the scoped list.
+ if (typeof token === 'string') {
+ // if it's a string we know we found the token value
+ if (token == color) {
+ // if token and color match
+ // 1. add to name,
+ // 2. return values, and
+ // 3. reorder theme list to make a better educated guess moving forward
+ // 4. break loop so we don't go looking anymore.
+ prioritizeThemes(key, base.split('-')[0]);
+
+ tokenValue = {
+ name: base + '-' + key, // token name
+ color: token,
+ };
+
+ break; // break loop
+ }
+ } else {
+ // if it's not a string let's recursively check the next object
+ tokenValue = searchCarbonTokens(token, color, scopedKeys, base + key);
+
+ if (tokenValue) {
+ // if our search returned a result break the loop
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return tokenValue;
+}
+
+export { searchCarbonTokens };
diff --git a/packages/webext/src/inject/components/Specs/Color/themeKeys.js b/packages/webext/src/inject/components/Specs/Color/themeKeys.js
new file mode 100644
index 00000000..dc33a9ca
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Color/themeKeys.js
@@ -0,0 +1,68 @@
+import { themes } from '@carbon/themes';
+
+// identifying which ones belong to text, background,
+// and such helps increase our chances of detecting an
+// accurate token by removing unrelated tokens with the
+// same value
+
+const themeKeys = {
+ theme: Object.keys(themes),
+ unique: ['uiBackground', 'field01', 'field02', 'disabled01'],
+ border: ['ui03', 'ui04', 'ui05', 'focus', 'disabled01', 'disabled02'],
+ text: [
+ 'text01',
+ 'text02',
+ 'text03',
+ 'text04',
+ 'text05',
+ 'interactive01',
+ 'interactive02',
+ 'textError',
+ 'link01',
+ 'inverse01',
+ 'inverseLink',
+ 'hoverPrimaryText',
+ 'skeleton02',
+ 'disabled02',
+ 'disabled03',
+ 'visitedLink',
+ ],
+ background: [
+ 'uiBackground',
+ 'danger',
+ 'ui01',
+ 'ui02',
+ 'ui03',
+ 'field01',
+ 'field02',
+ 'inverse02',
+ 'overlay01',
+ 'hoverUI',
+ 'disabled01',
+ 'interactive01',
+ 'interactive02',
+ 'interactive03',
+ ],
+ icon: [
+ 'interactive04',
+ 'icon01',
+ 'icon02',
+ 'icon03',
+ 'inverse01',
+ 'disabled02',
+ 'disabled03',
+ ],
+};
+
+function prioritizeThemes(key, theme) {
+ if (themeKeys.unique.indexOf(key) > -1) {
+ // does this key match a unique theme token?
+ // cycle through and find which theme the value belongs to...
+ const themeLocation = themeKeys.theme.indexOf(theme);
+
+ themeKeys.theme.splice(themeLocation, 1);
+ themeKeys.theme.unshift(theme);
+ }
+}
+
+export { themeKeys, prioritizeThemes };
diff --git a/packages/webext/src/inject/components/Specs/Dependencies/index.js b/packages/webext/src/inject/components/Specs/Dependencies/index.js
new file mode 100644
index 00000000..b4b58541
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Dependencies/index.js
@@ -0,0 +1,123 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { addHighlight } from '../../Highlight';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+} from '../../Tooltip';
+import { findAllDomShadow } from '@carbon/devtools-utilities/src/shadowDom';
+import { formInventoryData } from '@carbon/devtools-utilities/src/formInventoryData';
+import componentList from '@carbon/devtools-component-list/dist/index.json';
+const { prefix } = settings;
+const { allComponents } = formInventoryData(componentList);
+const selectors = Object.keys(allComponents).join(',');
+
+const specsDependenciesClass = `${prefix}--specs-dependencies-tooltip`;
+
+function highlightSpecsDependencies(target) {
+ let componentName;
+ let componentIdentified = false;
+ let tooltipContent = ``;
+ let siblings = target.getAttribute('data-componentname');
+ let dependencies = [];
+ let unique;
+
+ if (!siblings && target.nodeName === 'BODY') {
+ siblings = 'Page template';
+ }
+
+ if (siblings) {
+ componentIdentified = true;
+ siblings = siblings.split(','); // create array from list of names
+ componentName = siblings.pop(); // pull off the last item for point name
+ dependencies = findAllDomShadow(selectors, target); // get dependencies
+
+ tooltipContent += `${componentName} `;
+
+ // manage siblings
+ if (siblings.length) {
+ unique = [];
+ tooltipContent += `
+
+
siblings
+
+ `;
+
+ for (let i = 0; i < siblings.length; i += 1) {
+ const siblingName = siblings[i];
+
+ if (siblingName && unique.indexOf(siblingName) < 0) {
+ unique.push(siblingName);
+ tooltipContent += `${siblingName} `;
+ }
+ }
+
+ tooltipContent += ` `;
+
+ if (unique.length > 0) {
+ tooltipContent = tooltipContent.replace(
+ //g,
+ `${unique.length} `
+ );
+ }
+ }
+
+ // manage dependencies
+ if (dependencies.length) {
+ unique = [];
+ tooltipContent += `
+
+
dependencies
+
+ `;
+
+ for (let i = 0; i < dependencies.length; i += 1) {
+ const dependency = dependencies[i];
+ let dependencyNameList = dependency.getAttribute('data-componentname');
+
+ if (dependencyNameList) {
+ dependencyNameList = dependencyNameList.split(',');
+
+ // looping through siblings
+ for (let i = 0; i < dependencyNameList.length; i += 1) {
+ const dependencyName = dependencyNameList[i];
+
+ if (dependencyName && unique.indexOf(dependencyName) < 0) {
+ unique.push(dependencyName);
+ tooltipContent += `${dependencyName} `;
+ }
+ }
+
+ addHighlight(dependency, { type: 'specs' });
+ }
+ }
+
+ tooltipContent += ` `;
+
+ if (unique.length > 0) {
+ tooltipContent = tooltipContent.replace(
+ //g,
+ `${unique.length} `
+ );
+ }
+ }
+
+ if (dependencies.length === 0 && siblings.length === 0) {
+ tooltipContent += `No dependencies found rendered at this time.
`;
+ }
+ }
+
+ if (componentIdentified) {
+ updateTooltipContent(`
+
+ ${tooltipContent}
+
+ `);
+ addHighlight(target, { type: 'specs', outline: true });
+ positionTooltip(target);
+ showHideTooltip(true);
+ return dependencies;
+ }
+}
+
+export { highlightSpecsDependencies };
diff --git a/packages/webext/src/inject/components/Specs/Dependencies/index.scss b/packages/webext/src/inject/components/Specs/Dependencies/index.scss
new file mode 100644
index 00000000..2f3e1ec2
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Dependencies/index.scss
@@ -0,0 +1,74 @@
+@use '../../../vars.scss' as *;
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+
+.#{$specs-prefix} {
+ &-dependencies-tooltip {
+ min-width: calc(#{spacing.$spacing-03} * 20);
+ max-width: calc(#{spacing.$spacing-03} * 40);
+
+ &__group {
+ margin-top: calc(#{spacing.$spacing-03} * 1);
+ }
+
+ &__title {
+ @include type.type-style('heading-compact-01');
+
+ margin-bottom: calc(#{spacing.$spacing-03} * 1);
+ }
+
+ &__sub {
+ @include type.type-style('label-01');
+
+ display: inline-block;
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ p#{&}__empty {
+ @include type.type-style('helper-text-01');
+
+ margin-right: 25%;
+ }
+
+ &__list {
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+
+ &-item {
+ display: inline-block;
+ padding: 0 calc(#{spacing.$spacing-03} * 1);
+ border-radius: 3px;
+ margin-right: grid.$grid-gutter-condensed * 2;
+ margin-bottom: grid.$grid-gutter-condensed * 2;
+ }
+
+ &-item::before {
+ display: none;
+ }
+ }
+
+ // tooltip theme
+ @mixin dependencies-tooltip--theme {
+ &__title {
+ color: theme.$text-primary;
+ }
+
+ &__empty,
+ &__sub {
+ color: theme.$text-secondary;
+ }
+
+ &__list--unorder,
+ &__list-item {
+ background-color: theme.$layer-02;
+ color: theme.$text-primary;
+ }
+ }
+
+ @include dependencies-tooltip--theme;
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Grid/index.js b/packages/webext/src/inject/components/Specs/Grid/index.js
new file mode 100644
index 00000000..3fe510ae
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Grid/index.js
@@ -0,0 +1,114 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { breakpoints, rem } from '@carbon/layout';
+import { carbonPrefix } from '../../../../globals';
+import { addHighlight } from '../../Highlight';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+} from '../../Tooltip';
+
+const { prefix } = settings;
+
+const gridSelector = `${carbonPrefix}--grid`;
+const rowSelector = `${carbonPrefix}--row`;
+const colSelector = `${carbonPrefix}--col`;
+const specsGridClass = `${prefix}--specs-grid-tooltip`;
+const titleClass = `${specsGridClass}__title`;
+const classListClass = `${specsGridClass}__classlist`;
+
+function highlightSpecsGrid(target) {
+ // can we use computed values to determine grid, row, col,
+ // and free ourselves from class names incase adopters are using mixins?
+ const highlightOptions = { outline: true };
+ let grid = false;
+ let tooltipContent = ``;
+
+ if (!grid && target && target.classList) {
+ if (target.classList.contains(gridSelector)) {
+ grid = true;
+ tooltipContent += `
Grid `;
+ tooltipContent += `
+
+ `;
+ }
+
+ if (target.classList.contains(rowSelector)) {
+ grid = true;
+ tooltipContent += `
Row `;
+ tooltipContent += `
+
+ `;
+ }
+
+ if (target.classList.value.indexOf(colSelector) > -1) {
+ grid = true;
+
+ let classList, active;
+
+ classList = target.classList.value;
+
+ if (classList.indexOf('-sm-') === -1) {
+ classList = `${carbonPrefix}--col-sm-4 ${classList}`;
+ }
+
+ active = activeBreakpoint(classList);
+
+ classList = classList
+ .split(' ')
+ .map((className) => {
+ if (className.indexOf(colSelector) > -1) {
+ if (className.indexOf(active) > -1) {
+ return `
.${className} `;
+ } else {
+ return `
.${className} `;
+ }
+ }
+ })
+ .filter((d) => d)
+ .join('');
+
+ tooltipContent += `
Column `;
+ tooltipContent += `
`;
+ }
+ }
+
+ tooltipContent += '
';
+
+ if (grid) {
+ addHighlight(target, { ...highlightOptions, type: 'specs' });
+ updateTooltipContent(tooltipContent);
+ positionTooltip(target);
+ showHideTooltip(true);
+ return grid;
+ }
+}
+
+function activeBreakpoint(classList) {
+ const breakpointKeys = Object.keys(breakpoints);
+ const l = breakpointKeys.length;
+ const windowWidth = rem(window.innerWidth);
+ let breakpointName, breakpoint, active;
+
+ for (let i = 0; i < l; i++) {
+ breakpointName = breakpointKeys[i];
+ // if class list contains this breakpoint
+ if (classList.indexOf(breakpointName) > -1) {
+ breakpoint = breakpoints[breakpointName];
+ // AND breakpoint width is less than window width
+ if (parseInt(breakpoint.width, 10) <= parseInt(windowWidth, 10)) {
+ active = breakpointName;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return active;
+}
+
+export { highlightSpecsGrid };
diff --git a/packages/webext/src/inject/components/Specs/Grid/index.scss b/packages/webext/src/inject/components/Specs/Grid/index.scss
new file mode 100644
index 00000000..08f06f87
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Grid/index.scss
@@ -0,0 +1,80 @@
+@use '../../../vars.scss' as *;
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+
+.#{$specs-prefix} {
+ &-grid-tooltip {
+ max-width: calc(#{spacing.$spacing-03} * 34);
+ margin-bottom: calc(#{spacing.$spacing-03} * -0.5);
+
+ &__title {
+ @include type.type-style('heading-compact-01');
+
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ &__title,
+ &__classlist li {
+ display: inline-block;
+ margin-right: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ &__classlist {
+ @include type.type-style('code-01');
+
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ list-style-type: none;
+
+ li {
+ display: inline-block;
+ padding: 0;
+ border-radius: 3px;
+ margin: 0;
+ margin-right: calc(#{spacing.$spacing-03} * 0.5);
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ li::before {
+ display: none;
+ }
+
+ li::after {
+ content: ',';
+ }
+
+ li:last-child {
+ margin-right: 0;
+ }
+
+ li:last-child::after {
+ content: '';
+ }
+
+ li#{&}--active {
+ padding: 0 calc(#{spacing.$spacing-03} * 1);
+ }
+
+ li#{&}--active::after {
+ display: none;
+ }
+ }
+
+ // tooltip theme
+ @mixin grid-tooltip--theme {
+ &__title {
+ color: theme.$text-primary;
+ }
+
+ &__classlist--active {
+ background-color: theme.$layer-02;
+ color: theme.$text-primary;
+ }
+ }
+
+ @include grid-tooltip--theme;
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Outline/index.js b/packages/webext/src/inject/components/Specs/Outline/index.js
new file mode 100644
index 00000000..624cdb9e
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Outline/index.js
@@ -0,0 +1,24 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+
+const { prefix } = settings;
+
+const html = document.querySelector('html');
+const outlineClass = `${prefix}--specs--outline`;
+
+function manageSpecsOutline(specs, specOutline) {
+ if (specs && specOutline) {
+ activateOutline();
+ } else {
+ deactiveOutline();
+ }
+}
+
+function activateOutline() {
+ html.classList.add(outlineClass);
+}
+
+function deactiveOutline() {
+ html.classList.remove(outlineClass);
+}
+
+export { manageSpecsOutline };
diff --git a/packages/webext/src/inject/components/Specs/Outline/index.scss b/packages/webext/src/inject/components/Specs/Outline/index.scss
new file mode 100644
index 00000000..1e485723
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Outline/index.scss
@@ -0,0 +1,24 @@
+/* stylelint-disable a11y/media-prefers-reduced-motion */
+@use '../../../vars.scss' as *;
+
+.#{$specs-prefix} {
+ &.#{$specs-prefix}--outline body > *:not([class*='#{$prefix}--']) {
+ &,
+ * {
+ box-shadow: inset 0 0 0 1px rgba($specs-color, 0.1);
+ transition: box-shadow $transition-fade;
+
+ &:hover {
+ box-shadow: inset 0 0 0 1px $specs-color;
+ }
+ }
+ }
+
+ // hide on interaction
+ &.#{$specs-prefix}--outline:active body > *:not([class*='#{$prefix}--']) {
+ &,
+ * {
+ box-shadow: inset 0 0 0 1px rgba($specs-color, 0);
+ }
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Ratio/index.js b/packages/webext/src/inject/components/Specs/Ratio/index.js
new file mode 100644
index 00000000..26e31df5
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Ratio/index.js
@@ -0,0 +1,56 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { addHighlight } from '../../Highlight';
+import { getComponentName } from '@carbon/devtools-utilities/src/getComponentName';
+import { aspectRatios, svgMarkup } from '../../../../globals/options';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+} from '../../Tooltip';
+
+const { prefix } = settings;
+
+const aspectRatiosCalc = aspectRatios.map((ratio) => {
+ const vals = ratio.split(':');
+ return vals[0] / vals[1];
+});
+
+function highlightSpecsRatio(target) {
+ const comp = target.getBoundingClientRect();
+ const width = Math.round(comp.width);
+ const height = Math.round(comp.height);
+ const minValue = 16; // get from two mini units?
+
+ if (width > minValue && height > minValue) {
+ const highlightOptions = {};
+ const ratioIndex = aspectRatiosCalc.indexOf(width / height);
+ const componentName =
+ svgMarkup.indexOf(target.nodeName.toLowerCase()) > -1
+ ? 'SVG'
+ : getComponentName(target); // build this logic into the get component name?
+
+ let tooltipContent = ``;
+
+ if (ratioIndex > -1) {
+ highlightOptions.content = aspectRatios[ratioIndex];
+
+ tooltipContent += `${aspectRatios[ratioIndex]}
+ (${width}x${height}) `;
+ } else {
+ highlightOptions.outline = true;
+
+ tooltipContent += `${width}x${height} `;
+ }
+
+ tooltipContent += `${componentName} `;
+
+ addHighlight(target, { ...highlightOptions, type: 'specs' });
+ updateTooltipContent(tooltipContent);
+ positionTooltip(target);
+ showHideTooltip(true);
+
+ return true;
+ }
+}
+
+export { highlightSpecsRatio };
diff --git a/packages/webext/src/inject/components/Specs/Ratio/index.scss b/packages/webext/src/inject/components/Specs/Ratio/index.scss
new file mode 100644
index 00000000..08f06f87
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Ratio/index.scss
@@ -0,0 +1,80 @@
+@use '../../../vars.scss' as *;
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+
+.#{$specs-prefix} {
+ &-grid-tooltip {
+ max-width: calc(#{spacing.$spacing-03} * 34);
+ margin-bottom: calc(#{spacing.$spacing-03} * -0.5);
+
+ &__title {
+ @include type.type-style('heading-compact-01');
+
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ &__title,
+ &__classlist li {
+ display: inline-block;
+ margin-right: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ &__classlist {
+ @include type.type-style('code-01');
+
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ list-style-type: none;
+
+ li {
+ display: inline-block;
+ padding: 0;
+ border-radius: 3px;
+ margin: 0;
+ margin-right: calc(#{spacing.$spacing-03} * 0.5);
+ margin-bottom: calc(#{spacing.$spacing-03} * 0.5);
+ }
+
+ li::before {
+ display: none;
+ }
+
+ li::after {
+ content: ',';
+ }
+
+ li:last-child {
+ margin-right: 0;
+ }
+
+ li:last-child::after {
+ content: '';
+ }
+
+ li#{&}--active {
+ padding: 0 calc(#{spacing.$spacing-03} * 1);
+ }
+
+ li#{&}--active::after {
+ display: none;
+ }
+ }
+
+ // tooltip theme
+ @mixin grid-tooltip--theme {
+ &__title {
+ color: theme.$text-primary;
+ }
+
+ &__classlist--active {
+ background-color: theme.$layer-02;
+ color: theme.$text-primary;
+ }
+ }
+
+ @include grid-tooltip--theme;
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Spacing/index.js b/packages/webext/src/inject/components/Specs/Spacing/index.js
new file mode 100644
index 00000000..1bd8fb5f
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Spacing/index.js
@@ -0,0 +1,355 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { addHighlight } from '../../Highlight';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+ __specValueItem,
+ __specsContainer,
+} from '../../Tooltip';
+import { getComponentName } from '@carbon/devtools-utilities/src/getComponentName';
+import { setPx } from '@carbon/devtools-utilities/src/setPx';
+import { removeLeadingZero } from '@carbon/devtools-utilities/src/removeLeadingZero';
+import { rem, spacing, layout } from '@carbon/layout';
+
+const { prefix } = settings;
+
+const spacersClass = `${prefix}--spacers`;
+const spacerBoxClass = `${spacersClass}__box`;
+
+let spacerList, activeTarget;
+
+function highlightSpecsSpacing(target) {
+ injectSpacers(); // remove and refactor if this becomes a problem
+
+ if (
+ target.classList.contains(spacerBoxClass) && // don't run if we are on a spacer box
+ target === activeTarget
+ ) {
+ return true; // return true to let them know we are on top of something, and don't want to clear it yet.
+ }
+
+ const boundingBox = target.getBoundingClientRect();
+ const styles = window.getComputedStyle(target);
+
+ let spacerCount = 0;
+
+ spacerCount = setSpacers(target, boundingBox, spacerList, {
+ top: {
+ margin: stripUnit(styles.marginTop),
+ padding: stripUnit(styles.paddingTop),
+ border: stripUnit(styles.borderTopWidth),
+ },
+ right: {
+ margin: stripUnit(styles.marginRight),
+ padding: stripUnit(styles.paddingRight),
+ border: stripUnit(styles.borderRightWidth),
+ },
+ bottom: {
+ margin: stripUnit(styles.marginBottom),
+ padding: stripUnit(styles.paddingBottom),
+ border: stripUnit(styles.borderBottomWidth),
+ },
+ left: {
+ margin: stripUnit(styles.marginLeft),
+ padding: stripUnit(styles.paddingLeft),
+ border: stripUnit(styles.borderLeftWidth),
+ },
+ });
+
+ if (spacerCount) {
+ activeTarget = target;
+ addHighlight(target, { type: 'specs', outline: true });
+ return spacerCount;
+ }
+}
+
+function getSpacingToken(value) {
+ const valueRem = rem(value);
+
+ for (let i = 0; i < spacing.length; i++) {
+ if (valueRem === spacing[i]) {
+ return 'spacing-' + leadingZero(i + 1);
+ }
+ }
+
+ for (let i = 0; i < layout.length; i++) {
+ if (valueRem === layout[i]) {
+ return 'layout-' + leadingZero(i + 1);
+ }
+ }
+}
+
+function leadingZero(value) {
+ if (value < 10) {
+ return '0' + value;
+ }
+
+ return value;
+}
+
+function setSpacers(target, boundingBox, spacerList, spacingStyles) {
+ const orientations = Object.keys(spacingStyles);
+
+ let orientation,
+ spacing,
+ topLeft,
+ bottomRight,
+ componentName,
+ spacerCount = 0;
+
+ for (let i = 0; i < orientations.length; i++) {
+ orientation = orientations[i];
+ spacing = spacingStyles[orientation];
+ topLeft = ['top', 'left'].indexOf(orientation) > -1;
+ bottomRight = ['bottom', 'right'].indexOf(orientation) > -1;
+
+ if (spacing.margin) {
+ componentName = componentName || getComponentName(target); // only grab new if not already defined
+
+ spacerCount += positionSpacer(
+ componentName,
+ Math.abs(spacing.margin), // value
+ orientation, // current orientation
+ spacerList[spacerCount], // spacer element
+ spacingStyles,
+ target, // origin target
+ boundingBox, // origin target's bounding box
+ Math.abs(topLeft ? spacing.margin : 0), // offset value
+ true
+ );
+ }
+
+ if (spacing.padding) {
+ componentName = componentName || getComponentName(target); // only grab new if not already defined
+
+ spacerCount += positionSpacer(
+ componentName,
+ Math.abs(spacing.padding), // value
+ orientation, // current orientation
+ spacerList[spacerCount], // spacer element
+ spacingStyles,
+ target, // origin target
+ boundingBox, // origin target's bounding box
+ Math.abs(bottomRight ? spacing.padding : 0) // offset value
+ );
+ }
+ }
+
+ return spacerCount;
+}
+
+function positionSpacer(
+ componentName,
+ value,
+ orientation,
+ spacer,
+ spacingStyles,
+ target,
+ boundingBox,
+ offset,
+ ignoreBorders
+) {
+ resetSpacer(spacer);
+
+ const maxWidth =
+ target.clientWidth -
+ spacingStyles.left.padding -
+ spacingStyles.right.padding;
+ const maxHeight =
+ target.clientHeight -
+ spacingStyles.top.padding -
+ spacingStyles.bottom.padding;
+
+ spacer.dataset.value = value;
+ spacer.dataset.component = componentName;
+
+ if (value >= 16) {
+ spacer.innerHTML = `${value} `;
+
+ if (value < spacer.querySelector('.value').offsetWidth) {
+ // if the text doesn't fit in the box then let's remove the text
+ spacer.innerHTML = '';
+ }
+ }
+
+ spacer.style.opacity = 1;
+ spacer.style.visibility = 'visible';
+
+ switch (orientation) {
+ case 'right':
+ spacer.style.top = setPx(
+ boundingBox.top +
+ spacingStyles.top.padding +
+ (ignoreBorders ? 0 : spacingStyles.top.border)
+ ); // top + top padding + top border
+ spacer.style.left = setPx(
+ boundingBox.left +
+ boundingBox.width -
+ offset -
+ (ignoreBorders ? 0 : spacingStyles.right.border)
+ ); // left + width - offset container - right border
+ spacer.style.height = setPx(value);
+ spacer.style.minHeight = setPx(16);
+ spacer.style.maxHeight = setPx(maxHeight);
+ spacer.style.width = setPx(value);
+ break;
+ case 'bottom':
+ spacer.style.top = setPx(
+ boundingBox.top +
+ boundingBox.height -
+ offset -
+ (ignoreBorders ? 0 : spacingStyles.bottom.border)
+ ); // top + height - offset container - bottom border
+ spacer.style.left = setPx(
+ boundingBox.left +
+ spacingStyles.left.padding +
+ (ignoreBorders ? 0 : spacingStyles.left.border)
+ ); // left + left padding + left border
+ spacer.style.height = setPx(value);
+ spacer.style.width = setPx(value);
+ spacer.style.minWidth = setPx(16);
+ spacer.style.maxWidth = setPx(maxWidth);
+ break;
+ case 'left':
+ spacer.style.top = setPx(
+ boundingBox.top +
+ spacingStyles.top.padding +
+ (ignoreBorders ? 0 : spacingStyles.top.border)
+ ); // top + top padding + top border
+ spacer.style.left = setPx(
+ boundingBox.left -
+ offset +
+ (ignoreBorders ? 0 : spacingStyles.left.border)
+ ); // left - offset container + left border
+ spacer.style.height = setPx(value);
+ spacer.style.minHeight = setPx(16);
+ spacer.style.maxHeight = setPx(maxHeight);
+ spacer.style.width = setPx(value);
+ break;
+ default: // top - offset container + top border
+ // left + left padding + left border
+ // top
+ spacer.style.top = setPx(
+ boundingBox.top -
+ offset +
+ (ignoreBorders ? 0 : spacingStyles.top.border)
+ );
+ spacer.style.left = setPx(
+ boundingBox.left +
+ spacingStyles.left.padding +
+ (ignoreBorders ? 0 : spacingStyles.left.border)
+ );
+ spacer.style.height = setPx(value);
+ spacer.style.width = setPx(value);
+ spacer.style.minWidth = setPx(16);
+ spacer.style.maxWidth = setPx(maxWidth);
+ }
+
+ return 1;
+}
+
+function stripUnit(value) {
+ return parseFloat(value, 10);
+}
+
+function spacerHover(e) {
+ const spacer = e.currentTarget;
+
+ if (spacer.dataset.value.length > 0 && spacer.dataset.component.length > 0) {
+ // get token
+ // no token value warning
+ const value = spacer.dataset.value;
+ const spacingToken = getSpacingToken(value);
+
+ let tooltipContent = `
+ ${removeLeadingZero(
+ Math.round(value * 1000) / 1000
+ )}px /
+ ${removeLeadingZero(
+ Math.round(
+ parseFloat(rem(value), 10) * 1000
+ ) / 1000
+ )}rem
+
`;
+
+ if (!spacingToken) {
+ tooltipContent += `
+ ${__specValueItem('warning', 'Token not found')}
+ `;
+ }
+
+ updateTooltipContent(
+ __specsContainer([
+ {
+ eyebrow: spacer.dataset.component,
+ title: spacingToken || 'Spacing',
+ content: tooltipContent,
+ },
+ ])
+ );
+ positionTooltip(spacer); // mouse location?
+ showHideTooltip(true);
+ }
+}
+
+function resetAllSpacers() {
+ if (spacerList && spacerList.length) {
+ spacerList.forEach(resetSpacer);
+ }
+}
+
+function resetSpacer(spacer) {
+ spacer.innerHTML = ``;
+ spacer.style.width = null;
+ spacer.style.height = null;
+ spacer.style.minHeight = null;
+ spacer.style.maxHeight = null;
+ spacer.style.minWidth = null;
+ spacer.style.maxWidth = null;
+ spacer.style.top = null;
+ spacer.style.left = null;
+ spacer.style.opacity = null;
+ spacer.style.visibility = null;
+ spacer.dataset.value = '';
+ spacer.dataset.component = '';
+}
+
+function mouseOutSpacing(e) {
+ let target = e.relatedTarget;
+
+ const result =
+ !target || // if target is null clear it all
+ (target !== activeTarget && !target.classList.contains(spacerBoxClass)); // clear it if not going to active target
+ if (result) {
+ // and if not a spacer
+ // if we've hit the end let's stop and remove everything
+ activeTarget = null;
+ }
+
+ return result;
+}
+
+function injectSpacers() {
+ const spacers = document.querySelector(`.${spacersClass}`);
+
+ if (!spacers) {
+ const devtoolsContainer = document.querySelector(`.${prefix}--devtools`);
+ const spacersHTML = document.createElement('div');
+ spacersHTML.classList.add(spacersClass);
+
+ for (let i = 0; i < 8; i++) {
+ spacersHTML.innerHTML += `
`;
+ }
+
+ devtoolsContainer.appendChild(spacersHTML);
+
+ spacerList = document.querySelectorAll(`.${spacerBoxClass}`);
+ spacerList.forEach((spacer) => {
+ spacer.addEventListener('mouseenter', spacerHover);
+ spacer.addEventListener('mouseleave', () => showHideTooltip(false));
+ });
+ }
+}
+
+export { highlightSpecsSpacing, resetAllSpacers, mouseOutSpacing };
diff --git a/packages/webext/src/inject/components/Specs/Spacing/index.scss b/packages/webext/src/inject/components/Specs/Spacing/index.scss
new file mode 100644
index 00000000..766d98b5
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Spacing/index.scss
@@ -0,0 +1,27 @@
+/* stylelint-disable a11y/media-prefers-reduced-motion */
+@use '../../../vars.scss' as *;
+@use '@carbon/react/scss/type';
+
+.#{$prefix}--spacers {
+ pointer-events: all;
+
+ &__box {
+ @include type.type-style('code-01');
+
+ position: fixed;
+ z-index: $zindex / 2;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba($specs-color, 0.9);
+ box-shadow: inset 0 0 0 1px $specs-color;
+ color: #ffffff;
+ opacity: 0;
+ transition: opacity $transition-fade, visibility $transition-fade;
+ visibility: hidden;
+
+ * {
+ pointer-events: none;
+ }
+ }
+}
diff --git a/packages/webext/src/inject/components/Specs/Typography/index.js b/packages/webext/src/inject/components/Specs/Typography/index.js
new file mode 100644
index 00000000..ccb71e09
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/Typography/index.js
@@ -0,0 +1,379 @@
+import { addHighlight } from '../../Highlight';
+import {
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+ __specValueItem,
+ __specsContainer,
+} from '../../Tooltip';
+import { getComponentName } from '@carbon/devtools-utilities/src/getComponentName';
+import { doesItHaveText } from '@carbon/devtools-utilities/src/doesItHaveText';
+import { getActiveBreakpoint } from '@carbon/devtools-utilities/src/getActiveBreakpoint';
+import { removeLeadingZero } from '@carbon/devtools-utilities/src/removeLeadingZero';
+import { fontWeights } from '@carbon/type/src/fontWeight';
+import * as styles from '@carbon/type/src/styles';
+import { rem } from '@carbon/layout';
+
+function highlightSpecsType(target) {
+ if (target) {
+ const compStyles = window.getComputedStyle(target);
+
+ let typeToken,
+ typeCount = 0,
+ tooltipGroups = [],
+ tooltipContent = '';
+
+ if (doesItHaveText(target.childNodes, target)) {
+ typeCount = 1;
+ typeToken = getTypeToken(target, compStyles, styles);
+
+ tooltipContent += ``;
+
+ if (!typeToken) {
+ tooltipContent += __specValueItem('warning', 'Token not found');
+ }
+
+ tooltipContent += `
+ ${__specValueItem(
+ 'type:',
+ formatFontFamily(compStyles.fontFamily)
+ )}
+ ${__specValueItem(
+ 'size:',
+ formatPxValue(compStyles.fontSize)
+ )}
+ ${__specValueItem(
+ 'line-height:',
+ formatPxValue(compStyles.lineHeight)
+ )}
+ ${__specValueItem(
+ 'weight:',
+ getFontWeight(compStyles.fontWeight, fontWeights)
+ )}
+ ${__specValueItem(
+ 'letter-spacing:',
+ formatLetterSpacing(compStyles.letterSpacing)
+ )}
+ `;
+
+ tooltipGroups.push({
+ eyebrow: getComponentName(target),
+ title: typeToken || 'Type styles',
+ content: tooltipContent,
+ });
+ }
+
+ if (typeCount > 0) {
+ updateTooltipContent(__specsContainer(tooltipGroups));
+ addHighlight(target, { type: 'specs' });
+ positionTooltip(target); // mouse location?
+ showHideTooltip(true);
+ return typeCount;
+ }
+ }
+}
+
+function getTypeToken(target, compStyles, carbonStyles) {
+ const tokens = Object.keys(carbonStyles);
+ let token, tokenStyles, range, lowRange, highRange, tokenName;
+
+ if (target.dataset['typetokenname']) {
+ return target.dataset['typetokenname'];
+ } else {
+ for (let i = 0; i < tokens.length; i++) {
+ let matches = 0;
+
+ token = tokens[i];
+ tokenStyles = carbonStyles[token];
+
+ if (tokenStyles.breakpoints) {
+ range = styleRange(tokenStyles);
+ lowRange = range[0] || {};
+ highRange = range[1] || {};
+
+ // ranged checks for expressive type
+ if (lowRange.fontSize && highRange.fontSize) {
+ if (
+ validateRange(
+ normalizeUnit(compStyles.fontSize),
+ normalizeUnit(lowRange.fontSize),
+ normalizeUnit(highRange.fontSize)
+ )
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ if (lowRange.lineHeight && highRange.lineHeight) {
+ if (
+ validateRange(
+ unitlessLineHeight(compStyles.lineHeight, compStyles.fontSize),
+ normalizeUnit(lowRange.lineHeight, lowRange.fontSize),
+ normalizeUnit(lowRange.lineHeight, highRange.fontSize)
+ )
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ matches += fixedChecks(compStyles, lowRange, true);
+ } else {
+ // fixed checks for productive type
+ matches += fixedChecks(compStyles, tokenStyles);
+ }
+
+ if (matches === 5) {
+ tokenName = token;
+ target.dataset['typetokenname'] = token;
+
+ break;
+ }
+ }
+ }
+
+ return tokenName;
+}
+
+function fixedChecks(compStyles, tokenStyles, range) {
+ let matches = 0;
+ if (!range) {
+ if (tokenStyles.fontSize) {
+ if (
+ normalizeUnit(compStyles.fontSize) ===
+ normalizeUnit(tokenStyles.fontSize)
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ if (tokenStyles.lineHeight) {
+ if (
+ unitlessLineHeight(
+ compStyles.lineHeight,
+ compStyles.fontSize,
+ tokenStyles.lineHeight
+ ) === normalizeUnit(tokenStyles.lineHeight)
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+ }
+
+ if (tokenStyles.fontFamily) {
+ if (
+ normalizeFontFamily(compStyles.fontFamily) ===
+ normalizeFontFamily(tokenStyles.fontFamily)
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ if (tokenStyles.letterSpacing) {
+ if (
+ normalizeLetterSpacing(compStyles.letterSpacing) ===
+ normalizeLetterSpacing(tokenStyles.letterSpacing)
+ ) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ if (tokenStyles.fontWeight) {
+ if (Number(compStyles.fontWeight) === tokenStyles.fontWeight) {
+ matches++;
+ }
+ } else {
+ matches++;
+ }
+
+ return matches;
+}
+
+function styleRange(styles) {
+ const breakpoints = Object.keys(styles.breakpoints);
+ const breakpointState = getActiveBreakpoint();
+ const range = [];
+ let breakpoint, breakpointStyles, level;
+
+ range.push(JSON.parse(JSON.stringify(styles))); // copy over defaults
+
+ for (let i = 0; i < breakpoints.length; i++) {
+ breakpoint = breakpoints[i];
+ breakpointStyles = styles.breakpoints[breakpoint];
+ level = range.length - 1;
+
+ if (range.length === 2) {
+ if (breakpoint === breakpointState.after[0]) {
+ // if 1 up active breakpoint
+ updateProperties(range[level], breakpointStyles);
+ }
+ break;
+ } else {
+ // range is 1
+ // update if <= active breakpoint
+ if (
+ breakpoint === breakpointState.active ||
+ breakpointState.before.indexOf(breakpoint) > -1
+ ) {
+ updateProperties(range[level], breakpointStyles);
+ }
+ // if this is >= active
+ // or if this is the last item and we still don't have a second level
+ if (
+ breakpoint === breakpointState.active ||
+ breakpointState.after.indexOf(breakpoint) > -1 ||
+ i + 1 === breakpoints.length
+ ) {
+ range.push(JSON.parse(JSON.stringify(range[level])));
+
+ if (breakpoint === breakpointState.after[0]) {
+ updateProperties(range[level + 1], breakpointStyles);
+ break;
+ }
+ }
+ }
+ }
+
+ function updateProperty(property, existingStyles, newStyles) {
+ existingStyles[property] = newStyles[property]
+ ? newStyles[property]
+ : existingStyles[property];
+ }
+
+ function updateProperties(existingStyles, newStyles) {
+ updateProperty('fontFamily', existingStyles, newStyles);
+ updateProperty('fontSize', existingStyles, newStyles);
+ updateProperty('fontWeight', existingStyles, newStyles);
+ updateProperty('letterSpacing', existingStyles, newStyles);
+ updateProperty('lineHeight', existingStyles, newStyles);
+ }
+
+ return range;
+}
+
+function validateRange(val, low, high) {
+ const baseline = Math.abs(low - high);
+
+ if (baseline >= Math.abs(low - val) && baseline >= Math.abs(high - val)) {
+ return true;
+ }
+}
+
+function normalizeFontFamily(fontFamily) {
+ return fontFamily.replace(/['"]/g, '').toLowerCase();
+}
+
+function normalizeLetterSpacing(value) {
+ if (value === 'normal') {
+ value = 0;
+ }
+
+ return normalizeUnit(value);
+}
+
+function unitlessLineHeight(lineHeight, fontSize, matchRound) {
+ let fixDecimal = String(matchRound).split('.');
+
+ if (fixDecimal.length > 1) {
+ fixDecimal = Math.pow(10, fixDecimal[1].length);
+ } else {
+ fixDecimal = Math.pow(10, 3);
+ }
+
+ return (
+ Math.round(
+ (normalizeUnit(lineHeight) / normalizeUnit(fontSize)) * fixDecimal
+ ) / fixDecimal
+ );
+}
+
+function normalizeUnit(unit, fontSize) {
+ unit = String(unit);
+
+ let normUnit = parseFloat(unit, 10);
+
+ if (unit.indexOf('px') > -1) {
+ normUnit = parseFloat(rem(normUnit), 10);
+ } else if (unit.indexOf('%') > -1) {
+ normUnit = (normUnit / 100) * normalizeUnit(fontSize);
+ }
+
+ return Math.round(normUnit * 1000) / 1000; // round to 3 places
+}
+
+function formatFontFamily(compValue) {
+ let familyValue = '';
+
+ if (compValue) {
+ familyValue = compValue.split(',')[0].replace(/"/g, '');
+ }
+
+ return familyValue;
+}
+
+function formatLetterSpacing(compValue) {
+ let spaceValue = '';
+
+ if (compValue) {
+ if (compValue === 'normal') {
+ spaceValue = '0';
+ } else {
+ spaceValue = formatPxValue(compValue);
+ }
+ }
+
+ return spaceValue;
+}
+
+function formatPxValue(compValue) {
+ let pxNum,
+ remNum,
+ pxValue = '';
+
+ if (compValue) {
+ pxNum = removeLeadingZero(
+ Math.round(parseFloat(compValue, 10) * 1000) / 1000
+ ); // round to 3 places
+ remNum = removeLeadingZero(
+ Math.round(parseFloat(rem(pxNum), 10) * 1000) / 1000
+ ); // round to 3 places
+
+ pxValue = `${pxNum}px / ${remNum}rem`;
+ }
+
+ return pxValue;
+}
+
+function getFontWeight(compValue, carbonWeights) {
+ const weights = Object.keys(carbonWeights);
+ let weight,
+ returnedWeight = '';
+
+ if (compValue) {
+ returnedWeight = compValue;
+
+ for (let i = 0; i < weights.length; i++) {
+ weight = weights[i];
+
+ if (Number(compValue) === carbonWeights[weight]) {
+ returnedWeight += ` / ${weight}`;
+ break;
+ }
+ }
+ }
+
+ return returnedWeight;
+}
+
+export { highlightSpecsType };
diff --git a/packages/webext/src/inject/components/Specs/collection.js b/packages/webext/src/inject/components/Specs/collection.js
new file mode 100644
index 00000000..b4395fb1
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/collection.js
@@ -0,0 +1,7 @@
+export * from './Color';
+export * from './Dependencies';
+export * from './Grid';
+export * from './Outline';
+export * from './Ratio';
+export * from './Spacing';
+export * from './Typography';
diff --git a/packages/webext/src/inject/components/Specs/index.js b/packages/webext/src/inject/components/Specs/index.js
new file mode 100644
index 00000000..335996c9
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/index.js
@@ -0,0 +1,254 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { storageItemChanged } from '@carbon/devtools-utilities/src/storageItemChanged';
+import { randomId } from '@carbon/devtools-utilities/src/randomId';
+import { findSingleDomShadow } from '@carbon/devtools-utilities/src/shadowDom';
+import { removeAllHighlights } from '../Highlight';
+import { showHideTooltip } from '../Tooltip';
+import { defaults } from '../../../globals/defaults';
+import {
+ manageSpecsOutline,
+ highlightSpecsType,
+ highlightSpecsColor,
+ highlightSpecsDependencies,
+ highlightSpecsSpacing,
+ resetAllSpacers,
+ mouseOutSpacing,
+ highlightSpecsGrid,
+ highlightSpecsRatio,
+} from './collection';
+
+const { prefix } = settings;
+
+const html = document.querySelector('html');
+const specsClass = `${prefix}--specs`;
+
+const ignoreNodes = ['BODY', '#document-fragment']; // nodes to ignore below
+
+const specToolOptions = {
+ typography: highlightSpecsType,
+ color: highlightSpecsColor,
+ dependencies: highlightSpecsDependencies,
+ spacing: highlightSpecsSpacing,
+ grid: highlightSpecsGrid,
+ ratio: highlightSpecsRatio,
+};
+
+const state = {
+ specs: defaults.global.specs,
+ specType: defaults.specs.type,
+ specOutline: defaults.specs.outline,
+};
+
+let shadowEventCollection = [];
+let _noPointerRef = [];
+
+function initSpecs() {
+ // set onload based on defaults
+ getStorage(
+ ['globalToggleStates', 'toggleSpecs'],
+ ({ globalToggleStates, toggleSpecs }) => {
+ manageSpecsState('specs', globalToggleStates.specs);
+ manageSpecsState('specType', toggleSpecs.type);
+ manageSpecsState('specOutline', toggleSpecs.outline);
+ }
+ );
+
+ // update ui if options change
+ storageItemChanged('globalToggleStates', ({ specs }) =>
+ manageSpecsState('specs', specs)
+ );
+ storageItemChanged('toggleSpecs', (toggleSpecs) => {
+ manageSpecsState('specType', toggleSpecs.type);
+ manageSpecsState('specOutline', toggleSpecs.outline);
+ });
+ storageItemChanged('generalExperimental', () => manageSpecs(state));
+}
+
+function manageSpecsState(type, value) {
+ if (state[type] !== value) {
+ // if the current state value doesn't match the new value that means
+ // something changed and we need to update the ui using manageSpecs().
+ state[type] = value;
+ manageSpecs(state);
+ }
+}
+
+function manageSpecs({ specs, specOutline }) {
+ if (specs) {
+ addSpecs();
+ } else {
+ removeSpecs();
+ }
+
+ manageSpecsOutline(specs, specOutline);
+}
+
+function addSpecs() {
+ html.classList.add(specsClass);
+ document.body.addEventListener('mouseover', mouseOver);
+ document.body.addEventListener('mouseout', mouseOut);
+}
+
+function removeSpecs() {
+ html.classList.remove(specsClass);
+ document.body.removeEventListener('mouseover', mouseOver);
+ document.body.removeEventListener('mouseout', mouseOut);
+ cleanUpShadowEvents();
+ removeNoPointers();
+}
+
+function mouseOver(e) {
+ let target = e.srcElement || e;
+
+ setShadowEvents(target);
+
+ if (target && ignoreNodes.indexOf(target.nodeName) === -1) {
+ // ignore certain nodes
+ let targetFound = false;
+ const optionKeys = Object.keys(specToolOptions);
+
+ if (optionKeys.indexOf(state.specType) > -1) {
+ for (let i = 0; i < optionKeys.length; i++) {
+ const option = optionKeys[i];
+ if (state.specType === option) {
+ targetFound = specToolOptions[option](target);
+ break;
+ }
+ }
+
+ injectNoPointer(target);
+
+ if (!targetFound) {
+ mouseOver(getShadowParent(target));
+ } else {
+ document.addEventListener('scroll', clearOnScroll, true);
+ }
+ }
+ }
+}
+
+function injectNoPointer(target) {
+ // This solves a pretty extreme edge case where carbon for ibm.com is setting a pseudo element over
+ // their cards to fix an accessibility problem and in turn blocking our mouse events.
+ // This solution looks for those pseudo elements with an inste of `0px` and sets its pointer
+ // events to none so we can see underneath temporarily.
+ // This seems a little intense to touch the dom this much, but it works for now.
+ const obstruction = window.getComputedStyle(target, ':after');
+
+ if (obstruction.inset !== 'auto' && obstruction.pointerEvents !== 'none') {
+ const className = `${prefix}--no-pointer`;
+ const parent = target.parentNode;
+ let style = parent.querySelector(`style.${className}`);
+
+ if (!style) {
+ style = document.createElement('style');
+ style.classList.add(className);
+ style.innerHTML = `
+ *::after {
+ pointer-events: none;
+ }
+ `;
+
+ parent.prepend(style);
+ _noPointerRef.push(style);
+ }
+ }
+}
+
+function removeNoPointers() {
+ if (_noPointerRef.length) {
+ _noPointerRef.forEach((ref) => {
+ ref.remove();
+ });
+
+ _noPointerRef = [];
+ }
+}
+
+function mouseOut(e, bypass) {
+ if (state.specType === 'spacing') {
+ if (mouseOutSpacing(e) || bypass) {
+ // special condition when spacing is present
+ resetSpecs(e);
+ resetAllSpacers();
+ }
+ } else {
+ resetSpecs(e);
+ }
+}
+
+function clearOnScroll() {
+ resetSpecs();
+ resetAllSpacers();
+}
+
+function resetSpecs() {
+ showHideTooltip(false);
+ removeAllHighlights();
+ document.removeEventListener('scroll', clearOnScroll, true);
+}
+
+// shadow management
+// should these live in utilities?
+function cleanUpShadowEvents() {
+ if (shadowEventCollection.length) {
+ for (let i = 0; i < shadowEventCollection.length; i++) {
+ shadowEventCollection[i].removeEventListener(
+ 'mouseover',
+ shadowMouseOver
+ );
+ shadowEventCollection[i].removeEventListener('mouseout', shadowMouseOut);
+ }
+
+ shadowEventCollection = [];
+ }
+}
+
+function setShadowEvents(target) {
+ // does it have a shadow root?
+ // add an event listener
+ if (target.shadowRoot) {
+ target.dataset.shadowId = randomId();
+ const children = [...target.shadowRoot.children];
+
+ if (children.length) {
+ children.forEach((child) => {
+ child.dataset.shadowParent = target.dataset.shadowId; // skip shadow dom and provide reference to parent
+
+ child.addEventListener('mouseover', shadowMouseOver);
+ child.addEventListener('mouseout', shadowMouseOut);
+
+ shadowEventCollection.push(child);
+ });
+ }
+ }
+}
+
+function shadowMouseOver(e) {
+ let target = e.srcElement || e;
+
+ if (target.dataset.shadowParent) {
+ mouseOut(e, true);
+ }
+
+ mouseOver(target);
+}
+
+function shadowMouseOut(e) {
+ mouseOut(e);
+}
+
+function getShadowParent(target) {
+ let parent = target.parentNode;
+
+ if (target.dataset.shadowParent) {
+ parent = findSingleDomShadow(
+ `[data-shadow-id="${target.dataset.shadowParent}"]`
+ );
+ }
+
+ return parent;
+}
+
+export { initSpecs };
diff --git a/packages/webext/src/inject/components/Specs/index.scss b/packages/webext/src/inject/components/Specs/index.scss
new file mode 100644
index 00000000..e4725fc6
--- /dev/null
+++ b/packages/webext/src/inject/components/Specs/index.scss
@@ -0,0 +1,8 @@
+@use '../../vars.scss' as *;
+$specs-prefix: '#{$prefix}--specs';
+
+@import 'Color';
+@import 'Dependencies';
+@import 'Grid';
+@import 'Outline';
+@import 'Spacing';
diff --git a/packages/webext/src/inject/components/Tooltip/index.js b/packages/webext/src/inject/components/Tooltip/index.js
new file mode 100644
index 00000000..1e32a461
--- /dev/null
+++ b/packages/webext/src/inject/components/Tooltip/index.js
@@ -0,0 +1,221 @@
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { setPx } from '@carbon/devtools-utilities/src/setPx';
+
+const { prefix } = settings;
+
+const body = document.body;
+const tooltipClass = `${prefix}--tooltip`;
+const tooltipShowClass = `${tooltipClass}--shown`;
+const tooltipContentClass = `${tooltipClass}__content`;
+const tooltipCaretClass = `${tooltipClass}__caret`;
+let clearTooltipAnimation,
+ tooltipShowing = false;
+
+function initTooltip() {
+ const tooltip = body.querySelector('.' + tooltipClass);
+ const devtoolsContainer = body.querySelector(`.${prefix}--devtools`);
+
+ if (!tooltip) {
+ const tooltipHTML = document.createElement('div');
+ tooltipHTML.classList.add(tooltipClass);
+ tooltipHTML.setAttribute('data-floating-menu-direction', 'top');
+ tooltipHTML.innerHTML = `
+
+
+ `;
+ devtoolsContainer.appendChild(tooltipHTML);
+ }
+}
+
+function updateTooltipContent(content) {
+ const tooltipContent = body.querySelector('.' + tooltipContentClass);
+
+ tooltipContent.innerHTML = content;
+}
+
+function positionTooltip(component) {
+ const boundingBox = component.getBoundingClientRect();
+ const tooltip = body.querySelector('.' + tooltipClass);
+ const tooltipCaret = tooltip.querySelector('.' + tooltipCaretClass);
+
+ tooltip.style.left = tooltipOffsetX(boundingBox, tooltip, component);
+ tooltip.style.top = tooltipOffsetY(boundingBox, tooltip, tooltipCaret);
+ offsetCaretX(boundingBox, tooltip, tooltipCaret, component);
+
+ if (tooltipShowing) {
+ // don't run if tooltip is hidden
+ clearTooltipAnimation = window.requestAnimationFrame(() =>
+ positionTooltip(component)
+ );
+ }
+}
+
+function offsetCaretX(boundingBox, tooltip, tooltipCaret) {
+ // reset carot styles
+ tooltipCaret.style.left = null;
+ tooltipCaret.style.margin = null;
+
+ // reposition caret over item if too small
+ if (tooltip.offsetWidth > boundingBox.width) {
+ let carotOffset;
+
+ if (boundingBox.x + tooltip.offsetWidth >= window.innerWidth) {
+ // aligned right
+ // half of component width + component right + half of carot width
+ carotOffset =
+ boundingBox.width / 2 +
+ (window.innerWidth - boundingBox.right) +
+ tooltipCaret.offsetWidth / 2;
+ tooltipCaret.style.left = `calc(100% - ${carotOffset}px)`;
+ tooltipCaret.style.margin = 0;
+ } else if (boundingBox.right < tooltip.offsetWidth) {
+ // aligned left
+ // component left + half of component width - half of tooltip carot width
+ carotOffset =
+ boundingBox.x + boundingBox.width / 2 - tooltipCaret.offsetWidth / 2;
+ tooltipCaret.style.left = setPx(carotOffset);
+ tooltipCaret.style.margin = 0;
+ }
+ }
+}
+
+function flipTooltip(tooltip, direction) {
+ tooltip.setAttribute('data-floating-menu-direction', direction);
+}
+
+function tooltipOffsetY(comp, tooltip, tooltipCaret) {
+ const compBottom = comp.bottom;
+ const compTop = comp.y;
+ const tooltipHeight = tooltip.offsetHeight;
+ const windowHeight = window.innerHeight;
+ const caretHeight = tooltipCaret.offsetHeight;
+
+ let offsetValue = caretHeight; // 3. fix to top by default
+ let caretDirection = 'top';
+
+ // 1. room on top?
+ if (compTop >= tooltipHeight) {
+ // // 1a. below the screen?
+ if (compTop > windowHeight) {
+ offsetValue = windowHeight - tooltipHeight; // fix to bottom of screen
+ } else {
+ offsetValue = compTop - tooltipHeight; // fix to top of comp
+ }
+
+ // 2. room on bottom?
+ } else if (compBottom + tooltipHeight <= windowHeight) {
+ // // 2a. above the screen?
+ if (compBottom > caretHeight) {
+ offsetValue = compBottom + caretHeight; // fix to bottom of comp
+ }
+
+ caretDirection = 'bottom';
+ }
+
+ flipTooltip(tooltip, caretDirection);
+ return setPx(offsetValue);
+}
+
+function tooltipOffsetX({ x, width }, tooltip) {
+ let offsetValue = x;
+
+ // offset whole tooltip and ensure it stays within viewport
+ if (x + tooltip.offsetWidth > window.innerWidth) {
+ // right
+ // make sure the tooltip doesn't go beyond the right edge of screen
+ offsetValue = window.innerWidth - tooltip.offsetWidth;
+ } else if (tooltip.offsetWidth > width) {
+ // if component is smaller than tooltip center tooltip over it
+ offsetValue = x - tooltip.offsetWidth / 2 + width / 2;
+
+ if (offsetValue < 0) {
+ // set to zero if we over compinsated so it doesn't fall off the edge
+ offsetValue = 0;
+ }
+ }
+
+ return setPx(offsetValue);
+}
+
+function showHideTooltip(show) {
+ const tooltip = body.querySelector('.' + tooltipClass);
+
+ if (show) {
+ tooltipShowing = true;
+ tooltip.classList.add(tooltipShowClass);
+ } else {
+ tooltipShowing = false;
+ cancelAnimationFrame(clearTooltipAnimation);
+ tooltip.classList.remove(tooltipShowClass);
+ }
+}
+
+function __specValueItem(type, value) {
+ let html;
+
+ if (type === 'warning') {
+ html = `
+
+ ${value}
+
+ `;
+ } else {
+ html = ``;
+
+ if (type) {
+ html += ``;
+ }
+
+ if (value) {
+ html += `${value}
`;
+ }
+
+ html += ` `;
+ }
+
+ return html;
+}
+
+function __specsContainer(groups) {
+ let groupsContent = '';
+ let groupLayoutClass = '';
+
+ if (groups.length === 1) {
+ groupLayoutClass = `${prefix}--tooltip-specs__group--single`;
+ } else if (groups.length === 2) {
+ groupLayoutClass = `${prefix}--tooltip-specs__group--duo`;
+ }
+
+ groups.forEach(({ eyebrow, title, content }) => {
+ groupsContent += ``;
+
+ if (eyebrow) {
+ groupsContent += ``;
+ }
+
+ if (title) {
+ groupsContent += ``;
+ }
+
+ if (content) {
+ groupsContent += content;
+ }
+
+ groupsContent += `
`;
+ });
+
+ return `
+
+ ${groupsContent}
+
+ `;
+}
+
+export {
+ initTooltip,
+ positionTooltip,
+ showHideTooltip,
+ updateTooltipContent,
+ __specValueItem,
+ __specsContainer,
+};
diff --git a/packages/webext/src/inject/components/Tooltip/index.scss b/packages/webext/src/inject/components/Tooltip/index.scss
new file mode 100644
index 00000000..857146a8
--- /dev/null
+++ b/packages/webext/src/inject/components/Tooltip/index.scss
@@ -0,0 +1,167 @@
+@use 'sass:map';
+
+@use '../../vars.scss' as *;
+@use '@carbon/react/scss/breakpoint';
+@use '@carbon/react/scss/colors';
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+@use '@carbon/react/scss/components/tooltip';
+
+.#{$prefix}--tooltip {
+ @include type.type-style('helper-text-01');
+
+ position: fixed;
+ z-index: $zindex;
+ display: block;
+ min-width: initial;
+ max-width: 100vw;
+ padding: spacing.$spacing-05;
+ margin-top: 0;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2),
+ inset 0 0 0 1px rgba(255, 255, 255, 0.1);
+ opacity: 0;
+ transition: opacity $transition-fade, visibility $transition-fade;
+ visibility: hidden;
+
+ &[data-floating-menu-direction='top'] {
+ margin-top: calc(-1 * #{spacing.$spacing-03});
+ }
+
+ &__content {
+ display: flex;
+ width: 100%;
+ }
+
+ &--shown {
+ opacity: 1;
+ visibility: visible;
+ }
+
+ &--primary {
+ margin-right: spacing.$spacing-05;
+ font-weight: bold;
+ }
+
+ // spec detail groups
+ &-specs {
+ display: flex;
+ flex-wrap: wrap;
+
+ &__eyebrow {
+ @include type.type-style('label-01');
+ }
+
+ &__title {
+ @include type.type-style('heading-compact-01');
+
+ margin-right: 8px;
+ margin-bottom: calc(#{spacing.$spacing-03} * 1);
+ float: left;
+ }
+
+ &__group {
+ width: 50%;
+ min-width: calc(#{spacing.$spacing-03} * 30);
+ padding: calc(#{spacing.$spacing-03} * 1);
+ padding-right: calc(#{spacing.$spacing-03} * 2);
+
+ @include breakpoint.breakpoint('md') {
+ width: 25%;
+ }
+
+ &--single {
+ width: 100%;
+ }
+
+ &--duo {
+ width: 50%;
+ }
+
+ ul {
+ padding: 0;
+ margin: 0;
+ clear: both;
+ list-style-type: none;
+ }
+
+ li {
+ display: flex;
+ padding: 0;
+ margin: 0;
+ }
+
+ li::before {
+ display: none;
+ }
+
+ li:last-of-type {
+ margin-bottom: 0;
+ }
+ }
+
+ &__prop {
+ @include type.type-style('code-01');
+
+ margin-top: calc(#{spacing.$spacing-03} * 0.25);
+ margin-right: calc(#{spacing.$spacing-03} * 1);
+ font-weight: 500;
+ }
+
+ p#{&}__value {
+ @include type.type-style('code-01');
+
+ margin-top: calc(#{spacing.$spacing-03} * 0.25);
+ }
+
+ li#{&}__warning {
+ @include type.type-style('helper-text-01');
+ $yellow: colors.$yellow-30;
+
+ padding-left: calc(#{spacing.$spacing-03} * 1);
+ border-left: 3px solid $yellow;
+ margin-bottom: calc(#{spacing.$spacing-03} * 1);
+ font-weight: 500;
+ }
+ }
+
+ // tooltip theme
+ @mixin tooltip--theme {
+ background: theme.$layer-01;
+ color: theme.$text-primary;
+
+ .#{$prefix}--tooltip__caret {
+ border-bottom-color: theme.$layer-01;
+ }
+
+ &--secondary {
+ color: theme.$text-secondary;
+ }
+
+ &-specs {
+ &__group {
+ border-right: grid.$grid-gutter-condensed solid theme.$layer-01;
+ border-bottom: grid.$grid-gutter-condensed solid theme.$layer-01;
+ background-color: theme.$background;
+ }
+
+ &__warning {
+ background-color: theme.$layer-01;
+ color: theme.$text-primary;
+ }
+
+ &__title,
+ &__prop {
+ color: theme.$text-primary;
+ }
+
+ &__name,
+ &__value {
+ color: theme.$text-secondary;
+ }
+ }
+ }
+
+ @include tooltip--theme;
+}
diff --git a/packages/webext/src/inject/components/index.js b/packages/webext/src/inject/components/index.js
new file mode 100644
index 00000000..e2b68dc1
--- /dev/null
+++ b/packages/webext/src/inject/components/index.js
@@ -0,0 +1,7 @@
+export * from './Grid';
+export * from './Inventory';
+export * from './Specs';
+export * from './Tooltip';
+export * from './Highlight';
+export * from './Shortcuts';
+export * from './BreakpointLabel';
diff --git a/packages/webext/src/inject/index.js b/packages/webext/src/inject/index.js
new file mode 100644
index 00000000..5134842b
--- /dev/null
+++ b/packages/webext/src/inject/index.js
@@ -0,0 +1,24 @@
+import './index.scss';
+import { sendMessage } from '@carbon/devtools-utilities/src/sendMessage';
+import {
+ initGrid,
+ initSpecs,
+ initInventory,
+ initTooltip,
+ initShortcuts,
+ injectHighlights,
+ initBreakpointLabel,
+} from './components';
+
+if (!self.carbonDevtoolsInjected) {
+ injectHighlights();
+ initGrid();
+ initSpecs();
+ initTooltip();
+ initInventory();
+ initShortcuts();
+ initBreakpointLabel();
+
+ self.carbonDevtoolsInjected = true;
+ sendMessage({ carbonDevtoolsInjected: true });
+}
diff --git a/packages/webext/src/inject/index.scss b/packages/webext/src/inject/index.scss
new file mode 100644
index 00000000..7a5d5081
--- /dev/null
+++ b/packages/webext/src/inject/index.scss
@@ -0,0 +1,44 @@
+@use 'sass:map';
+
+@use './vars.scss' as *;
+@use '@carbon/react/scss/colors';
+@use '@carbon/react/scss/breakpoint';
+@use '@carbon/react/scss/fonts';
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/motion';
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/themes';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+
+@use 'components/Tooltip/index.scss' as *;
+@use 'components/Highlight/index.scss' as *;
+@use 'components/Grid/index.scss' as *;
+@use 'components/Specs/index.scss' as *;
+@use 'components/Specs/Color/index.scss' as *;
+@use 'components/Specs/Dependencies/index.scss' as *;
+@use 'components/Specs/Grid/index.scss' as *;
+@use 'components/Specs/Outline/index.scss' as *;
+@use 'components/Specs/Spacing/index.scss' as *;
+@use 'components/BreakpointLabel/index.scss' as *;
+
+$themes: (
+ 'white': themes.$white,
+ 'g10': themes.$g10,
+ 'g80': themes.$g90,
+ 'g100': themes.$g100,
+);
+
+.#{$prefix}--devtools {
+ @include theme.theme(themes.$g90);
+
+ @media (prefers-color-scheme: light) {
+ @include theme.theme(themes.$white);
+ }
+
+ @each $theme in map-keys($themes) {
+ .#{$prefix}--#{$theme} & {
+ @include theme.theme(map.get($themes, $theme));
+ }
+ }
+}
diff --git a/packages/webext/src/inject/vars.scss b/packages/webext/src/inject/vars.scss
new file mode 100644
index 00000000..f315f42e
--- /dev/null
+++ b/packages/webext/src/inject/vars.scss
@@ -0,0 +1,37 @@
+@use '@carbon/react/scss/config';
+@use '@carbon/react/scss/motion';
+@use '@carbon/react/scss/colors';
+
+$prefix: '#{config.$prefix}-dev';
+$carbonPrefix: config.$prefix;
+$specs-prefix: '#{$prefix}--specs';
+$zindex: 9999999999;
+
+$transition-in: motion.$duration-moderate-02 motion.motion(standard, productive);
+$transition-fade: motion.$duration-fast-02 motion.motion(standard, productive);
+$grid-color: colors.$blue-60;
+$inventory-color: colors.$purple-50;
+$specs-color: colors.$magenta-50;
+
+$grid-opacity: 0.5;
+$grid-border-size: 1px;
+$grid-border-color: $grid-color;
+$grid-border-opacity: 1;
+
+$grid-inner-color: $grid-color;
+$grid-inner-opacity: $grid-opacity;
+$grid-inner-border-size: $grid-border-size;
+$grid-inner-border-color: $grid-border-color;
+$grid-inner-border-opacity: $grid-border-opacity;
+
+$grid-outer-color: $grid-color;
+$grid-outer-opacity: $grid-inner-opacity / 2;
+$grid-outer-border-size: $grid-border-size;
+$grid-outer-border-color: $grid-border-color;
+$grid-outer-border-opacity: $grid-border-opacity;
+
+$grid-mini-unit-color: $grid-color;
+$grid-mini-unit-opacity: $grid-opacity;
+$grid-mini-unit-border-size: $grid-border-size;
+$grid-mini-unit-border-color: $grid-border-color;
+$grid-mini-unit-border-opacity: $grid-opacity;
diff --git a/packages/webext/src/manifest.json b/packages/webext/src/manifest.json
new file mode 100644
index 00000000..5dd5d7e6
--- /dev/null
+++ b/packages/webext/src/manifest.json
@@ -0,0 +1,33 @@
+{
+ "manifest_version": 3,
+ "icons": {
+ "16": "icon/16.png",
+ "32": "icon/32.png",
+ "48": "icon/48.png",
+ "96": "icon/96.png",
+ "128": "icon/128.png"
+ },
+ "permissions": [
+ "activeTab",
+ "storage",
+ "scripting"
+ ],
+ "action": {
+ "default_popup": "src/popup/index.html"
+ },
+ "background": {
+ "{{chrome}}.service_worker": "src/background/index.js",
+ "{{firefox}}.scripts": ["src/background/index.js"],
+ "type": "module"
+ },
+ "host_permissions": [
+ "*://*/*"
+ ],
+ "{{chrome}}.minimum_chrome_version": "121",
+ "{{firefox}}.browser_specific_settings": {
+ "gecko": {
+ "strict_min_version": "121.0",
+ "id": "{a12521e6-1ba2-4d48-9ec2-9286a37d82d1}"
+ }
+ }
+}
diff --git a/packages/webext/src/popup/app.jsx b/packages/webext/src/popup/app.jsx
new file mode 100644
index 00000000..f00263e3
--- /dev/null
+++ b/packages/webext/src/popup/app.jsx
@@ -0,0 +1,57 @@
+import React, { useEffect } from "react";
+import ReactDOM from "react-dom/client";
+import MoreOptions from "./components/MoreOptions";
+
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { getMessage } from '@carbon/devtools-utilities/src/getMessage';
+import {
+ sendMessage,
+} from '@carbon/devtools-utilities/src/sendMessage';
+
+const { prefix } = settings;
+
+function Popup() {
+
+ useEffect(() => {
+ sendMessage({ popup: true });
+ console.log('Popup opened!');
+
+ getMessage((msg) => {
+ console.log("Message caught in popup: ", msg);
+
+ if (msg.carbonDevtoolsInjected) {
+ console.log("Carbon devtools injected!");
+ }
+
+ if (msg.runningCarbon) {
+ console.log("Page is running carbon!");
+ } else if (
+ Boolean(msg.runningCarbon) === false
+ ) {
+ console.log("Page is not running carbon.");
+ }
+ });
+ }, []);
+
+
+
+ return (
+ <>
+
+
+ devtools go here.
+
+ >
+ )
+}
+
+const appRoot = document.querySelector('#popup');
+
+ReactDOM.createRoot(appRoot).render(
+
+
+
+);
diff --git a/packages/webext/src/popup/components/MoreOptions/index.jsx b/packages/webext/src/popup/components/MoreOptions/index.jsx
new file mode 100644
index 00000000..55e161b8
--- /dev/null
+++ b/packages/webext/src/popup/components/MoreOptions/index.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import * as settings from '@carbon/web-components/es/globals/settings.js';
+import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
+import { openChromeExtensionOptions } from '@carbon/devtools-utilities/src/openChromeExtensionOptions';
+import packageJSON from '../../../../../../package.json';
+
+const { prefix } = settings;
+
+export default function() {
+ return (
+ {
+ setTimeout(() => {
+ menuBody.style.left = 'auto';
+ menuBody.style.right = 0;
+ }, 0);
+ }}
+ ariaLabel="More options">
+
+
+
+
+ );
+}
diff --git a/packages/webext/src/popup/components/MoreOptions/index.scss b/packages/webext/src/popup/components/MoreOptions/index.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/packages/webext/src/popup/index.html b/packages/webext/src/popup/index.html
new file mode 100644
index 00000000..d56272e0
--- /dev/null
+++ b/packages/webext/src/popup/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+ Popup
+
+
+
+
+
+
+
+
diff --git a/packages/webext/src/popup/index.scss b/packages/webext/src/popup/index.scss
new file mode 100644
index 00000000..2b36b4cc
--- /dev/null
+++ b/packages/webext/src/popup/index.scss
@@ -0,0 +1,67 @@
+/* stylelint-disable a11y/media-prefers-reduced-motion */
+@use 'sass:map';
+
+// Import Carbon Tokens
+@use '@carbon/react/scss/config' as *;
+@use '@carbon/react/scss/breakpoint';
+@use '@carbon/react/scss/fonts';
+@use '@carbon/react/scss/grid';
+@use '@carbon/react/scss/motion';
+@use '@carbon/react/scss/spacing';
+@use '@carbon/react/scss/themes';
+@use '@carbon/react/scss/theme';
+@use '@carbon/react/scss/type';
+
+// Import Carbon Components
+@use '@carbon/react/scss/components/overflow-menu' as *;
+@use '@carbon/react/scss/components/toggle' as *;
+@use '@carbon/react/scss/components/accordion' as *;
+@use '@carbon/react/scss/components/skeleton-styles' as *;
+@use '@carbon/react/scss/components/link' as *;
+@use '@carbon/react/scss/components/checkbox' as *;
+@use '@carbon/react/scss/components/number-input' as *;
+@use '@carbon/react/scss/components/form' as *;
+@use '@carbon/react/scss/components/tooltip' as *;
+@use '@carbon/react/scss/components/tag' as *;
+@use '@carbon/react/scss/components/tile' as *;
+@use '@carbon/react/scss/components/content-switcher' as *;
+@use '@carbon/react/scss/components/button' as *;
+@use '@carbon/react/scss/components/search' as *;
+@use '@carbon/react/scss/components/code-snippet' as *;
+@use '@carbon/react/scss/components/notification' as *;
+
+:root {
+ @include theme.theme(themes.$g90);
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ @include theme.theme(themes.$white);
+ }
+}
+
+body {
+ background: theme.$background;
+ color: theme.$text-primary;
+ margin: 0;
+ padding: 0;
+ width: map.get(map.get(grid.$grid-breakpoints, 'sm'), 'width');
+}
+
+header {
+ background-color: theme.$layer-01;
+ display: flex;
+ justify-content: space-between;
+ gap: spacing.$spacing-03;
+
+ h1 {
+ @include type.type-style('productive-heading-02');
+
+ margin: 0;
+ margin: spacing.$spacing-03
+ }
+}
+
+main {
+ min-height: map.get(map.get(grid.$grid-breakpoints, 'sm'), 'width');
+}
diff --git a/packages/webext/src/validate/index.js b/packages/webext/src/validate/index.js
new file mode 100644
index 00000000..9456e6b7
--- /dev/null
+++ b/packages/webext/src/validate/index.js
@@ -0,0 +1,65 @@
+import { sendMessage } from '@carbon/devtools-utilities/src/sendMessage';
+import { getStorage } from '@carbon/devtools-utilities/src/getStorage';
+import { prefixSelectors } from '../globals/prefixSelectors';
+import { bxDevGetLibraries } from "./validationScript";
+
+bxDevGetLibraries();
+sendValidation();
+console.log('validation completed.');
+
+function sendValidation() {
+ const carbonComponents = document.querySelector(prefixSelectors);
+ const html = document.querySelector('html');
+ const body = document.querySelector('body');
+ const title = document.querySelector('title');
+ const description = document.querySelector('meta[name="description"]');
+ const keywords = document.querySelector('meta[name="keywords"]');
+
+ const pageInfo = {
+ // do as much as we can here since this code is already injected, and to avoid larger stringifys in dataset attributes.
+ title: title && title.innerText,
+ description: description && description.getAttribute('content'),
+ keywords:
+ keywords &&
+ keywords
+ .getAttribute('content')
+ .split(',')
+ .map((keyword) => keyword.trim())
+ .filter((k) => k),
+ location: {
+ domain: window.location.domain,
+ host: window.location.host,
+ hostname: window.location.hostname,
+ href: window.location.href,
+ origin: window.location.origin,
+ pathname: window.location.pathname,
+ port: window.location.port,
+ protocol: window.location.protocol,
+ search: window.location.search,
+ hash: window.location.hash,
+ },
+ language: html.getAttribute('lang'),
+ };
+
+ const msg = {
+ windowWidth: window.outerWidth,
+ carbonDevtoolsInjected: window.carbonDevtoolsInjected || false,
+ pageInfo: {
+ ...pageInfo,
+ ...JSON.parse(body.dataset.pageInfo),
+ },
+ };
+
+ delete body.dataset.pageInfo;
+
+ getStorage(['generalNonCarbon'], ({ generalNonCarbon }) => {
+ // at least components on page
+ // or user chooses to ignore carbon validation
+ if (generalNonCarbon || carbonComponents) {
+ msg.runningCarbon = true;
+ msg.ignoreValidation = generalNonCarbon;
+ }
+
+ sendMessage(msg);
+ });
+}
diff --git a/packages/webext/src/validate/validationScript.js b/packages/webext/src/validate/validationScript.js
new file mode 100644
index 00000000..c6a46b7d
--- /dev/null
+++ b/packages/webext/src/validate/validationScript.js
@@ -0,0 +1,114 @@
+bxDevGetLibraries();
+
+function bxDevGetLibraries() {
+ const libraries = {
+ 'IBM Analytics':
+ window._ibmAnalytics ||
+ document.querySelector('script[src*="common/stats/ibm-common.js"]') ||
+ undefined,
+ Kaltura: (window.kWidget || window.KWidget) && true,
+ w3DS: window.w3ds && true,
+ React: window.React,
+ Angular: window.angular,
+ jQuery: window.jQuery || window.$,
+ Dojo: window.dojo,
+ Bootstrap: window.bootstrap,
+ Vue: window.Vue,
+ };
+
+ const pageInfo = {
+ digitalData: window.digitalData,
+ libraries,
+ };
+
+ if (libraries.jQuery) {
+ // try to get version number
+ try {
+ // let's try to see if there are more than one version lurking in the shadows
+ const versions = [];
+ Object.keys(window).forEach((key) => {
+ if (key.indexOf('jQuery') === 0 || key.indexOf('$') === 0) {
+ if (typeof window[key] === 'function') {
+ const version = window[key]().jquery;
+ if (version) {
+ versions.push(version);
+ }
+ }
+ }
+ });
+
+ libraries.jQuery = versions.length ? versions : true;
+ } catch (_e) {
+ libraries.jQuery = libraries.jQuery().jquery || true;
+ }
+ }
+
+ if (libraries.Dojo) {
+ // try to get version number
+ libraries.Dojo =
+ (libraries.Dojo.version && libraries.Dojo.version.toString()) || true;
+ }
+
+ if (libraries.Bootstrap) {
+ // try to get version number
+ libraries.Bootstrap =
+ (libraries.Bootstrap.Alert && libraries.Bootstrap.Alert.VERSION) || true;
+ }
+
+ if (libraries.Vue) {
+ // try to get version number
+ libraries.Vue = (libraries.Vue && libraries.Vue.version) || true;
+ }
+
+ if (libraries.React) {
+ // try to get version number
+ libraries.React = libraries.React.version || true;
+ } else {
+ libraries.React = findReactRoot();
+ }
+
+ if (libraries.Angular) {
+ libraries.Angular =
+ (window.angular.version && window.angular.version.full) || true;
+ } else {
+ const angularRoot =
+ window.getAllAngularRootElements && window.getAllAngularRootElements();
+
+ if (angularRoot) {
+ if (angularRoot.length) {
+ libraries.Angular = angularRoot[0].getAttribute('ng-version') || true;
+ } else {
+ libraries.Angular = true;
+ }
+ }
+ }
+
+ if (libraries['IBM Analytics']) {
+ const analytics = libraries['IBM Analytics'];
+ if (analytics.ver) {
+ libraries['IBM Analytics'] = analytics.ver.libraryVersion || true;
+ } else {
+ libraries['IBM Analytics'] = true;
+ }
+ }
+ document.body.dataset.pageInfo = JSON.stringify(pageInfo);
+}
+
+function findReactRoot(elem) {
+ elem = elem || document.querySelector('body');
+ const children = elem.children;
+ let result;
+
+ for (let i = 0; i < children.length; i++) {
+ if (children[i]._reactRootContainer) {
+ result = true;
+ break;
+ } else {
+ findReactRoot(children[i]);
+ }
+ }
+
+ return result;
+}
+
+export { bxDevGetLibraries }
diff --git a/packages/webext/vite.config.js b/packages/webext/vite.config.js
new file mode 100644
index 00000000..a083c8e8
--- /dev/null
+++ b/packages/webext/vite.config.js
@@ -0,0 +1,33 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import webExtension, { readJsonFile } from "vite-plugin-web-extension";
+
+function generateManifest() {
+ const manifest = readJsonFile("src/manifest.json");
+ const pkg = readJsonFile("package.json");
+ return {
+ name: pkg.name,
+ description: pkg.description,
+ version: pkg.version,
+ ...manifest,
+ };
+}
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ react(),
+ webExtension({
+ manifest: generateManifest,
+ additionalInputs: [
+ "src/validate/index.js",
+ "src/validate/validationScript.js",
+ "src/inject/index.js",
+ "src/inject/index.scss"
+ ],
+ webExtConfig: {
+ startUrl: "https://carbondesignsystem.com/"
+ }
+ }),
+ ],
+});
diff --git a/yarn.lock b/yarn.lock
index 2e75d512..3bf39f64 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -88,7 +88,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/core@npm:^7.25.2":
+"@babel/core@npm:^7.24.5, @babel/core@npm:^7.25.2":
version: 7.25.2
resolution: "@babel/core@npm:7.25.2"
dependencies:
@@ -886,6 +886,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7":
+ version: 7.25.6
+ resolution: "@babel/parser@npm:7.25.6"
+ dependencies:
+ "@babel/types": "npm:^7.25.6"
+ bin:
+ parser: ./bin/babel-parser.js
+ checksum: 10c0/f88a0e895dbb096fd37c4527ea97d12b5fc013720602580a941ac3a339698872f0c911e318c292b184c36b5fbe23b612f05aff9d24071bc847c7b1c21552c41d
+ languageName: node
+ linkType: hard
+
"@babel/parser@npm:^7.15.4":
version: 7.15.7
resolution: "@babel/parser@npm:7.15.7"
@@ -2258,6 +2269,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/plugin-transform-react-jsx-self@npm:^7.24.5":
+ version: 7.24.7
+ resolution: "@babel/plugin-transform-react-jsx-self@npm:7.24.7"
+ dependencies:
+ "@babel/helper-plugin-utils": "npm:^7.24.7"
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+ checksum: 10c0/dcf3b732401f47f06bb29d6016e48066f66de00029a0ded98ddd9983c770a00a109d91cd04d2700d15ee0bcec3ae3027a5f12d69e15ec56efc0bcbfac65e92cb
+ languageName: node
+ linkType: hard
+
"@babel/plugin-transform-react-jsx-source@npm:7.0.0-rc.0":
version: 7.0.0-rc.0
resolution: "@babel/plugin-transform-react-jsx-source@npm:7.0.0-rc.0"
@@ -2270,6 +2292,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/plugin-transform-react-jsx-source@npm:^7.24.1":
+ version: 7.24.7
+ resolution: "@babel/plugin-transform-react-jsx-source@npm:7.24.7"
+ dependencies:
+ "@babel/helper-plugin-utils": "npm:^7.24.7"
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+ checksum: 10c0/970ef1264c7c6c416ab11610665d5309aec2bd2b9086ae394e1132e65138d97b060a7dc9d31054e050d6dc475b5a213938c9707c0202a5022d55dcb4c5abe28f
+ languageName: node
+ linkType: hard
+
"@babel/plugin-transform-react-jsx@npm:7.0.0-rc.0":
version: 7.0.0-rc.0
resolution: "@babel/plugin-transform-react-jsx@npm:7.0.0-rc.0"
@@ -2790,7 +2823,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.7":
+"@babel/runtime@npm:7.25.6, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.7":
version: 7.25.6
resolution: "@babel/runtime@npm:7.25.6"
dependencies:
@@ -2902,6 +2935,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.6":
+ version: 7.25.6
+ resolution: "@babel/types@npm:7.25.6"
+ dependencies:
+ "@babel/helper-string-parser": "npm:^7.24.8"
+ "@babel/helper-validator-identifier": "npm:^7.24.7"
+ to-fast-properties: "npm:^2.0.0"
+ checksum: 10c0/89d45fbee24e27a05dca2d08300a26b905bd384a480448823f6723c72d3a30327c517476389b7280ce8cb9a2c48ef8f47da7f9f6d326faf6f53fd6b68237bdc4
+ languageName: node
+ linkType: hard
+
"@babel/types@npm:^7.14.9, @babel/types@npm:^7.15.4, @babel/types@npm:^7.15.6, @babel/types@npm:^7.4.4":
version: 7.15.6
resolution: "@babel/types@npm:7.15.6"
@@ -3756,6 +3800,30 @@ __metadata:
languageName: node
linkType: hard
+"@crxjs/vite-plugin@npm:^2.0.0-beta.25":
+ version: 2.0.0-beta.25
+ resolution: "@crxjs/vite-plugin@npm:2.0.0-beta.25"
+ dependencies:
+ "@rollup/pluginutils": "npm:^4.1.2"
+ "@webcomponents/custom-elements": "npm:^1.5.0"
+ acorn-walk: "npm:^8.2.0"
+ cheerio: "npm:^1.0.0-rc.10"
+ connect-injector: "npm:^0.4.4"
+ convert-source-map: "npm:^1.7.0"
+ debug: "npm:^4.3.3"
+ es-module-lexer: "npm:^0.10.0"
+ fast-glob: "npm:^3.2.11"
+ fs-extra: "npm:^10.0.1"
+ jsesc: "npm:^3.0.2"
+ magic-string: "npm:^0.26.0"
+ picocolors: "npm:^1.0.0"
+ react-refresh: "npm:^0.13.0"
+ rollup: "npm:2.78.1"
+ rxjs: "npm:7.5.7"
+ checksum: 10c0/58147a0c1a7fb6bff02087d8e013ec9a0b233843c7a67328f43a64a2a20a2d9567fc3e454c454294535d68410cd70b3d94270c2d8f27bebb11c92f3e055c854b
+ languageName: node
+ linkType: hard
+
"@csstools/cascade-layer-name-parser@npm:^2.0.0":
version: 2.0.0
resolution: "@csstools/cascade-layer-name-parser@npm:2.0.0"
@@ -4417,6 +4485,167 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/aix-ppc64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/aix-ppc64@npm:0.21.5"
+ conditions: os=aix & cpu=ppc64
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-arm64@npm:0.21.5"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-arm@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-arm@npm:0.21.5"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-x64@npm:0.21.5"
+ conditions: os=android & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/darwin-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/darwin-arm64@npm:0.21.5"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/darwin-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/darwin-x64@npm:0.21.5"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/freebsd-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/freebsd-arm64@npm:0.21.5"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/freebsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/freebsd-x64@npm:0.21.5"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-arm64@npm:0.21.5"
+ conditions: os=linux & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-arm@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-arm@npm:0.21.5"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-ia32@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-ia32@npm:0.21.5"
+ conditions: os=linux & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-loong64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-loong64@npm:0.21.5"
+ conditions: os=linux & cpu=loong64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-mips64el@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-mips64el@npm:0.21.5"
+ conditions: os=linux & cpu=mips64el
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-ppc64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-ppc64@npm:0.21.5"
+ conditions: os=linux & cpu=ppc64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-riscv64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-riscv64@npm:0.21.5"
+ conditions: os=linux & cpu=riscv64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-s390x@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-s390x@npm:0.21.5"
+ conditions: os=linux & cpu=s390x
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-x64@npm:0.21.5"
+ conditions: os=linux & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/netbsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/netbsd-x64@npm:0.21.5"
+ conditions: os=netbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openbsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/openbsd-x64@npm:0.21.5"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/sunos-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/sunos-x64@npm:0.21.5"
+ conditions: os=sunos & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-arm64@npm:0.21.5"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-ia32@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-ia32@npm:0.21.5"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-x64@npm:0.21.5"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@eslint-community/eslint-utils@npm:^4.2.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -4494,6 +4723,13 @@ __metadata:
languageName: node
linkType: hard
+"@eslint/js@npm:8.57.1":
+ version: 8.57.1
+ resolution: "@eslint/js@npm:8.57.1"
+ checksum: 10c0/b489c474a3b5b54381c62e82b3f7f65f4b8a5eaaed126546520bf2fede5532a8ed53212919fed1e9048dcf7f37167c8561d58d0ba4492a4244004e7793805223
+ languageName: node
+ linkType: hard
+
"@eslint/js@npm:9.9.0":
version: 9.9.0
resolution: "@eslint/js@npm:9.9.0"
@@ -4501,6 +4737,13 @@ __metadata:
languageName: node
linkType: hard
+"@eslint/js@npm:^9.9.0":
+ version: 9.10.0
+ resolution: "@eslint/js@npm:9.10.0"
+ checksum: 10c0/2ac45a002dc1ccf25be46ea61001ada8d77248d1313ab4e53f3735e5ae00738a757874e41f62ad6fbd49df7dffeece66e5f53ff0d7b78a99ce4c68e8fea66753
+ languageName: node
+ linkType: hard
+
"@eslint/object-schema@npm:^2.1.4":
version: 2.1.4
resolution: "@eslint/object-schema@npm:2.1.4"
@@ -4578,6 +4821,17 @@ __metadata:
languageName: node
linkType: hard
+"@humanwhocodes/config-array@npm:^0.13.0":
+ version: 0.13.0
+ resolution: "@humanwhocodes/config-array@npm:0.13.0"
+ dependencies:
+ "@humanwhocodes/object-schema": "npm:^2.0.3"
+ debug: "npm:^4.3.1"
+ minimatch: "npm:^3.0.5"
+ checksum: 10c0/205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/module-importer@npm:^1.0.1":
version: 1.0.1
resolution: "@humanwhocodes/module-importer@npm:1.0.1"
@@ -4585,7 +4839,7 @@ __metadata:
languageName: node
linkType: hard
-"@humanwhocodes/object-schema@npm:^2.0.2":
+"@humanwhocodes/object-schema@npm:^2.0.2, @humanwhocodes/object-schema@npm:^2.0.3":
version: 2.0.3
resolution: "@humanwhocodes/object-schema@npm:2.0.3"
checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c
@@ -4852,6 +5106,13 @@ __metadata:
languageName: node
linkType: hard
+"@mdn/browser-compat-data@npm:5.6.0":
+ version: 5.6.0
+ resolution: "@mdn/browser-compat-data@npm:5.6.0"
+ checksum: 10c0/7a694b5784243bd1ac6f214acd7f09400a54440bac56b487b6ec3c11e08266173ea32621f5c8160fc45f159ada691ac5ecbeb5ac43b6bad5b3cf691cf63f5bf0
+ languageName: node
+ linkType: hard
+
"@motionone/animation@npm:^10.12.0":
version: 10.18.0
resolution: "@motionone/animation@npm:10.18.0"
@@ -5484,6 +5745,128 @@ __metadata:
languageName: node
linkType: hard
+"@rollup/pluginutils@npm:^4.1.2":
+ version: 4.2.1
+ resolution: "@rollup/pluginutils@npm:4.2.1"
+ dependencies:
+ estree-walker: "npm:^2.0.1"
+ picomatch: "npm:^2.2.2"
+ checksum: 10c0/3ee56b2c8f1ed8dfd0a92631da1af3a2dfdd0321948f089b3752b4de1b54dc5076701eadd0e5fc18bd191b77af594ac1db6279e83951238ba16bf8a414c64c48
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-android-arm-eabi@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.3"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-android-arm64@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-android-arm64@npm:4.21.3"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-darwin-arm64@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-darwin-arm64@npm:4.21.3"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-darwin-x64@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-darwin-x64@npm:4.21.3"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm-gnueabihf@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.3"
+ conditions: os=linux & cpu=arm & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm-musleabihf@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.3"
+ conditions: os=linux & cpu=arm & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm64-gnu@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.3"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm64-musl@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.3"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.3"
+ conditions: os=linux & cpu=ppc64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-riscv64-gnu@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.3"
+ conditions: os=linux & cpu=riscv64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-s390x-gnu@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.3"
+ conditions: os=linux & cpu=s390x & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-x64-gnu@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-x64-gnu@npm:4.21.3"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-x64-musl@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.3"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-arm64-msvc@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.3"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-ia32-msvc@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.3"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-x64-msvc@npm:4.21.3":
+ version: 4.21.3
+ resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.3"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@sigstore/bundle@npm:^2.3.2":
version: 2.3.2
resolution: "@sigstore/bundle@npm:2.3.2"
@@ -5641,6 +6024,47 @@ __metadata:
languageName: node
linkType: hard
+"@types/babel__core@npm:^7.20.5":
+ version: 7.20.5
+ resolution: "@types/babel__core@npm:7.20.5"
+ dependencies:
+ "@babel/parser": "npm:^7.20.7"
+ "@babel/types": "npm:^7.20.7"
+ "@types/babel__generator": "npm:*"
+ "@types/babel__template": "npm:*"
+ "@types/babel__traverse": "npm:*"
+ checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff
+ languageName: node
+ linkType: hard
+
+"@types/babel__generator@npm:*":
+ version: 7.6.8
+ resolution: "@types/babel__generator@npm:7.6.8"
+ dependencies:
+ "@babel/types": "npm:^7.0.0"
+ checksum: 10c0/f0ba105e7d2296bf367d6e055bb22996886c114261e2cb70bf9359556d0076c7a57239d019dee42bb063f565bade5ccb46009bce2044b2952d964bf9a454d6d2
+ languageName: node
+ linkType: hard
+
+"@types/babel__template@npm:*":
+ version: 7.4.4
+ resolution: "@types/babel__template@npm:7.4.4"
+ dependencies:
+ "@babel/parser": "npm:^7.1.0"
+ "@babel/types": "npm:^7.0.0"
+ checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b
+ languageName: node
+ linkType: hard
+
+"@types/babel__traverse@npm:*":
+ version: 7.20.6
+ resolution: "@types/babel__traverse@npm:7.20.6"
+ dependencies:
+ "@babel/types": "npm:^7.20.7"
+ checksum: 10c0/7ba7db61a53e28cac955aa99af280d2600f15a8c056619c05b6fc911cbe02c61aa4f2823299221b23ce0cce00b294c0e5f618ec772aa3f247523c2e48cf7b888
+ languageName: node
+ linkType: hard
+
"@types/body-parser@npm:*":
version: 1.19.5
resolution: "@types/body-parser@npm:1.19.5"
@@ -5717,7 +6141,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/estree@npm:*, @types/estree@npm:^1.0.5":
+"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.5":
version: 1.0.5
resolution: "@types/estree@npm:1.0.5"
checksum: 10c0/b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d
@@ -5905,6 +6329,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/react-dom@npm:^18.3.0":
+ version: 18.3.0
+ resolution: "@types/react-dom@npm:18.3.0"
+ dependencies:
+ "@types/react": "npm:*"
+ checksum: 10c0/6c90d2ed72c5a0e440d2c75d99287e4b5df3e7b011838cdc03ae5cd518ab52164d86990e73246b9d812eaf02ec351d74e3b4f5bd325bf341e13bf980392fd53b
+ languageName: node
+ linkType: hard
+
"@types/react-redux@npm:^7.1.16":
version: 7.1.18
resolution: "@types/react-redux@npm:7.1.18"
@@ -5928,6 +6361,16 @@ __metadata:
languageName: node
linkType: hard
+"@types/react@npm:^18.3.3":
+ version: 18.3.5
+ resolution: "@types/react@npm:18.3.5"
+ dependencies:
+ "@types/prop-types": "npm:*"
+ csstype: "npm:^3.0.2"
+ checksum: 10c0/548b1d3d7c2f0242fbfdbbd658731b4ce69a134be072fa83e6ab516f2840402a3f20e3e7f72e95133b23d4880ef24a6d864050dc8e1f7c68f39fa87ca8445917
+ languageName: node
+ linkType: hard
+
"@types/retry@npm:0.12.2":
version: 0.12.2
resolution: "@types/retry@npm:0.12.2"
@@ -6211,6 +6654,21 @@ __metadata:
languageName: node
linkType: hard
+"@vitejs/plugin-react@npm:^4.2.1, @vitejs/plugin-react@npm:^4.3.1":
+ version: 4.3.1
+ resolution: "@vitejs/plugin-react@npm:4.3.1"
+ dependencies:
+ "@babel/core": "npm:^7.24.5"
+ "@babel/plugin-transform-react-jsx-self": "npm:^7.24.5"
+ "@babel/plugin-transform-react-jsx-source": "npm:^7.24.1"
+ "@types/babel__core": "npm:^7.20.5"
+ react-refresh: "npm:^0.14.2"
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0
+ checksum: 10c0/39a027feddfd6b3e307121d79631462ef1aae05714ba7a2f9a73d240d0f89c2bf281132568eb27b55d6ddaf08d86ad1bd8b0066090240e570de8c6320eb9a903
+ languageName: node
+ linkType: hard
+
"@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1":
version: 1.12.1
resolution: "@webassemblyjs/ast@npm:1.12.1"
@@ -6362,6 +6820,13 @@ __metadata:
languageName: node
linkType: hard
+"@webcomponents/custom-elements@npm:^1.5.0":
+ version: 1.6.0
+ resolution: "@webcomponents/custom-elements@npm:1.6.0"
+ checksum: 10c0/8c3c3b0250ad7b063fe92b550fb725cc6074c8c5caea4a80901f9d9a93cdacf6dc0c73f715fa7b16f86e2ca1630e43cd80499bbf80e3a9b5c6ec042e074d22b4
+ languageName: node
+ linkType: hard
+
"@webpack-cli/configtest@npm:^2.1.1":
version: 2.1.1
resolution: "@webpack-cli/configtest@npm:2.1.1"
@@ -6536,7 +7001,7 @@ __metadata:
languageName: node
linkType: hard
-"acorn-walk@npm:^8.0.2":
+"acorn-walk@npm:^8.0.2, acorn-walk@npm:^8.2.0":
version: 8.3.4
resolution: "acorn-walk@npm:8.3.4"
dependencies:
@@ -6611,6 +7076,43 @@ __metadata:
languageName: node
linkType: hard
+"addons-linter@npm:7.1.0":
+ version: 7.1.0
+ resolution: "addons-linter@npm:7.1.0"
+ dependencies:
+ "@fluent/syntax": "npm:0.19.0"
+ "@mdn/browser-compat-data": "npm:5.6.0"
+ addons-moz-compare: "npm:1.3.0"
+ addons-scanner-utils: "npm:9.11.0"
+ ajv: "npm:8.17.1"
+ chalk: "npm:4.1.2"
+ cheerio: "npm:1.0.0-rc.12"
+ columnify: "npm:1.6.0"
+ common-tags: "npm:1.8.2"
+ deepmerge: "npm:4.3.1"
+ eslint: "npm:8.57.1"
+ eslint-plugin-no-unsanitized: "npm:4.0.2"
+ eslint-visitor-keys: "npm:4.0.0"
+ espree: "npm:10.1.0"
+ esprima: "npm:4.0.1"
+ fast-json-patch: "npm:3.1.1"
+ image-size: "npm:1.1.1"
+ jed: "npm:1.1.1"
+ json-merge-patch: "npm:1.0.2"
+ os-locale: "npm:5.0.0"
+ pino: "npm:8.20.0"
+ relaxed-json: "npm:1.0.3"
+ semver: "npm:7.6.3"
+ source-map-support: "npm:0.5.21"
+ upath: "npm:2.0.1"
+ yargs: "npm:17.7.2"
+ yauzl: "npm:2.10.0"
+ bin:
+ addons-linter: bin/addons-linter
+ checksum: 10c0/b25b97808e60e8950315e0568d653ab53def0fec91f1c49ad44059c9b97c4e1bf82b3257a314841395c8f2b2b1edf6c59a162717cb8ebc0d8612283e1673594e
+ languageName: node
+ linkType: hard
+
"addons-moz-compare@npm:1.3.0":
version: 1.3.0
resolution: "addons-moz-compare@npm:1.3.0"
@@ -6746,19 +7248,7 @@ __metadata:
languageName: node
linkType: hard
-"ajv@npm:^6.12.4, ajv@npm:^6.12.5":
- version: 6.12.6
- resolution: "ajv@npm:6.12.6"
- dependencies:
- fast-deep-equal: "npm:^3.1.1"
- fast-json-stable-stringify: "npm:^2.0.0"
- json-schema-traverse: "npm:^0.4.1"
- uri-js: "npm:^4.2.2"
- checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71
- languageName: node
- linkType: hard
-
-"ajv@npm:^8.0.0, ajv@npm:^8.11.0, ajv@npm:^8.9.0":
+"ajv@npm:8.17.1, ajv@npm:^8.0.0, ajv@npm:^8.11.0, ajv@npm:^8.9.0":
version: 8.17.1
resolution: "ajv@npm:8.17.1"
dependencies:
@@ -6770,6 +7260,18 @@ __metadata:
languageName: node
linkType: hard
+"ajv@npm:^6.12.4, ajv@npm:^6.12.5":
+ version: 6.12.6
+ resolution: "ajv@npm:6.12.6"
+ dependencies:
+ fast-deep-equal: "npm:^3.1.1"
+ fast-json-stable-stringify: "npm:^2.0.0"
+ json-schema-traverse: "npm:^0.4.1"
+ uri-js: "npm:^4.2.2"
+ checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71
+ languageName: node
+ linkType: hard
+
"ajv@npm:^8.0.1":
version: 8.6.3
resolution: "ajv@npm:8.6.3"
@@ -7179,6 +7681,13 @@ __metadata:
languageName: node
linkType: hard
+"async-lock@npm:^1.3.2":
+ version: 1.4.1
+ resolution: "async-lock@npm:1.4.1"
+ checksum: 10c0/f696991c7d894af1dc91abc81cc4f14b3785190a35afb1646d8ab91138238d55cabd83bfdd56c42663a008d72b3dc39493ff83797e550effc577d1ccbde254af
+ languageName: node
+ linkType: hard
+
"async@npm:^3.2.0, async@npm:^3.2.3":
version: 3.2.5
resolution: "async@npm:3.2.5"
@@ -7207,6 +7716,16 @@ __metadata:
languageName: node
linkType: hard
+"atomically@npm:^2.0.3":
+ version: 2.0.3
+ resolution: "atomically@npm:2.0.3"
+ dependencies:
+ stubborn-fs: "npm:^1.2.5"
+ when-exit: "npm:^2.1.1"
+ checksum: 10c0/b9008a74f590d29be947f34b7583dab32034335fedfe340ac3e6458e2e315c770d8af6f15cd3947214702c523d91b5f989498348b1ab49c197bd645dc87d7a94
+ languageName: node
+ linkType: hard
+
"autoprefixer@npm:^10.4.19":
version: 10.4.20
resolution: "autoprefixer@npm:10.4.20"
@@ -7492,6 +8011,22 @@ __metadata:
languageName: node
linkType: hard
+"boxen@npm:^8.0.1":
+ version: 8.0.1
+ resolution: "boxen@npm:8.0.1"
+ dependencies:
+ ansi-align: "npm:^3.0.1"
+ camelcase: "npm:^8.0.0"
+ chalk: "npm:^5.3.0"
+ cli-boxes: "npm:^3.0.0"
+ string-width: "npm:^7.2.0"
+ type-fest: "npm:^4.21.0"
+ widest-line: "npm:^5.0.0"
+ wrap-ansi: "npm:^9.0.0"
+ checksum: 10c0/8c54f9797bf59eec0b44c9043d9cb5d5b2783dc673e4650235e43a5155c43334e78ec189fd410cf92056c1054aee3758279809deed115b49e68f1a1c6b3faa32
+ languageName: node
+ linkType: hard
+
"bplist-parser@npm:^0.2.0":
version: 0.2.0
resolution: "bplist-parser@npm:0.2.0"
@@ -7801,7 +8336,7 @@ __metadata:
languageName: node
linkType: hard
-"camelcase@npm:8.0.0":
+"camelcase@npm:8.0.0, camelcase@npm:^8.0.0":
version: 8.0.0
resolution: "camelcase@npm:8.0.0"
checksum: 10c0/56c5fe072f0523c9908cdaac21d4a3b3fb0f608fb2e9ba90a60e792b95dd3bb3d1f3523873ab17d86d146e94171305f73ef619e2f538bd759675bc4a14b4bff3
@@ -7920,6 +8455,8 @@ __metadata:
"@carbon/icons-react": "npm:^10.49.5"
"@carbon/react": "npm:^1.65.0"
"@carbon/web-components": "npm:^2.12.0"
+ "@crxjs/vite-plugin": "npm:^2.0.0-beta.25"
+ "@vitejs/plugin-react": "npm:^4.3.1"
babel-loader: "npm:^9.1.3"
clean-webpack-plugin: "npm:^4.0.0"
color: "npm:^4.2.3"
@@ -7954,7 +8491,9 @@ __metadata:
stylelint-config-carbon: "npm:^1.17.0"
terser-webpack-plugin: "npm:^5.3.10"
url: "npm:^0.11.4"
- web-ext: "npm:^8.2.0"
+ vite: "npm:^5.4.6"
+ vite-plugin-web-extension: "npm:^4.1.6"
+ web-ext: "npm:^8.3.0"
webpack: "npm:^5.93.0"
webpack-bundle-analyzer: "npm:^4.10.2"
webpack-cli: "npm:^5.1.4"
@@ -7964,6 +8503,32 @@ __metadata:
languageName: unknown
linkType: soft
+"carbon-devtools-v11@workspace:packages/vite-test":
+ version: 0.0.0-use.local
+ resolution: "carbon-devtools-v11@workspace:packages/vite-test"
+ dependencies:
+ "@carbon/ibmdotcom-utilities": "npm:^2.12.0"
+ "@carbon/ibmdotcom-web-components": "npm:^2.12.0"
+ "@carbon/react": "npm:^1.65.0"
+ "@carbon/web-components": "npm:^2.12.0"
+ "@crxjs/vite-plugin": "npm:^2.0.0-beta.25"
+ "@eslint/js": "npm:^9.9.0"
+ "@types/react": "npm:^18.3.3"
+ "@types/react-dom": "npm:^18.3.0"
+ "@vitejs/plugin-react": "npm:^4.3.1"
+ eslint: "npm:^9.9.0"
+ eslint-plugin-react: "npm:^7.35.0"
+ eslint-plugin-react-hooks: "npm:^5.1.0-rc.0"
+ eslint-plugin-react-refresh: "npm:^0.4.9"
+ globals: "npm:^15.9.0"
+ react: "npm:^18.3.1"
+ react-dom: "npm:^18.3.1"
+ sass: "npm:^1.78.0"
+ vite: "npm:^5.4.1"
+ web-ext: "npm:^8.2.0"
+ languageName: unknown
+ linkType: soft
+
"carbon-icons@npm:7.0.7":
version: 7.0.7
resolution: "carbon-icons@npm:7.0.7"
@@ -8029,6 +8594,13 @@ __metadata:
languageName: node
linkType: hard
+"charenc@npm:0.0.2":
+ version: 0.0.2
+ resolution: "charenc@npm:0.0.2"
+ checksum: 10c0/a45ec39363a16799d0f9365c8dd0c78e711415113c6f14787a22462ef451f5013efae8a28f1c058f81fc01f2a6a16955f7a5fd0cd56247ce94a45349c89877d8
+ languageName: node
+ linkType: hard
+
"cheerio-select@npm:^1.5.0":
version: 1.5.0
resolution: "cheerio-select@npm:1.5.0"
@@ -8056,7 +8628,7 @@ __metadata:
languageName: node
linkType: hard
-"cheerio@npm:1.0.0":
+"cheerio@npm:1.0.0, cheerio@npm:^1.0.0-rc.10":
version: 1.0.0
resolution: "cheerio@npm:1.0.0"
dependencies:
@@ -8164,6 +8736,34 @@ __metadata:
languageName: node
linkType: hard
+"chrome-launcher@npm:1.1.0":
+ version: 1.1.0
+ resolution: "chrome-launcher@npm:1.1.0"
+ dependencies:
+ "@types/node": "npm:*"
+ escape-string-regexp: "npm:^4.0.0"
+ is-wsl: "npm:^2.2.0"
+ lighthouse-logger: "npm:^2.0.1"
+ bin:
+ print-chrome-path: bin/print-chrome-path.js
+ checksum: 10c0/f178356a2d3d4c22236acda01b1390e285659d5145058a8c2f4d2f5c3c79555d039c1b101c49e047cf77fe4e0222a82b62dc0cea1e46d1d83e6a50081b4bf92f
+ languageName: node
+ linkType: hard
+
+"chrome-launcher@npm:1.1.2":
+ version: 1.1.2
+ resolution: "chrome-launcher@npm:1.1.2"
+ dependencies:
+ "@types/node": "npm:*"
+ escape-string-regexp: "npm:^4.0.0"
+ is-wsl: "npm:^2.2.0"
+ lighthouse-logger: "npm:^2.0.1"
+ bin:
+ print-chrome-path: bin/print-chrome-path.js
+ checksum: 10c0/518a6cb846b7187a692c510cc9d3f4d2a87ad3e21cec5eaefb3dcb7ce72ac6ab8b5465cb90510480b9f0b077c8fc340f57e2e078e1d7719aff576595800470b2
+ languageName: node
+ linkType: hard
+
"chrome-trace-event@npm:^1.0.2":
version: 1.0.3
resolution: "chrome-trace-event@npm:1.0.3"
@@ -8656,6 +9256,18 @@ __metadata:
languageName: node
linkType: hard
+"configstore@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "configstore@npm:7.0.0"
+ dependencies:
+ atomically: "npm:^2.0.3"
+ dot-prop: "npm:^9.0.0"
+ graceful-fs: "npm:^4.2.11"
+ xdg-basedir: "npm:^5.1.0"
+ checksum: 10c0/46639ddcebe94e58ab903d1bcfaddf297585469ee11fb2900975531cf6e59f495fa1324bf594d6bf13c5daf02e1110e9f0634caecc11203c52283ff26e2a4d8b
+ languageName: node
+ linkType: hard
+
"confusing-browser-globals@npm:^1.0.10":
version: 1.0.10
resolution: "confusing-browser-globals@npm:1.0.10"
@@ -8670,6 +9282,18 @@ __metadata:
languageName: node
linkType: hard
+"connect-injector@npm:^0.4.4":
+ version: 0.4.4
+ resolution: "connect-injector@npm:0.4.4"
+ dependencies:
+ debug: "npm:^2.0.0"
+ q: "npm:^1.0.1"
+ stream-buffers: "npm:^0.2.3"
+ uberproto: "npm:^1.1.0"
+ checksum: 10c0/6186a21285db6e989d610e7e2223305f59f1f11d4977bbf62db21680eb6b2f9b6080d5f03171d98d346496388c0cdb3f38bcec9bfebd3a58bc258ce12186ea1c
+ languageName: node
+ linkType: hard
+
"console-control-strings@npm:^1.1.0":
version: 1.1.0
resolution: "console-control-strings@npm:1.1.0"
@@ -8959,6 +9583,13 @@ __metadata:
languageName: node
linkType: hard
+"crypt@npm:0.0.2":
+ version: 0.0.2
+ resolution: "crypt@npm:0.0.2"
+ checksum: 10c0/adbf263441dd801665d5425f044647533f39f4612544071b1471962209d235042fb703c27eea2795c7c53e1dfc242405173003f83cf4f4761a633d11f9653f18
+ languageName: node
+ linkType: hard
+
"crypto-random-string@npm:^4.0.0":
version: 4.0.0
resolution: "crypto-random-string@npm:4.0.0"
@@ -9322,6 +9953,13 @@ __metadata:
languageName: node
linkType: hard
+"cssom@npm:^0.5.0":
+ version: 0.5.0
+ resolution: "cssom@npm:0.5.0"
+ checksum: 10c0/8c4121c243baf0678c65dcac29b201ff0067dfecf978de9d5c83b2ff127a8fdefd2bfd54577f5ad8c80ed7d2c8b489ae01c82023545d010c4ecb87683fb403dd
+ languageName: node
+ linkType: hard
+
"cssstyle@npm:^3.0.0":
version: 3.0.0
resolution: "cssstyle@npm:3.0.0"
@@ -9424,7 +10062,7 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:2.6.9, debug@npm:^2.6.9":
+"debug@npm:2.6.9, debug@npm:^2.0.0, debug@npm:^2.6.9":
version: 2.6.9
resolution: "debug@npm:2.6.9"
dependencies:
@@ -9478,6 +10116,18 @@ __metadata:
languageName: node
linkType: hard
+"debug@npm:^4.3.3":
+ version: 4.3.7
+ resolution: "debug@npm:4.3.7"
+ dependencies:
+ ms: "npm:^2.1.3"
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b
+ languageName: node
+ linkType: hard
+
"decamelize-keys@npm:^1.1.0":
version: 1.1.0
resolution: "decamelize-keys@npm:1.1.0"
@@ -10027,6 +10677,15 @@ __metadata:
languageName: node
linkType: hard
+"dot-prop@npm:^9.0.0":
+ version: 9.0.0
+ resolution: "dot-prop@npm:9.0.0"
+ dependencies:
+ type-fest: "npm:^4.18.2"
+ checksum: 10c0/4bac49a2f559156811862ac92813906f70529c50da918eaab81b38dd869743c667d578e183607f5ae11e8ae2a02e43e98e32c8a37bc4cae76b04d5b576e3112f
+ languageName: node
+ linkType: hard
+
"dotenv-defaults@npm:^2.0.2":
version: 2.0.2
resolution: "dotenv-defaults@npm:2.0.2"
@@ -10593,6 +11252,13 @@ __metadata:
languageName: node
linkType: hard
+"es-module-lexer@npm:^0.10.0":
+ version: 0.10.5
+ resolution: "es-module-lexer@npm:0.10.5"
+ checksum: 10c0/5a199242971341fefe12ce5984602698d8f9c477e207f847aaed0f70519cf2c68ddbd22dd92b2cc4669a9d421a3b89a67d371994b64604ea24da21d35c42089e
+ languageName: node
+ linkType: hard
+
"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.5.3":
version: 1.5.4
resolution: "es-module-lexer@npm:1.5.4"
@@ -10647,6 +11313,86 @@ __metadata:
languageName: node
linkType: hard
+"esbuild@npm:^0.21.3":
+ version: 0.21.5
+ resolution: "esbuild@npm:0.21.5"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.21.5"
+ "@esbuild/android-arm": "npm:0.21.5"
+ "@esbuild/android-arm64": "npm:0.21.5"
+ "@esbuild/android-x64": "npm:0.21.5"
+ "@esbuild/darwin-arm64": "npm:0.21.5"
+ "@esbuild/darwin-x64": "npm:0.21.5"
+ "@esbuild/freebsd-arm64": "npm:0.21.5"
+ "@esbuild/freebsd-x64": "npm:0.21.5"
+ "@esbuild/linux-arm": "npm:0.21.5"
+ "@esbuild/linux-arm64": "npm:0.21.5"
+ "@esbuild/linux-ia32": "npm:0.21.5"
+ "@esbuild/linux-loong64": "npm:0.21.5"
+ "@esbuild/linux-mips64el": "npm:0.21.5"
+ "@esbuild/linux-ppc64": "npm:0.21.5"
+ "@esbuild/linux-riscv64": "npm:0.21.5"
+ "@esbuild/linux-s390x": "npm:0.21.5"
+ "@esbuild/linux-x64": "npm:0.21.5"
+ "@esbuild/netbsd-x64": "npm:0.21.5"
+ "@esbuild/openbsd-x64": "npm:0.21.5"
+ "@esbuild/sunos-x64": "npm:0.21.5"
+ "@esbuild/win32-arm64": "npm:0.21.5"
+ "@esbuild/win32-ia32": "npm:0.21.5"
+ "@esbuild/win32-x64": "npm:0.21.5"
+ dependenciesMeta:
+ "@esbuild/aix-ppc64":
+ optional: true
+ "@esbuild/android-arm":
+ optional: true
+ "@esbuild/android-arm64":
+ optional: true
+ "@esbuild/android-x64":
+ optional: true
+ "@esbuild/darwin-arm64":
+ optional: true
+ "@esbuild/darwin-x64":
+ optional: true
+ "@esbuild/freebsd-arm64":
+ optional: true
+ "@esbuild/freebsd-x64":
+ optional: true
+ "@esbuild/linux-arm":
+ optional: true
+ "@esbuild/linux-arm64":
+ optional: true
+ "@esbuild/linux-ia32":
+ optional: true
+ "@esbuild/linux-loong64":
+ optional: true
+ "@esbuild/linux-mips64el":
+ optional: true
+ "@esbuild/linux-ppc64":
+ optional: true
+ "@esbuild/linux-riscv64":
+ optional: true
+ "@esbuild/linux-s390x":
+ optional: true
+ "@esbuild/linux-x64":
+ optional: true
+ "@esbuild/netbsd-x64":
+ optional: true
+ "@esbuild/openbsd-x64":
+ optional: true
+ "@esbuild/sunos-x64":
+ optional: true
+ "@esbuild/win32-arm64":
+ optional: true
+ "@esbuild/win32-ia32":
+ optional: true
+ "@esbuild/win32-x64":
+ optional: true
+ bin:
+ esbuild: bin/esbuild
+ checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de
+ languageName: node
+ linkType: hard
+
"escalade@npm:^3.1.1":
version: 3.1.1
resolution: "escalade@npm:3.1.1"
@@ -10948,6 +11694,24 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-react-hooks@npm:^5.1.0-rc.0":
+ version: 5.1.0-rc-fb9a90fa48-20240614
+ resolution: "eslint-plugin-react-hooks@npm:5.1.0-rc-fb9a90fa48-20240614"
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+ checksum: 10c0/e27a8073a19d8411cb1cbbd2a935d0f5ec824efb7fd17d907df5c71df47ace9faa9c08c0e8f6db627b62db202a146ff111e6b3067e31773af0b41d15a34ba956
+ languageName: node
+ linkType: hard
+
+"eslint-plugin-react-refresh@npm:^0.4.9":
+ version: 0.4.11
+ resolution: "eslint-plugin-react-refresh@npm:0.4.11"
+ peerDependencies:
+ eslint: ">=7"
+ checksum: 10c0/0c7d4ce30a70fbd6460ea9ca45b029b1cc806fd922d308ad332315d0e1725a37a578283809913bf7a7c84c613e3313e891dde7692a8e6ef2979dbff7edf45901
+ languageName: node
+ linkType: hard
+
"eslint-plugin-react@npm:^7.32.2":
version: 7.35.0
resolution: "eslint-plugin-react@npm:7.35.0"
@@ -10976,6 +11740,34 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-react@npm:^7.35.0":
+ version: 7.36.1
+ resolution: "eslint-plugin-react@npm:7.36.1"
+ dependencies:
+ array-includes: "npm:^3.1.8"
+ array.prototype.findlast: "npm:^1.2.5"
+ array.prototype.flatmap: "npm:^1.3.2"
+ array.prototype.tosorted: "npm:^1.1.4"
+ doctrine: "npm:^2.1.0"
+ es-iterator-helpers: "npm:^1.0.19"
+ estraverse: "npm:^5.3.0"
+ hasown: "npm:^2.0.2"
+ jsx-ast-utils: "npm:^2.4.1 || ^3.0.0"
+ minimatch: "npm:^3.1.2"
+ object.entries: "npm:^1.1.8"
+ object.fromentries: "npm:^2.0.8"
+ object.values: "npm:^1.2.0"
+ prop-types: "npm:^15.8.1"
+ resolve: "npm:^2.0.0-next.5"
+ semver: "npm:^6.3.1"
+ string.prototype.matchall: "npm:^4.0.11"
+ string.prototype.repeat: "npm:^1.0.0"
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+ checksum: 10c0/8cb37f7fb351213bc44263580ff77627e14e27870fd81dae593e3de2826340b9bd8bbac7ae00fd5de69751a0660b2e51bd26760596f4ae85548f6b1bd76706e6
+ languageName: node
+ linkType: hard
+
"eslint-plugin-ssr-friendly@npm:^1.3.0":
version: 1.3.0
resolution: "eslint-plugin-ssr-friendly@npm:1.3.0"
@@ -11136,6 +11928,54 @@ __metadata:
languageName: node
linkType: hard
+"eslint@npm:8.57.1":
+ version: 8.57.1
+ resolution: "eslint@npm:8.57.1"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.2.0"
+ "@eslint-community/regexpp": "npm:^4.6.1"
+ "@eslint/eslintrc": "npm:^2.1.4"
+ "@eslint/js": "npm:8.57.1"
+ "@humanwhocodes/config-array": "npm:^0.13.0"
+ "@humanwhocodes/module-importer": "npm:^1.0.1"
+ "@nodelib/fs.walk": "npm:^1.2.8"
+ "@ungap/structured-clone": "npm:^1.2.0"
+ ajv: "npm:^6.12.4"
+ chalk: "npm:^4.0.0"
+ cross-spawn: "npm:^7.0.2"
+ debug: "npm:^4.3.2"
+ doctrine: "npm:^3.0.0"
+ escape-string-regexp: "npm:^4.0.0"
+ eslint-scope: "npm:^7.2.2"
+ eslint-visitor-keys: "npm:^3.4.3"
+ espree: "npm:^9.6.1"
+ esquery: "npm:^1.4.2"
+ esutils: "npm:^2.0.2"
+ fast-deep-equal: "npm:^3.1.3"
+ file-entry-cache: "npm:^6.0.1"
+ find-up: "npm:^5.0.0"
+ glob-parent: "npm:^6.0.2"
+ globals: "npm:^13.19.0"
+ graphemer: "npm:^1.4.0"
+ ignore: "npm:^5.2.0"
+ imurmurhash: "npm:^0.1.4"
+ is-glob: "npm:^4.0.0"
+ is-path-inside: "npm:^3.0.3"
+ js-yaml: "npm:^4.1.0"
+ json-stable-stringify-without-jsonify: "npm:^1.0.1"
+ levn: "npm:^0.4.1"
+ lodash.merge: "npm:^4.6.2"
+ minimatch: "npm:^3.1.2"
+ natural-compare: "npm:^1.4.0"
+ optionator: "npm:^0.9.3"
+ strip-ansi: "npm:^6.0.1"
+ text-table: "npm:^0.2.0"
+ bin:
+ eslint: bin/eslint.js
+ checksum: 10c0/1fd31533086c1b72f86770a4d9d7058ee8b4643fd1cfd10c7aac1ecb8725698e88352a87805cf4b2ce890aa35947df4b4da9655fb7fdfa60dbb448a43f6ebcf1
+ languageName: node
+ linkType: hard
+
"eslint@npm:^9.9.0":
version: 9.9.0
resolution: "eslint@npm:9.9.0"
@@ -11196,7 +12036,7 @@ __metadata:
languageName: node
linkType: hard
-"espree@npm:^10.0.1, espree@npm:^10.1.0":
+"espree@npm:10.1.0, espree@npm:^10.0.1, espree@npm:^10.1.0":
version: 10.1.0
resolution: "espree@npm:10.1.0"
dependencies:
@@ -11276,6 +12116,13 @@ __metadata:
languageName: node
linkType: hard
+"estree-walker@npm:^2.0.1":
+ version: 2.0.2
+ resolution: "estree-walker@npm:2.0.2"
+ checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af
+ languageName: node
+ linkType: hard
+
"esutils@npm:^2.0.0, esutils@npm:^2.0.2":
version: 2.0.3
resolution: "esutils@npm:2.0.3"
@@ -11481,29 +12328,29 @@ __metadata:
languageName: node
linkType: hard
-"fast-glob@npm:^3.2.9":
- version: 3.2.12
- resolution: "fast-glob@npm:3.2.12"
+"fast-glob@npm:^3.2.11, fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2":
+ version: 3.3.2
+ resolution: "fast-glob@npm:3.3.2"
dependencies:
"@nodelib/fs.stat": "npm:^2.0.2"
"@nodelib/fs.walk": "npm:^1.2.3"
glob-parent: "npm:^5.1.2"
merge2: "npm:^1.3.0"
micromatch: "npm:^4.0.4"
- checksum: 10c0/08604fb8ef6442ce74068bef3c3104382bb1f5ab28cf75e4ee904662778b60ad620e1405e692b7edea598ef445f5d387827a965ba034e1892bf54b1dfde97f26
+ checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845
languageName: node
linkType: hard
-"fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2":
- version: 3.3.2
- resolution: "fast-glob@npm:3.3.2"
+"fast-glob@npm:^3.2.9":
+ version: 3.2.12
+ resolution: "fast-glob@npm:3.2.12"
dependencies:
"@nodelib/fs.stat": "npm:^2.0.2"
"@nodelib/fs.walk": "npm:^1.2.3"
glob-parent: "npm:^5.1.2"
merge2: "npm:^1.3.0"
micromatch: "npm:^4.0.4"
- checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845
+ checksum: 10c0/08604fb8ef6442ce74068bef3c3104382bb1f5ab28cf75e4ee904662778b60ad620e1405e692b7edea598ef445f5d387827a965ba034e1892bf54b1dfde97f26
languageName: node
linkType: hard
@@ -11778,6 +12625,21 @@ __metadata:
languageName: node
linkType: hard
+"firefox-profile@npm:4.7.0":
+ version: 4.7.0
+ resolution: "firefox-profile@npm:4.7.0"
+ dependencies:
+ adm-zip: "npm:~0.5.x"
+ fs-extra: "npm:^11.2.0"
+ ini: "npm:^4.1.3"
+ minimist: "npm:^1.2.8"
+ xml2js: "npm:^0.6.2"
+ bin:
+ firefox-profile: lib/cli.js
+ checksum: 10c0/032949c923336f843015757f1aba90d19b9f0d7277ba91705958a5579d55c98a424700388ac263a1dc67d6a942403e25090443703ff0f38347a20e9dbc40e1a3
+ languageName: node
+ linkType: hard
+
"first-chunk-stream@npm:3.0.0, first-chunk-stream@npm:^3.0.0":
version: 3.0.0
resolution: "first-chunk-stream@npm:3.0.0"
@@ -12037,6 +12899,17 @@ __metadata:
languageName: node
linkType: hard
+"fs-extra@npm:^10.0.1, fs-extra@npm:^10.1.0":
+ version: 10.1.0
+ resolution: "fs-extra@npm:10.1.0"
+ dependencies:
+ graceful-fs: "npm:^4.2.0"
+ jsonfile: "npm:^6.0.1"
+ universalify: "npm:^2.0.0"
+ checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e
+ languageName: node
+ linkType: hard
+
"fs-extra@npm:~9.0.1":
version: 9.0.1
resolution: "fs-extra@npm:9.0.1"
@@ -12084,8 +12957,18 @@ __metadata:
languageName: node
linkType: hard
-"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin":
- version: 2.3.2
+"fsevents@npm:~2.3.3":
+ version: 2.3.3
+ resolution: "fsevents@npm:2.3.3"
+ dependencies:
+ node-gyp: "npm:latest"
+ checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
+"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin":
+ version: 2.3.2
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1"
dependencies:
node-gyp: "npm:latest"
@@ -12093,6 +12976,15 @@ __metadata:
languageName: node
linkType: hard
+"fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin":
+ version: 2.3.3
+ resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
+ dependencies:
+ node-gyp: "npm:latest"
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
"function-bind@npm:^1.1.1":
version: 1.1.1
resolution: "function-bind@npm:1.1.1"
@@ -12560,6 +13452,13 @@ __metadata:
languageName: node
linkType: hard
+"globals@npm:^15.9.0":
+ version: 15.9.0
+ resolution: "globals@npm:15.9.0"
+ checksum: 10c0/de4b553e412e7e830998578d51b605c492256fb2a9273eaeec6ec9ee519f1c5aa50de57e3979911607fd7593a4066420e01d8c3d551e7a6a236e96c521aee36c
+ languageName: node
+ linkType: hard
+
"globalthis@npm:^1.0.3":
version: 1.0.4
resolution: "globalthis@npm:1.0.4"
@@ -12975,6 +13874,13 @@ __metadata:
languageName: node
linkType: hard
+"html-escaper@npm:^3.0.3":
+ version: 3.0.3
+ resolution: "html-escaper@npm:3.0.3"
+ checksum: 10c0/a042fa4139127ff7546513e90ea39cc9161a1938ce90122dbc4260d4b7252c9aa8452f4509c0c2889901b8ae9a8699179150f1f99d3f80bcf7317573c5f08f4e
+ languageName: node
+ linkType: hard
+
"html-loader@npm:^5.1.0":
version: 5.1.0
resolution: "html-loader@npm:5.1.0"
@@ -13659,6 +14565,13 @@ __metadata:
languageName: node
linkType: hard
+"is-buffer@npm:~1.1.6":
+ version: 1.1.6
+ resolution: "is-buffer@npm:1.1.6"
+ checksum: 10c0/ae18aa0b6e113d6c490ad1db5e8df9bdb57758382b313f5a22c9c61084875c6396d50bbf49315f5b1926d142d74dfb8d31b40d993a383e0a158b15fea7a82234
+ languageName: node
+ linkType: hard
+
"is-callable@npm:^1.1.3, is-callable@npm:^1.2.7":
version: 1.2.7
resolution: "is-callable@npm:1.2.7"
@@ -13804,6 +14717,15 @@ __metadata:
languageName: node
linkType: hard
+"is-in-ci@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "is-in-ci@npm:1.0.0"
+ bin:
+ is-in-ci: cli.js
+ checksum: 10c0/98f9cec4c35aece4cf731abf35ebf28359a9b0324fac810da05b842515d9ccb33b8999c1d9a679f0362e1a4df3292065c38d7f86327b1387fa667bc0150f4898
+ languageName: node
+ linkType: hard
+
"is-inside-container@npm:^1.0.0":
version: 1.0.0
resolution: "is-inside-container@npm:1.0.0"
@@ -13825,6 +14747,16 @@ __metadata:
languageName: node
linkType: hard
+"is-installed-globally@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "is-installed-globally@npm:1.0.0"
+ dependencies:
+ global-directory: "npm:^4.0.1"
+ is-path-inside: "npm:^4.0.0"
+ checksum: 10c0/5f57745b6e75b2e9e707a26470d0cb74291d9be33c0fe0dc06c6955fe086bc2ca0a8960631b1ecb9677100eac90af33e911aec7a2c0b88097d702bfa3b76486d
+ languageName: node
+ linkType: hard
+
"is-interactive@npm:^1.0.0":
version: 1.0.0
resolution: "is-interactive@npm:1.0.0"
@@ -13943,6 +14875,13 @@ __metadata:
languageName: node
linkType: hard
+"is-path-inside@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "is-path-inside@npm:4.0.0"
+ checksum: 10c0/51188d7e2b1d907a9a5f7c18d99a90b60870b951ed87cf97595d9aaa429d4c010652c3350bcbf31182e7f4b0eab9a1860b43e16729b13cb1a44baaa6cdb64c46
+ languageName: node
+ linkType: hard
+
"is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0":
version: 1.1.0
resolution: "is-plain-obj@npm:1.1.0"
@@ -13980,6 +14919,13 @@ __metadata:
languageName: node
linkType: hard
+"is-primitive@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "is-primitive@npm:3.0.1"
+ checksum: 10c0/2e3b6f029fabbdda467ea51ea4fdd00e6552434108b863a08f296638072c506a7c195089e3e31f83e7fc14bebbd1c5c9f872fe127c9284a7665c8227b47ffdd6
+ languageName: node
+ linkType: hard
+
"is-regex@npm:^1.0.5, is-regex@npm:^1.1.0, is-regex@npm:^1.1.4":
version: 1.1.4
resolution: "is-regex@npm:1.1.4"
@@ -14342,6 +15288,13 @@ __metadata:
languageName: node
linkType: hard
+"jose@npm:5.9.2":
+ version: 5.9.2
+ resolution: "jose@npm:5.9.2"
+ checksum: 10c0/0727bc21777e54f9b1e690ad9461c12f5d642b40ffd4e4e3adc7169277e31d9d979c1bc667ba30627a13795dca6cfa61bc5d169bb3f91a6da1f67350d094c3e6
+ languageName: node
+ linkType: hard
+
"js-base64@npm:^2.1.9":
version: 2.6.4
resolution: "js-base64@npm:2.6.4"
@@ -14462,6 +15415,15 @@ __metadata:
languageName: node
linkType: hard
+"jsesc@npm:^3.0.2":
+ version: 3.0.2
+ resolution: "jsesc@npm:3.0.2"
+ bin:
+ jsesc: bin/jsesc
+ checksum: 10c0/ef22148f9e793180b14d8a145ee6f9f60f301abf443288117b4b6c53d0ecd58354898dc506ccbb553a5f7827965cd38bc5fb726575aae93c5e8915e2de8290e1
+ languageName: node
+ linkType: hard
+
"jsesc@npm:~0.5.0":
version: 0.5.0
resolution: "jsesc@npm:0.5.0"
@@ -14699,6 +15661,13 @@ __metadata:
languageName: node
linkType: hard
+"ky@npm:^1.2.0":
+ version: 1.7.2
+ resolution: "ky@npm:1.7.2"
+ checksum: 10c0/ce42c0c5eec839dd13fd14f0b60fb6f56c7c8a0df8e228597f5206b1db2f3608f3a0e477a4c002c838d21b8e65872632ef4655e9eb8508455b3c3b296af40ebc
+ languageName: node
+ linkType: hard
+
"language-subtag-registry@npm:^0.3.20":
version: 0.3.23
resolution: "language-subtag-registry@npm:0.3.23"
@@ -14734,6 +15703,15 @@ __metadata:
languageName: node
linkType: hard
+"latest-version@npm:^9.0.0":
+ version: 9.0.0
+ resolution: "latest-version@npm:9.0.0"
+ dependencies:
+ package-json: "npm:^10.0.0"
+ checksum: 10c0/643cfda3a58dfb3af221a2950e433393d28a5adbe225d1cbbb358dbcbb04e9f8dce15b892f8ae3e3156f50693428dbd7ca13a69edfbdfcd94e62519480d7041e
+ languageName: node
+ linkType: hard
+
"launch-editor@npm:^2.6.1":
version: 2.8.1
resolution: "launch-editor@npm:2.8.1"
@@ -14919,6 +15897,16 @@ __metadata:
languageName: node
linkType: hard
+"lighthouse-logger@npm:^2.0.1":
+ version: 2.0.1
+ resolution: "lighthouse-logger@npm:2.0.1"
+ dependencies:
+ debug: "npm:^2.6.9"
+ marky: "npm:^1.2.2"
+ checksum: 10c0/414743d9b1491ad127c78741edfe88bd1c2411b267274c973036b90f56a268c3b8c3e02498bce04b560083da34a149bc3f81d2c47b6c6ad592202354cf781c43
+ languageName: node
+ linkType: hard
+
"lilconfig@npm:^2.0.3":
version: 2.1.0
resolution: "lilconfig@npm:2.1.0"
@@ -14947,6 +15935,19 @@ __metadata:
languageName: node
linkType: hard
+"linkedom@npm:^0.14.21":
+ version: 0.14.26
+ resolution: "linkedom@npm:0.14.26"
+ dependencies:
+ css-select: "npm:^5.1.0"
+ cssom: "npm:^0.5.0"
+ html-escaper: "npm:^3.0.3"
+ htmlparser2: "npm:^8.0.1"
+ uhyphen: "npm:^0.2.0"
+ checksum: 10c0/4550b1145a37512488cb4de48dcf80ad5bc54947966a00d04d9558b96fef5ed7552c3d8847707dd1b296bb4527cae75f449ec65db3e528050947423d4333864e
+ languageName: node
+ linkType: hard
+
"lint-staged@npm:^15.2.9":
version: 15.2.9
resolution: "lint-staged@npm:15.2.9"
@@ -15248,6 +16249,13 @@ __metadata:
languageName: node
linkType: hard
+"lodash.uniqby@npm:^4.7.0":
+ version: 4.7.0
+ resolution: "lodash.uniqby@npm:4.7.0"
+ checksum: 10c0/c505c0de20ca759599a2ba38710e8fb95ff2d2028e24d86c901ef2c74be8056518571b9b754bfb75053b2818d30dd02243e4a4621a6940c206bbb3f7626db656
+ languageName: node
+ linkType: hard
+
"lodash.upperfirst@npm:^4.3.1":
version: 4.3.1
resolution: "lodash.upperfirst@npm:4.3.1"
@@ -15358,6 +16366,15 @@ __metadata:
languageName: node
linkType: hard
+"magic-string@npm:^0.26.0":
+ version: 0.26.7
+ resolution: "magic-string@npm:0.26.7"
+ dependencies:
+ sourcemap-codec: "npm:^1.4.8"
+ checksum: 10c0/950035b344fe2a8163668980bc4a215a0b225086e6e22100fd947e7647053c6ba6b4f11a04de83a97a276526ccb602ef53b173725dbb1971fb146cff5a5e14f6
+ languageName: node
+ linkType: hard
+
"make-dir@npm:4.0.0":
version: 4.0.0
resolution: "make-dir@npm:4.0.0"
@@ -15450,6 +16467,17 @@ __metadata:
languageName: node
linkType: hard
+"md5@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "md5@npm:2.3.0"
+ dependencies:
+ charenc: "npm:0.0.2"
+ crypt: "npm:0.0.2"
+ is-buffer: "npm:~1.1.6"
+ checksum: 10c0/14a21d597d92e5b738255fbe7fe379905b8cb97e0a49d44a20b58526a646ec5518c337b817ce0094ca94d3e81a3313879c4c7b510d250c282d53afbbdede9110
+ languageName: node
+ linkType: hard
+
"mdn-data@npm:2.0.14":
version: 2.0.14
resolution: "mdn-data@npm:2.0.14"
@@ -16007,7 +17035,7 @@ __metadata:
languageName: node
linkType: hard
-"ms@npm:2.1.3, ms@npm:^2.1.1":
+"ms@npm:2.1.3, ms@npm:^2.1.1, ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
@@ -17233,6 +18261,18 @@ __metadata:
languageName: node
linkType: hard
+"package-json@npm:^10.0.0":
+ version: 10.0.1
+ resolution: "package-json@npm:10.0.1"
+ dependencies:
+ ky: "npm:^1.2.0"
+ registry-auth-token: "npm:^5.0.2"
+ registry-url: "npm:^6.0.1"
+ semver: "npm:^7.6.0"
+ checksum: 10c0/4a55648d820496326730a7b149fd3fd8382e96f3d6def5ec687f46b75063894acf06b21f79832b40bb094c821d97f532cb0f009f85c4102d0084b488d4f492d3
+ languageName: node
+ linkType: hard
+
"package-json@npm:^8.1.0":
version: 8.1.1
resolution: "package-json@npm:8.1.1"
@@ -17581,7 +18621,7 @@ __metadata:
languageName: node
linkType: hard
-"picomatch@npm:^2.3.1":
+"picomatch@npm:^2.2.2, picomatch@npm:^2.3.1":
version: 2.3.1
resolution: "picomatch@npm:2.3.1"
checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
@@ -17641,7 +18681,7 @@ __metadata:
languageName: node
linkType: hard
-"pino-abstract-transport@npm:^1.1.0":
+"pino-abstract-transport@npm:^1.1.0, pino-abstract-transport@npm:^1.2.0":
version: 1.2.0
resolution: "pino-abstract-transport@npm:1.2.0"
dependencies:
@@ -17658,6 +18698,13 @@ __metadata:
languageName: node
linkType: hard
+"pino-std-serializers@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "pino-std-serializers@npm:7.0.0"
+ checksum: 10c0/73e694d542e8de94445a03a98396cf383306de41fd75ecc07085d57ed7a57896198508a0dec6eefad8d701044af21eb27253ccc352586a03cf0d4a0bd25b4133
+ languageName: node
+ linkType: hard
+
"pino@npm:8.20.0":
version: 8.20.0
resolution: "pino@npm:8.20.0"
@@ -17679,6 +18726,27 @@ __metadata:
languageName: node
linkType: hard
+"pino@npm:9.4.0":
+ version: 9.4.0
+ resolution: "pino@npm:9.4.0"
+ dependencies:
+ atomic-sleep: "npm:^1.0.0"
+ fast-redact: "npm:^3.1.1"
+ on-exit-leak-free: "npm:^2.1.0"
+ pino-abstract-transport: "npm:^1.2.0"
+ pino-std-serializers: "npm:^7.0.0"
+ process-warning: "npm:^4.0.0"
+ quick-format-unescaped: "npm:^4.0.3"
+ real-require: "npm:^0.2.0"
+ safe-stable-stringify: "npm:^2.3.1"
+ sonic-boom: "npm:^4.0.1"
+ thread-stream: "npm:^3.0.0"
+ bin:
+ pino: bin.js
+ checksum: 10c0/12a3d74968964d92b18ca7d6095a3c5b86478dc22264a37486d64e102085ed08820fcbe75e640acc3542fdf2937a34e5050b624f98e6ac62dd10f5e1328058a2
+ languageName: node
+ linkType: hard
+
"pirates@npm:^4.0.6":
version: 4.0.6
resolution: "pirates@npm:4.0.6"
@@ -18927,6 +19995,17 @@ __metadata:
languageName: node
linkType: hard
+"postcss@npm:^8.4.43":
+ version: 8.4.45
+ resolution: "postcss@npm:8.4.45"
+ dependencies:
+ nanoid: "npm:^3.3.7"
+ picocolors: "npm:^1.0.1"
+ source-map-js: "npm:^1.2.0"
+ checksum: 10c0/ad6f8b9b1157d678560373696109745ab97a947d449f8a997acac41c7f1e4c0f3ca4b092d6df1387f430f2c9a319987b1780dbdc27e35800a88cde9b606c1e8f
+ languageName: node
+ linkType: hard
+
"preact@npm:^10.23.2":
version: 10.23.2
resolution: "preact@npm:10.23.2"
@@ -19033,6 +20112,13 @@ __metadata:
languageName: node
linkType: hard
+"process-warning@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "process-warning@npm:4.0.0"
+ checksum: 10c0/5312a72b69d37a1b82ad03f3dfa0090dab3804a8fd995d06c28e3c002852bd82f5584217d9f4a3f197892bb2afc22d57e2c662c7e906b5abb48c0380c7b0880d
+ languageName: node
+ linkType: hard
+
"process@npm:^0.11.10":
version: 0.11.10
resolution: "process@npm:0.11.10"
@@ -19214,7 +20300,7 @@ __metadata:
languageName: node
linkType: hard
-"q@npm:^1.1.2":
+"q@npm:^1.0.1, q@npm:^1.1.2":
version: 1.5.1
resolution: "q@npm:1.5.1"
checksum: 10c0/7855fbdba126cb7e92ef3a16b47ba998c0786ec7fface236e3eb0135b65df36429d91a86b1fff3ab0927b4ac4ee88a2c44527c7c3b8e2a37efbec9fe34803df4
@@ -19412,7 +20498,7 @@ __metadata:
languageName: node
linkType: hard
-"react-dom@npm:^18.3.1":
+"react-dom@npm:^18.2.0, react-dom@npm:^18.3.1":
version: 18.3.1
resolution: "react-dom@npm:18.3.1"
dependencies:
@@ -19473,6 +20559,20 @@ __metadata:
languageName: node
linkType: hard
+"react-refresh@npm:^0.13.0":
+ version: 0.13.0
+ resolution: "react-refresh@npm:0.13.0"
+ checksum: 10c0/cb9f324d471485e569628854dc08d1550c0798cde57f1bfb8d954e006659de1da0bdccaf7d5d2ac0d3d53df1aae7b740b2a36128789afb8aff0f7ec01b128587
+ languageName: node
+ linkType: hard
+
+"react-refresh@npm:^0.14.2":
+ version: 0.14.2
+ resolution: "react-refresh@npm:0.14.2"
+ checksum: 10c0/875b72ef56b147a131e33f2abd6ec059d1989854b3ff438898e4f9310bfcc73acff709445b7ba843318a953cb9424bcc2c05af2b3d80011cee28f25aef3e2ebb
+ languageName: node
+ linkType: hard
+
"react-svg-core@npm:^3.0.3":
version: 3.0.3
resolution: "react-svg-core@npm:3.0.3"
@@ -19570,7 +20670,7 @@ __metadata:
languageName: node
linkType: hard
-"react@npm:^18.3.1":
+"react@npm:^18.2.0, react@npm:^18.3.1":
version: 18.3.1
resolution: "react@npm:18.3.1"
dependencies:
@@ -19978,7 +21078,7 @@ __metadata:
languageName: node
linkType: hard
-"registry-auth-token@npm:^5.0.1":
+"registry-auth-token@npm:^5.0.1, registry-auth-token@npm:^5.0.2":
version: 5.0.2
resolution: "registry-auth-token@npm:5.0.2"
dependencies:
@@ -19987,7 +21087,7 @@ __metadata:
languageName: node
linkType: hard
-"registry-url@npm:^6.0.0":
+"registry-url@npm:^6.0.0, registry-url@npm:^6.0.1":
version: 6.0.1
resolution: "registry-url@npm:6.0.1"
dependencies:
@@ -20332,6 +21432,83 @@ __metadata:
languageName: node
linkType: hard
+"rollup@npm:2.78.1":
+ version: 2.78.1
+ resolution: "rollup@npm:2.78.1"
+ dependencies:
+ fsevents: "npm:~2.3.2"
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ bin:
+ rollup: dist/bin/rollup
+ checksum: 10c0/ae24b886adee31455ec030736b113f84628a4f74106ea47ebf666fa5c7eac23f49b7d15acb5b5ee47671f3e184fb1c8b05946e5c76eb889a1dafaf5c75c9b70a
+ languageName: node
+ linkType: hard
+
+"rollup@npm:^4.20.0":
+ version: 4.21.3
+ resolution: "rollup@npm:4.21.3"
+ dependencies:
+ "@rollup/rollup-android-arm-eabi": "npm:4.21.3"
+ "@rollup/rollup-android-arm64": "npm:4.21.3"
+ "@rollup/rollup-darwin-arm64": "npm:4.21.3"
+ "@rollup/rollup-darwin-x64": "npm:4.21.3"
+ "@rollup/rollup-linux-arm-gnueabihf": "npm:4.21.3"
+ "@rollup/rollup-linux-arm-musleabihf": "npm:4.21.3"
+ "@rollup/rollup-linux-arm64-gnu": "npm:4.21.3"
+ "@rollup/rollup-linux-arm64-musl": "npm:4.21.3"
+ "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.21.3"
+ "@rollup/rollup-linux-riscv64-gnu": "npm:4.21.3"
+ "@rollup/rollup-linux-s390x-gnu": "npm:4.21.3"
+ "@rollup/rollup-linux-x64-gnu": "npm:4.21.3"
+ "@rollup/rollup-linux-x64-musl": "npm:4.21.3"
+ "@rollup/rollup-win32-arm64-msvc": "npm:4.21.3"
+ "@rollup/rollup-win32-ia32-msvc": "npm:4.21.3"
+ "@rollup/rollup-win32-x64-msvc": "npm:4.21.3"
+ "@types/estree": "npm:1.0.5"
+ fsevents: "npm:~2.3.2"
+ dependenciesMeta:
+ "@rollup/rollup-android-arm-eabi":
+ optional: true
+ "@rollup/rollup-android-arm64":
+ optional: true
+ "@rollup/rollup-darwin-arm64":
+ optional: true
+ "@rollup/rollup-darwin-x64":
+ optional: true
+ "@rollup/rollup-linux-arm-gnueabihf":
+ optional: true
+ "@rollup/rollup-linux-arm-musleabihf":
+ optional: true
+ "@rollup/rollup-linux-arm64-gnu":
+ optional: true
+ "@rollup/rollup-linux-arm64-musl":
+ optional: true
+ "@rollup/rollup-linux-powerpc64le-gnu":
+ optional: true
+ "@rollup/rollup-linux-riscv64-gnu":
+ optional: true
+ "@rollup/rollup-linux-s390x-gnu":
+ optional: true
+ "@rollup/rollup-linux-x64-gnu":
+ optional: true
+ "@rollup/rollup-linux-x64-musl":
+ optional: true
+ "@rollup/rollup-win32-arm64-msvc":
+ optional: true
+ "@rollup/rollup-win32-ia32-msvc":
+ optional: true
+ "@rollup/rollup-win32-x64-msvc":
+ optional: true
+ fsevents:
+ optional: true
+ bin:
+ rollup: dist/bin/rollup
+ checksum: 10c0/a9f98366a451f1302276390de9c0c59b464d680946410f53c14e7057fa84642efbe05eca8d85076962657955d77bb4a2d2b6dd8b70baf58c3c4b56f565d804dd
+ languageName: node
+ linkType: hard
+
"rrweb-cssom@npm:^0.6.0":
version: 0.6.0
resolution: "rrweb-cssom@npm:0.6.0"
@@ -20381,6 +21558,15 @@ __metadata:
languageName: node
linkType: hard
+"rxjs@npm:7.5.7":
+ version: 7.5.7
+ resolution: "rxjs@npm:7.5.7"
+ dependencies:
+ tslib: "npm:^2.1.0"
+ checksum: 10c0/283620b3c90b85467c3549f7cda0dd768bc18719cccbbdd9aacadb0f0946827ab20d036f1a00d78066d769764e73070bfee8706091d77b8d971975598f6cbbd4
+ languageName: node
+ linkType: hard
+
"rxjs@npm:^7.5.5":
version: 7.8.1
resolution: "rxjs@npm:7.8.1"
@@ -20617,6 +21803,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:7.6.3, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3":
+ version: 7.6.3
+ resolution: "semver@npm:7.6.3"
+ bin:
+ semver: bin/semver.js
+ checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
+ languageName: node
+ linkType: hard
+
"semver@npm:^5.3.0, semver@npm:^5.4.1, semver@npm:^5.7.2":
version: 5.7.2
resolution: "semver@npm:5.7.2"
@@ -20644,15 +21839,6 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3":
- version: 7.6.3
- resolution: "semver@npm:7.6.3"
- bin:
- semver: bin/semver.js
- checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
- languageName: node
- linkType: hard
-
"semver@npm:^7.3.4, semver@npm:^7.3.5":
version: 7.3.5
resolution: "semver@npm:7.3.5"
@@ -20765,6 +21951,16 @@ __metadata:
languageName: node
linkType: hard
+"set-value@npm:4.1.0":
+ version: 4.1.0
+ resolution: "set-value@npm:4.1.0"
+ dependencies:
+ is-plain-object: "npm:^2.0.4"
+ is-primitive: "npm:^3.0.1"
+ checksum: 10c0/dc186676b6cc0cfcf1656b8acdfe7a68591f0645dd2872250100817fb53e5e9298dc1727a95605ac03f82110e9b3820c90a0a02d84e0fb89f210922b08b37e02
+ languageName: node
+ linkType: hard
+
"setimmediate@npm:^1.0.5":
version: 1.0.5
resolution: "setimmediate@npm:1.0.5"
@@ -21031,6 +22227,15 @@ __metadata:
languageName: node
linkType: hard
+"sonic-boom@npm:^4.0.1":
+ version: 4.1.0
+ resolution: "sonic-boom@npm:4.1.0"
+ dependencies:
+ atomic-sleep: "npm:^1.0.0"
+ checksum: 10c0/4c9e082db296fbfb02e22a1a9b8de8b82f5965697dda3fe7feadc4759bf25d1de0094e3c35f16e015bfdc00fad7b8cf15bef5b0144501a2a5c5b86efb5684096
+ languageName: node
+ linkType: hard
+
"sort-keys@npm:^2.0.0":
version: 2.0.0
resolution: "sort-keys@npm:2.0.0"
@@ -21102,6 +22307,13 @@ __metadata:
languageName: node
linkType: hard
+"sourcemap-codec@npm:^1.4.8":
+ version: 1.4.8
+ resolution: "sourcemap-codec@npm:1.4.8"
+ checksum: 10c0/f099279fdaae070ff156df7414bbe39aad69cdd615454947ed3e19136bfdfcb4544952685ee73f56e17038f4578091e12b17b283ed8ac013882916594d95b9e6
+ languageName: node
+ linkType: hard
+
"spawn-sync@npm:1.0.15":
version: 1.0.15
resolution: "spawn-sync@npm:1.0.15"
@@ -21270,6 +22482,13 @@ __metadata:
languageName: node
linkType: hard
+"stream-buffers@npm:^0.2.3":
+ version: 0.2.6
+ resolution: "stream-buffers@npm:0.2.6"
+ checksum: 10c0/8d685a5f98e0b392802fc07617f31e6ae63652ed2fff7fe7df309222ffb06502f47b31ab35c2cf9b4de0320f657ed3aa6d697641f0f72f5c6f3a703ba8d7b594
+ languageName: node
+ linkType: hard
+
"stream-shift@npm:^1.0.2":
version: 1.0.3
resolution: "stream-shift@npm:1.0.3"
@@ -21317,7 +22536,7 @@ __metadata:
languageName: node
linkType: hard
-"string-width@npm:^7.0.0":
+"string-width@npm:^7.0.0, string-width@npm:^7.2.0":
version: 7.2.0
resolution: "string-width@npm:7.2.0"
dependencies:
@@ -21629,6 +22848,13 @@ __metadata:
languageName: node
linkType: hard
+"stubborn-fs@npm:^1.2.5":
+ version: 1.2.5
+ resolution: "stubborn-fs@npm:1.2.5"
+ checksum: 10c0/0676befd9901d4dd4e162700fa0396f11d523998589cd6b61b06d1021db811dc4c1e6713869748c6cfa49d58beb9b6f0dc5b6aca6b075811b949e1602ce1e26f
+ languageName: node
+ linkType: hard
+
"style-loader@npm:^4.0.0":
version: 4.0.0
resolution: "style-loader@npm:4.0.0"
@@ -22254,6 +23480,15 @@ __metadata:
languageName: node
linkType: hard
+"thread-stream@npm:^3.0.0":
+ version: 3.1.0
+ resolution: "thread-stream@npm:3.1.0"
+ dependencies:
+ real-require: "npm:^0.2.0"
+ checksum: 10c0/c36118379940b77a6ef3e6f4d5dd31e97b8210c3f7b9a54eb8fe6358ab173f6d0acfaf69b9c3db024b948c0c5fd2a7df93e2e49151af02076b35ada3205ec9a6
+ languageName: node
+ linkType: hard
+
"throttle-debounce@npm:^5.0.0":
version: 5.0.2
resolution: "throttle-debounce@npm:5.0.2"
@@ -22599,6 +23834,13 @@ __metadata:
languageName: node
linkType: hard
+"type-fest@npm:^4.18.2, type-fest@npm:^4.21.0":
+ version: 4.26.1
+ resolution: "type-fest@npm:4.26.1"
+ checksum: 10c0/d2719ff8d380befe8a3c61068f37f28d6fa2849fd140c5d2f0f143099e371da6856aad7c97e56b83329d45bfe504afe9fd936a7cff600cc0d46aa9ffb008d6c6
+ languageName: node
+ linkType: hard
+
"type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
@@ -22697,6 +23939,13 @@ __metadata:
languageName: node
linkType: hard
+"uberproto@npm:^1.1.0":
+ version: 1.2.0
+ resolution: "uberproto@npm:1.2.0"
+ checksum: 10c0/0071dbc7b3b71b4fedd4de5c914a6851df8ce11f6d98cf84ef8a1973afd8562027d111db97c047e2e42894bd5f99b24c6d07058d338d3204b3aea2c3c75421d2
+ languageName: node
+ linkType: hard
+
"uglify-js@npm:^3.1.4":
version: 3.14.2
resolution: "uglify-js@npm:3.14.2"
@@ -22706,6 +23955,13 @@ __metadata:
languageName: node
linkType: hard
+"uhyphen@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "uhyphen@npm:0.2.0"
+ checksum: 10c0/1e7129fe7a5c86445d1adf04d5c58913b5992e4899ea5553d9ddf6e7ef88af0f807a47f1bf9673b92f705276e5cf1b2c1d3852f1ab5d08ecac3382bcc3a642f9
+ languageName: node
+ linkType: hard
+
"unbox-primitive@npm:^1.0.1":
version: 1.0.1
resolution: "unbox-primitive@npm:1.0.1"
@@ -22922,6 +24178,24 @@ __metadata:
languageName: node
linkType: hard
+"update-notifier@npm:7.3.1":
+ version: 7.3.1
+ resolution: "update-notifier@npm:7.3.1"
+ dependencies:
+ boxen: "npm:^8.0.1"
+ chalk: "npm:^5.3.0"
+ configstore: "npm:^7.0.0"
+ is-in-ci: "npm:^1.0.0"
+ is-installed-globally: "npm:^1.0.0"
+ is-npm: "npm:^6.0.0"
+ latest-version: "npm:^9.0.0"
+ pupa: "npm:^3.1.0"
+ semver: "npm:^7.6.3"
+ xdg-basedir: "npm:^5.1.0"
+ checksum: 10c0/678839453840f46bb75e8cfebc0ff522262d2d3ece343fca722dd506039832e2a952d14ae39153f05f684467c8293ebc4c6479c9652c1bf97908fcaf300c2b31
+ languageName: node
+ linkType: hard
+
"uri-js@npm:^4.2.2, uri-js@npm:^4.4.1":
version: 4.4.1
resolution: "uri-js@npm:4.4.1"
@@ -23047,6 +24321,112 @@ __metadata:
languageName: node
linkType: hard
+"vite-plugin-web-extension@npm:^4.0.0, vite-plugin-web-extension@npm:^4.1.6":
+ version: 4.1.6
+ resolution: "vite-plugin-web-extension@npm:4.1.6"
+ dependencies:
+ ajv: "npm:^8.11.0"
+ async-lock: "npm:^1.3.2"
+ fs-extra: "npm:^10.1.0"
+ json5: "npm:^2.2.3"
+ linkedom: "npm:^0.14.21"
+ lodash.uniq: "npm:^4.5.0"
+ lodash.uniqby: "npm:^4.7.0"
+ md5: "npm:^2.3.0"
+ vite: "npm:^5.0.0 || ^4.1.4"
+ web-ext-run: "npm:^0.2.1"
+ webextension-polyfill: "npm:^0.10.0"
+ yaml: "npm:^2.3.4"
+ checksum: 10c0/740096eb29e47c3f24aa0a80efb778ca67649726d903741b8420016d4e154d581cd1ae4b962489b6b0b1242bc4eebe6a0a65b1669684c12c03fe77da80b7f4c2
+ languageName: node
+ linkType: hard
+
+"vite@npm:^5.0.0, vite@npm:^5.0.0 || ^4.1.4, vite@npm:^5.4.6":
+ version: 5.4.6
+ resolution: "vite@npm:5.4.6"
+ dependencies:
+ esbuild: "npm:^0.21.3"
+ fsevents: "npm:~2.3.3"
+ postcss: "npm:^8.4.43"
+ rollup: "npm:^4.20.0"
+ peerDependencies:
+ "@types/node": ^18.0.0 || >=20.0.0
+ less: "*"
+ lightningcss: ^1.21.0
+ sass: "*"
+ sass-embedded: "*"
+ stylus: "*"
+ sugarss: "*"
+ terser: ^5.4.0
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ bin:
+ vite: bin/vite.js
+ checksum: 10c0/5f87be3a10e970eaf9ac52dfab39cf9fff583036685252fb64570b6d7bfa749f6d221fb78058f5ef4b5664c180d45a8e7a7ff68d7f3770e69e24c7c68b958bde
+ languageName: node
+ linkType: hard
+
+"vite@npm:^5.4.1":
+ version: 5.4.5
+ resolution: "vite@npm:5.4.5"
+ dependencies:
+ esbuild: "npm:^0.21.3"
+ fsevents: "npm:~2.3.3"
+ postcss: "npm:^8.4.43"
+ rollup: "npm:^4.20.0"
+ peerDependencies:
+ "@types/node": ^18.0.0 || >=20.0.0
+ less: "*"
+ lightningcss: ^1.21.0
+ sass: "*"
+ sass-embedded: "*"
+ stylus: "*"
+ sugarss: "*"
+ terser: ^5.4.0
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ bin:
+ vite: bin/vite.js
+ checksum: 10c0/89c6459452fc238cdf8e99681b30996af171c9c557af476f96408a18a639fb5a0a6ee2d2257e005b21dc284edceb604595c34920cd4a007ad18f7ebafb654c76
+ languageName: node
+ linkType: hard
+
"w3c-xmlserializer@npm:^4.0.0":
version: 4.0.0
resolution: "w3c-xmlserializer@npm:4.0.0"
@@ -23082,7 +24462,7 @@ __metadata:
languageName: node
linkType: hard
-"watchpack@npm:^2.4.1":
+"watchpack@npm:2.4.2, watchpack@npm:^2.4.1":
version: 2.4.2
resolution: "watchpack@npm:2.4.2"
dependencies:
@@ -23110,6 +24490,38 @@ __metadata:
languageName: node
linkType: hard
+"web-ext-run@npm:^0.2.1":
+ version: 0.2.1
+ resolution: "web-ext-run@npm:0.2.1"
+ dependencies:
+ "@babel/runtime": "npm:7.24.7"
+ "@devicefarmer/adbkit": "npm:3.2.6"
+ bunyan: "npm:1.8.15"
+ chrome-launcher: "npm:1.1.0"
+ debounce: "npm:1.2.1"
+ es6-error: "npm:4.1.1"
+ firefox-profile: "npm:4.6.0"
+ fs-extra: "npm:11.2.0"
+ fx-runner: "npm:1.4.0"
+ mkdirp: "npm:3.0.1"
+ multimatch: "npm:6.0.0"
+ mz: "npm:2.7.0"
+ node-notifier: "npm:10.0.1"
+ parse-json: "npm:7.1.1"
+ promise-toolbox: "npm:0.21.0"
+ set-value: "npm:4.1.0"
+ source-map-support: "npm:0.5.21"
+ strip-bom: "npm:5.0.0"
+ strip-json-comments: "npm:5.0.1"
+ tmp: "npm:0.2.3"
+ update-notifier: "npm:6.0.2"
+ watchpack: "npm:2.4.1"
+ ws: "npm:8.18.0"
+ zip-dir: "npm:2.0.0"
+ checksum: 10c0/765aa8c2c3aad061d23e6bc30c063ffffd8cdf9b11ade0c29d77b373aaf49b4ceb693adb95941b04195f4e0b8a9442aa1b23a59dbf7560d703c02b728685fc37
+ languageName: node
+ linkType: hard
+
"web-ext@npm:^8.2.0":
version: 8.2.0
resolution: "web-ext@npm:8.2.0"
@@ -23152,6 +24564,44 @@ __metadata:
languageName: node
linkType: hard
+"web-ext@npm:^8.3.0":
+ version: 8.3.0
+ resolution: "web-ext@npm:8.3.0"
+ dependencies:
+ "@babel/runtime": "npm:7.25.6"
+ "@devicefarmer/adbkit": "npm:3.2.6"
+ addons-linter: "npm:7.1.0"
+ camelcase: "npm:8.0.0"
+ chrome-launcher: "npm:1.1.2"
+ debounce: "npm:1.2.1"
+ decamelize: "npm:6.0.0"
+ es6-error: "npm:4.1.1"
+ firefox-profile: "npm:4.7.0"
+ fx-runner: "npm:1.4.0"
+ https-proxy-agent: "npm:^7.0.0"
+ jose: "npm:5.9.2"
+ jszip: "npm:3.10.1"
+ multimatch: "npm:6.0.0"
+ node-notifier: "npm:10.0.1"
+ open: "npm:9.1.0"
+ parse-json: "npm:7.1.1"
+ pino: "npm:9.4.0"
+ promise-toolbox: "npm:0.21.0"
+ source-map-support: "npm:0.5.21"
+ strip-bom: "npm:5.0.0"
+ strip-json-comments: "npm:5.0.1"
+ tmp: "npm:0.2.3"
+ update-notifier: "npm:7.3.1"
+ watchpack: "npm:2.4.2"
+ ws: "npm:8.18.0"
+ yargs: "npm:17.7.2"
+ zip-dir: "npm:2.0.0"
+ bin:
+ web-ext: bin/web-ext.js
+ checksum: 10c0/a94bcdf0be255a8d642423c5db06d3f55403a63da04930ae38793a9f3866b79b989184f6ec11694f4e287a84b4fcdcacedfa71d29d0ef008e586d3f74097976e
+ languageName: node
+ linkType: hard
+
"web-streams-polyfill@npm:^3.0.3":
version: 3.2.0
resolution: "web-streams-polyfill@npm:3.2.0"
@@ -23159,6 +24609,34 @@ __metadata:
languageName: node
linkType: hard
+"webext@workspace:packages/webext":
+ version: 0.0.0-use.local
+ resolution: "webext@workspace:packages/webext"
+ dependencies:
+ "@carbon/ibm-products": "npm:^2.48.0"
+ "@carbon/ibm-security": "npm:^2.15.0"
+ "@carbon/ibmdotcom-utilities": "npm:^2.12.0"
+ "@carbon/ibmdotcom-web-components": "npm:^2.12.0"
+ "@carbon/icons-react": "npm:^10.49.5"
+ "@carbon/react": "npm:^1.65.0"
+ "@carbon/web-components": "npm:^2.12.0"
+ "@vitejs/plugin-react": "npm:^4.2.1"
+ react: "npm:^18.2.0"
+ react-dom: "npm:^18.2.0"
+ sass: "npm:^1.78.0"
+ vite: "npm:^5.0.0"
+ vite-plugin-web-extension: "npm:^4.0.0"
+ webextension-polyfill: "npm:^0.10.0"
+ languageName: unknown
+ linkType: soft
+
+"webextension-polyfill@npm:^0.10.0":
+ version: 0.10.0
+ resolution: "webextension-polyfill@npm:0.10.0"
+ checksum: 10c0/6a45278f1fed8fbd5355f9b19a7b0b3fadc91fa3a6eef69125a1706bb3efa2181235eefbfb3f538443bb396cfcb97512361551888ce8465c08914431cb2d5b6d
+ languageName: node
+ linkType: hard
+
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
@@ -23439,6 +24917,13 @@ __metadata:
languageName: node
linkType: hard
+"when-exit@npm:^2.1.1":
+ version: 2.1.3
+ resolution: "when-exit@npm:2.1.3"
+ checksum: 10c0/9b8f3bee4b0f8711a586d5b6241a1cd14d501a52c8138c26d425758552b3605b3785260b87cdd0bbc12e853d194b7a1f81598d789c8aa6f241debaccea99e777
+ languageName: node
+ linkType: hard
+
"when@npm:3.7.7":
version: 3.7.7
resolution: "when@npm:3.7.7"
@@ -23574,6 +25059,15 @@ __metadata:
languageName: node
linkType: hard
+"widest-line@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "widest-line@npm:5.0.0"
+ dependencies:
+ string-width: "npm:^7.0.0"
+ checksum: 10c0/6bd6cca8cda502ef50e05353fd25de0df8c704ffc43ada7e0a9cf9a5d4f4e12520485d80e0b77cec8a21f6c3909042fcf732aa9281e5dbb98cc9384a138b2578
+ languageName: node
+ linkType: hard
+
"wildcard@npm:^2.0.0":
version: 2.0.0
resolution: "wildcard@npm:2.0.0"
@@ -23747,33 +25241,33 @@ __metadata:
languageName: node
linkType: hard
-"ws@npm:^7.3.1":
- version: 7.5.10
- resolution: "ws@npm:7.5.10"
+"ws@npm:8.18.0, ws@npm:^8.13.0, ws@npm:^8.16.0":
+ version: 8.18.0
+ resolution: "ws@npm:8.18.0"
peerDependencies:
bufferutil: ^4.0.1
- utf-8-validate: ^5.0.2
+ utf-8-validate: ">=5.0.2"
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
- checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d
+ checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06
languageName: node
linkType: hard
-"ws@npm:^8.13.0, ws@npm:^8.16.0":
- version: 8.18.0
- resolution: "ws@npm:8.18.0"
+"ws@npm:^7.3.1":
+ version: 7.5.10
+ resolution: "ws@npm:7.5.10"
peerDependencies:
bufferutil: ^4.0.1
- utf-8-validate: ">=5.0.2"
+ utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
- checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06
+ checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d
languageName: node
linkType: hard
@@ -23801,6 +25295,16 @@ __metadata:
languageName: node
linkType: hard
+"xml2js@npm:^0.6.2":
+ version: 0.6.2
+ resolution: "xml2js@npm:0.6.2"
+ dependencies:
+ sax: "npm:>=0.6.0"
+ xmlbuilder: "npm:~11.0.0"
+ checksum: 10c0/e98a84e9c172c556ee2c5afa0fc7161b46919e8b53ab20de140eedea19903ed82f7cd5b1576fb345c84f0a18da1982ddf65908129b58fc3d7cbc658ae232108f
+ languageName: node
+ linkType: hard
+
"xmlbuilder@npm:~11.0.0":
version: 11.0.1
resolution: "xmlbuilder@npm:11.0.1"
@@ -23850,6 +25354,15 @@ __metadata:
languageName: node
linkType: hard
+"yaml@npm:^2.3.4":
+ version: 2.5.1
+ resolution: "yaml@npm:2.5.1"
+ bin:
+ yaml: bin.mjs
+ checksum: 10c0/40fba5682898dbeeb3319e358a968fe886509fab6f58725732a15f8dda3abac509f91e76817c708c9959a15f786f38ff863c1b88062d7c1162c5334a7d09cb4a
+ languageName: node
+ linkType: hard
+
"yaml@npm:~2.5.0":
version: 2.5.0
resolution: "yaml@npm:2.5.0"