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 += ``; + } + + 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

+
`; + + if (unique.length > 0) { + tooltipContent = tooltipContent.replace( + //g, + `${unique.length} ` + ); + } + } + + // manage dependencies + if (dependencies.length) { + unique = []; + tooltipContent += ` +
+

dependencies

+
`; + + 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 += ``; + } + + 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 += ``; + + 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 += `

    ${type}

    `; + } + + 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 += `

    ${eyebrow}

    `; + } + + if (title) { + groupsContent += `

    ${title}

    `; + } + + 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 ( + <> +
    +

    Carbon Devtools

    + +
    +
    +

    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"