nudge-ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/bin/nudge-ui.mjs +7 -0
- package/dist/LICENSE +21 -0
- package/dist/client.mjs +261 -0
- package/dist/compiler/ast.d.ts +18 -0
- package/dist/compiler/ast.js +75 -0
- package/dist/compiler/componentContractTypes.d.ts +19 -0
- package/dist/compiler/componentContractTypes.js +1 -0
- package/dist/compiler/componentContracts.d.ts +2 -0
- package/dist/compiler/componentContracts.js +260 -0
- package/dist/compiler/componentInstrumentation.d.ts +32 -0
- package/dist/compiler/componentInstrumentation.js +140 -0
- package/dist/compiler/componentPolicyResolution.d.ts +71 -0
- package/dist/compiler/componentPolicyResolution.js +388 -0
- package/dist/compiler/index.d.ts +9 -0
- package/dist/compiler/index.js +9 -0
- package/dist/compiler/packageComponentContracts.d.ts +32 -0
- package/dist/compiler/packageComponentContracts.js +308 -0
- package/dist/compiler/reactComponentProtocols.d.ts +145 -0
- package/dist/compiler/reactComponentProtocols.js +47 -0
- package/dist/compiler/reactIdentity.d.ts +15 -0
- package/dist/compiler/reactIdentity.js +339 -0
- package/dist/compiler/sourcePaths.d.ts +12 -0
- package/dist/compiler/sourcePaths.js +27 -0
- package/dist/css/dialects/index.d.ts +62 -0
- package/dist/css/dialects/index.js +103 -0
- package/dist/css/dialects/tailwind.d.ts +46 -0
- package/dist/css/dialects/tailwind.js +119 -0
- package/dist/css/dialects/vanillaExtract.d.ts +20 -0
- package/dist/css/dialects/vanillaExtract.js +32 -0
- package/dist/css/index.d.ts +10 -0
- package/dist/css/index.js +10 -0
- package/dist/css/model/index.d.ts +190 -0
- package/dist/css/model/index.js +10 -0
- package/dist/css/token-inventory/index.d.ts +26 -0
- package/dist/css/token-inventory/index.js +24 -0
- package/dist/css/token-inventory/inventory.d.ts +8 -0
- package/dist/css/token-inventory/inventory.js +421 -0
- package/dist/css/token-inventory/parseStylesheet.d.ts +16 -0
- package/dist/css/token-inventory/parseStylesheet.js +155 -0
- package/dist/css/token-inventory/policy.d.ts +47 -0
- package/dist/css/token-inventory/policy.js +50 -0
- package/dist/css/token-inventory/types.d.ts +97 -0
- package/dist/css/token-inventory/types.js +1 -0
- package/dist/css/value-semantics/borderSemantics.d.ts +28 -0
- package/dist/css/value-semantics/borderSemantics.js +91 -0
- package/dist/css/value-semantics/boxSemantics.d.ts +37 -0
- package/dist/css/value-semantics/boxSemantics.js +71 -0
- package/dist/css/value-semantics/colorSemantics.d.ts +99 -0
- package/dist/css/value-semantics/colorSemantics.js +526 -0
- package/dist/css/value-semantics/cssSyntax.d.ts +19 -0
- package/dist/css/value-semantics/cssSyntax.js +111 -0
- package/dist/css/value-semantics/fontSemantics.d.ts +35 -0
- package/dist/css/value-semantics/fontSemantics.js +131 -0
- package/dist/css/value-semantics/index.d.ts +20 -0
- package/dist/css/value-semantics/index.js +15 -0
- package/dist/css/value-semantics/propertyPolicy.d.ts +118 -0
- package/dist/css/value-semantics/propertyPolicy.js +356 -0
- package/dist/css/value-semantics/stringMaps.d.ts +2 -0
- package/dist/css/value-semantics/stringMaps.js +1 -0
- package/dist/css/value-semantics/structuredValues.d.ts +64 -0
- package/dist/css/value-semantics/structuredValues.js +250 -0
- package/dist/css/value-semantics/tokenInterpretation.d.ts +90 -0
- package/dist/css/value-semantics/tokenInterpretation.js +193 -0
- package/dist/css/virtual-design-tokens.d.ts +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.d.ts +12 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +26 -0
- package/dist/hosts/astro/clientAsset.d.ts +12 -0
- package/dist/hosts/astro/clientAsset.js +51 -0
- package/dist/hosts/astro/clientTransport.d.ts +3 -0
- package/dist/hosts/astro/clientTransport.js +57 -0
- package/dist/hosts/astro/index.d.ts +3 -0
- package/dist/hosts/astro/index.js +2 -0
- package/dist/hosts/astro/integration.d.ts +35 -0
- package/dist/hosts/astro/integration.js +96 -0
- package/dist/hosts/astro/middleware.d.ts +13 -0
- package/dist/hosts/astro/middleware.js +22 -0
- package/dist/hosts/astro/projectContext.d.ts +11 -0
- package/dist/hosts/astro/projectContext.js +34 -0
- package/dist/hosts/astro/responseInstrumentation.d.ts +41 -0
- package/dist/hosts/astro/responseInstrumentation.js +80 -0
- package/dist/hosts/next/index.d.ts +4 -0
- package/dist/hosts/next/index.js +7 -0
- package/dist/hosts/next/loader.d.ts +66 -0
- package/dist/hosts/next/loader.js +346 -0
- package/dist/hosts/next/loaders/identity-loader.cjs +228257 -0
- package/dist/hosts/next/loaders/loader-plugin.cjs +228257 -0
- package/dist/hosts/next/manifest.d.ts +38 -0
- package/dist/hosts/next/manifest.js +30 -0
- package/dist/hosts/next/mount.d.ts +12 -0
- package/dist/hosts/next/mount.js +23 -0
- package/dist/hosts/next/repositoryScope.d.ts +20 -0
- package/dist/hosts/next/repositoryScope.js +33 -0
- package/dist/hosts/next/sidecar.d.ts +27 -0
- package/dist/hosts/next/sidecar.js +526 -0
- package/dist/hosts/next/wrapper.d.ts +67 -0
- package/dist/hosts/next/wrapper.js +310 -0
- package/dist/hosts/static/cli.d.ts +12 -0
- package/dist/hosts/static/cli.js +77 -0
- package/dist/hosts/static/html/bootstrap.d.ts +27 -0
- package/dist/hosts/static/html/bootstrap.js +93 -0
- package/dist/hosts/static/index.d.ts +4 -0
- package/dist/hosts/static/index.js +4 -0
- package/dist/hosts/static/manifest.d.ts +18 -0
- package/dist/hosts/static/manifest.js +51 -0
- package/dist/hosts/static/server.d.ts +72 -0
- package/dist/hosts/static/server.js +554 -0
- package/dist/hosts/vite/index.d.ts +20 -0
- package/dist/hosts/vite/index.js +48 -0
- package/dist/hosts/vite/react.d.ts +67 -0
- package/dist/hosts/vite/react.js +231 -0
- package/dist/hosts/vite/tokens/activeStylesheets.d.ts +28 -0
- package/dist/hosts/vite/tokens/activeStylesheets.js +70 -0
- package/dist/hosts/vite/tokens/fileMap.d.ts +2 -0
- package/dist/hosts/vite/tokens/fileMap.js +1 -0
- package/dist/hosts/vite/tokens/viteStylesheetArtifacts.d.ts +46 -0
- package/dist/hosts/vite/tokens/viteStylesheetArtifacts.js +104 -0
- package/dist/hosts/vite/virtual/design-tokens.d.ts +9 -0
- package/dist/hosts/vite/virtual/design-tokens.js +7 -0
- package/dist/hosts/vite/virtual-design-tokens.d.ts +1 -0
- package/dist/hosts/vite/vite.d.ts +62 -0
- package/dist/hosts/vite/vite.js +854 -0
- package/dist/html/dom.d.ts +13 -0
- package/dist/html/dom.js +46 -0
- package/dist/html/identity.d.ts +40 -0
- package/dist/html/identity.js +196 -0
- package/dist/html/insertions.d.ts +26 -0
- package/dist/html/insertions.js +59 -0
- package/dist/inspector/agent/McpConnectionDialog.d.ts +26 -0
- package/dist/inspector/agent/McpConnectionDialog.js +135 -0
- package/dist/inspector/agent/client.d.ts +98 -0
- package/dist/inspector/agent/client.js +728 -0
- package/dist/inspector/agent/connectionStatus.d.ts +13 -0
- package/dist/inspector/agent/connectionStatus.js +35 -0
- package/dist/inspector/agent/httpTransport.d.ts +21 -0
- package/dist/inspector/agent/httpTransport.js +347 -0
- package/dist/inspector/agent/protocol.d.ts +95 -0
- package/dist/inspector/agent/protocol.js +9 -0
- package/dist/inspector/agent/verification.d.ts +39 -0
- package/dist/inspector/agent/verification.js +207 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +10 -0
- package/dist/inspector/canvas/CanvasCard.js +275 -0
- package/dist/inspector/canvas/CanvasElementOverlay.d.ts +2 -0
- package/dist/inspector/canvas/CanvasElementOverlay.js +301 -0
- package/dist/inspector/canvas/CanvasWorkspace.d.ts +2 -0
- package/dist/inspector/canvas/CanvasWorkspace.js +301 -0
- package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +6 -0
- package/dist/inspector/canvas/LockedWorkspaceNotice.js +17 -0
- package/dist/inspector/canvas/StaleChangeIndicator.d.ts +7 -0
- package/dist/inspector/canvas/StaleChangeIndicator.js +43 -0
- package/dist/inspector/canvas/agentPresentation.d.ts +87 -0
- package/dist/inspector/canvas/agentPresentation.js +421 -0
- package/dist/inspector/canvas/canvasGestures.d.ts +12 -0
- package/dist/inspector/canvas/canvasGestures.js +21 -0
- package/dist/inspector/canvas/canvasStore.d.ts +108 -0
- package/dist/inspector/canvas/canvasStore.js +448 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +231 -0
- package/dist/inspector/canvas/frameProtocol.js +165 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +8 -0
- package/dist/inspector/canvas/linkEligibility.js +68 -0
- package/dist/inspector/canvas/measurementProjection.d.ts +8 -0
- package/dist/inspector/canvas/measurementProjection.js +16 -0
- package/dist/inspector/canvas/normalizeUrl.d.ts +7 -0
- package/dist/inspector/canvas/normalizeUrl.js +27 -0
- package/dist/inspector/canvas/projection.d.ts +41 -0
- package/dist/inspector/canvas/projection.js +232 -0
- package/dist/inspector/canvas/rendererBootstrap.d.ts +4 -0
- package/dist/inspector/canvas/rendererBootstrap.js +410 -0
- package/dist/inspector/canvas/rendererCidIndex.d.ts +11 -0
- package/dist/inspector/canvas/rendererCidIndex.js +21 -0
- package/dist/inspector/canvas/rendererElementSelector.d.ts +2 -0
- package/dist/inspector/canvas/rendererElementSelector.js +378 -0
- package/dist/inspector/canvas/rendererSelectionProxy.d.ts +2 -0
- package/dist/inspector/canvas/rendererSelectionProxy.js +61 -0
- package/dist/inspector/canvas/rendererStylesheet.d.ts +16 -0
- package/dist/inspector/canvas/rendererStylesheet.js +221 -0
- package/dist/inspector/canvas/resizeHandleScale.d.ts +13 -0
- package/dist/inspector/canvas/resizeHandleScale.js +20 -0
- package/dist/inspector/canvas/roleDetection.d.ts +5 -0
- package/dist/inspector/canvas/roleDetection.js +21 -0
- package/dist/inspector/canvas/sessionStore.d.ts +71 -0
- package/dist/inspector/canvas/sessionStore.js +453 -0
- package/dist/inspector/canvas/staleChangeDetector.d.ts +11 -0
- package/dist/inspector/canvas/staleChangeDetector.js +217 -0
- package/dist/inspector/canvas/toolbarScale.d.ts +7 -0
- package/dist/inspector/canvas/toolbarScale.js +11 -0
- package/dist/inspector/canvas/workspaceLease.d.ts +26 -0
- package/dist/inspector/canvas/workspaceLease.js +214 -0
- package/dist/inspector/changes/changesLog.d.ts +46 -0
- package/dist/inspector/changes/changesLog.js +209 -0
- package/dist/inspector/changes/codecs.d.ts +98 -0
- package/dist/inspector/changes/codecs.js +389 -0
- package/dist/inspector/changes/editModel.d.ts +74 -0
- package/dist/inspector/changes/editModel.js +120 -0
- package/dist/inspector/changes/managedStyleProjection.d.ts +6 -0
- package/dist/inspector/changes/managedStyleProjection.js +109 -0
- package/dist/inspector/changes/model.d.ts +13 -0
- package/dist/inspector/changes/model.js +163 -0
- package/dist/inspector/changes/presentation.d.ts +14 -0
- package/dist/inspector/changes/presentation.js +81 -0
- package/dist/inspector/changes/previewDiagnostics.d.ts +39 -0
- package/dist/inspector/changes/previewDiagnostics.js +172 -0
- package/dist/inspector/changes/structuralTypes.d.ts +35 -0
- package/dist/inspector/changes/structuralTypes.js +1 -0
- package/dist/inspector/changes/types.d.ts +76 -0
- package/dist/inspector/changes/types.js +16 -0
- package/dist/inspector/changes/workspaceChanges.d.ts +43 -0
- package/dist/inspector/changes/workspaceChanges.js +239 -0
- package/dist/inspector/clientManifest.d.ts +24 -0
- package/dist/inspector/clientManifest.js +69 -0
- package/dist/inspector/componentSemantics/ComponentPropsSection.d.ts +5 -0
- package/dist/inspector/componentSemantics/ComponentPropsSection.js +44 -0
- package/dist/inspector/componentSemantics/adapterRegistry.d.ts +11 -0
- package/dist/inspector/componentSemantics/adapterRegistry.js +75 -0
- package/dist/inspector/componentSemantics/boundaries.d.ts +2 -0
- package/dist/inspector/componentSemantics/boundaries.js +10 -0
- package/dist/inspector/componentSemantics/changeAction.d.ts +3 -0
- package/dist/inspector/componentSemantics/changeAction.js +6 -0
- package/dist/inspector/componentSemantics/changeModel.d.ts +8 -0
- package/dist/inspector/componentSemantics/changeModel.js +60 -0
- package/dist/inspector/componentSemantics/index.d.ts +4 -0
- package/dist/inspector/componentSemantics/index.js +2 -0
- package/dist/inspector/componentSemantics/reactRuntime.d.ts +10 -0
- package/dist/inspector/componentSemantics/reactRuntime.js +186 -0
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +25 -0
- package/dist/inspector/componentSemantics/runtimeBridge.js +161 -0
- package/dist/inspector/componentSemantics/runtimeTypes.d.ts +2 -0
- package/dist/inspector/componentSemantics/runtimeTypes.js +1 -0
- package/dist/inspector/componentSemantics/textBinding.d.ts +69 -0
- package/dist/inspector/componentSemantics/textBinding.js +514 -0
- package/dist/inspector/componentSemantics/types.d.ts +96 -0
- package/dist/inspector/componentSemantics/types.js +5 -0
- package/dist/inspector/conformance/borderCases.d.ts +7 -0
- package/dist/inspector/conformance/borderCases.js +428 -0
- package/dist/inspector/conformance/colorCases.d.ts +8 -0
- package/dist/inspector/conformance/colorCases.js +470 -0
- package/dist/inspector/conformance/fixture.d.ts +68 -0
- package/dist/inspector/conformance/fixture.js +153 -0
- package/dist/inspector/conformance/projection.d.ts +2 -0
- package/dist/inspector/conformance/projection.js +3 -0
- package/dist/inspector/conformance/spacingCases.d.ts +6 -0
- package/dist/inspector/conformance/spacingCases.js +417 -0
- package/dist/inspector/conformance/typographyCases.d.ts +7 -0
- package/dist/inspector/conformance/typographyCases.js +174 -0
- package/dist/inspector/editScope.d.ts +4 -0
- package/dist/inspector/editScope.js +5 -0
- package/dist/inspector/index.d.ts +20 -0
- package/dist/inspector/index.js +236 -0
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +74 -0
- package/dist/inspector/inline-text/inlineTextEditor.js +1207 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.d.ts +2 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.js +17 -0
- package/dist/inspector/inline-text/textChangeBoundary.d.ts +6 -0
- package/dist/inspector/inline-text/textChangeBoundary.js +1 -0
- package/dist/inspector/inspection/bridge.d.ts +72 -0
- package/dist/inspector/inspection/bridge.js +134 -0
- package/dist/inspector/inspection/browserCssInspection.d.ts +74 -0
- package/dist/inspector/inspection/browserCssInspection.js +317 -0
- package/dist/inspector/inspection/browserCssInspectionRegistry.d.ts +12 -0
- package/dist/inspector/inspection/browserCssInspectionRegistry.js +66 -0
- package/dist/inspector/inspection/selectionProperty.d.ts +32 -0
- package/dist/inspector/inspection/selectionProperty.js +33 -0
- package/dist/inspector/inspection/useBrowserCssInspection.d.ts +20 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +159 -0
- package/dist/inspector/overlay/DropGuideOverlay.d.ts +17 -0
- package/dist/inspector/overlay/DropGuideOverlay.js +17 -0
- package/dist/inspector/overlay/InspectorOverlay.d.ts +5 -0
- package/dist/inspector/overlay/InspectorOverlay.js +328 -0
- package/dist/inspector/overlay/MeasurementGuideOverlay.d.ts +19 -0
- package/dist/inspector/overlay/MeasurementGuideOverlay.js +40 -0
- package/dist/inspector/overlay/clickPolicy.d.ts +7 -0
- package/dist/inspector/overlay/clickPolicy.js +13 -0
- package/dist/inspector/overlay/dropGuide.d.ts +22 -0
- package/dist/inspector/overlay/dropGuide.js +48 -0
- package/dist/inspector/overlay/elementSelector.d.ts +1 -0
- package/dist/inspector/overlay/elementSelector.js +100 -0
- package/dist/inspector/overlay/frameThrottle.d.ts +10 -0
- package/dist/inspector/overlay/frameThrottle.js +38 -0
- package/dist/inspector/overlay/interactionStyles.d.ts +3 -0
- package/dist/inspector/overlay/interactionStyles.js +70 -0
- package/dist/inspector/overlay/measurementGeometry.d.ts +25 -0
- package/dist/inspector/overlay/measurementGeometry.js +104 -0
- package/dist/inspector/overlay/overlayGeometry.d.ts +40 -0
- package/dist/inspector/overlay/overlayGeometry.js +59 -0
- package/dist/inspector/overlay/selectedGeometry.d.ts +7 -0
- package/dist/inspector/overlay/selectedGeometry.js +51 -0
- package/dist/inspector/overlay/structuralGestures.d.ts +28 -0
- package/dist/inspector/overlay/structuralGestures.js +180 -0
- package/dist/inspector/projection/cssEscapes.d.ts +3 -0
- package/dist/inspector/projection/cssEscapes.js +20 -0
- package/dist/inspector/projection/managedStylesheet.d.ts +33 -0
- package/dist/inspector/projection/managedStylesheet.js +240 -0
- package/dist/inspector/projection/renderedInstance.d.ts +56 -0
- package/dist/inspector/projection/renderedInstance.js +306 -0
- package/dist/inspector/projection/structuralProjection.d.ts +93 -0
- package/dist/inspector/projection/structuralProjection.js +560 -0
- package/dist/inspector/projection/structuralProjectionBoundary.d.ts +5 -0
- package/dist/inspector/projection/structuralProjectionBoundary.js +65 -0
- package/dist/inspector/projection/textProjection.d.ts +53 -0
- package/dist/inspector/projection/textProjection.js +705 -0
- package/dist/inspector/projection/workspaceProjection.d.ts +43 -0
- package/dist/inspector/projection/workspaceProjection.js +58 -0
- package/dist/inspector/prompt/PromptSettingsDialog.d.ts +15 -0
- package/dist/inspector/prompt/PromptSettingsDialog.js +17 -0
- package/dist/inspector/prompt/clipboardHandoff.d.ts +34 -0
- package/dist/inspector/prompt/clipboardHandoff.js +212 -0
- package/dist/inspector/prompt/copyToClipboard.d.ts +1 -0
- package/dist/inspector/prompt/copyToClipboard.js +34 -0
- package/dist/inspector/prompt/detectFramework.d.ts +6 -0
- package/dist/inspector/prompt/detectFramework.js +4 -0
- package/dist/inspector/prompt/generatePrompt.d.ts +7 -0
- package/dist/inspector/prompt/generatePrompt.js +391 -0
- package/dist/inspector/prompt/promptSettings.d.ts +8 -0
- package/dist/inspector/prompt/promptSettings.js +28 -0
- package/dist/inspector/runtime/devFlag.d.ts +28 -0
- package/dist/inspector/runtime/devFlag.js +50 -0
- package/dist/inspector/runtime/documentStylesheetOrder.d.ts +19 -0
- package/dist/inspector/runtime/documentStylesheetOrder.js +89 -0
- package/dist/inspector/runtime/domRealm.d.ts +4 -0
- package/dist/inspector/runtime/domRealm.js +11 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +119 -0
- package/dist/inspector/runtime/runtimeConfig.js +276 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +32 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +149 -0
- package/dist/inspector/runtime/useRuntimeConfig.d.ts +10 -0
- package/dist/inspector/runtime/useRuntimeConfig.js +13 -0
- package/dist/inspector/selection/editScope.d.ts +32 -0
- package/dist/inspector/selection/editScope.js +140 -0
- package/dist/inspector/selection/editTarget.d.ts +8 -0
- package/dist/inspector/selection/editTarget.js +11 -0
- package/dist/inspector/selection/hierarchy.d.ts +7 -0
- package/dist/inspector/selection/hierarchy.js +42 -0
- package/dist/inspector/selection/resolveSelection.d.ts +10 -0
- package/dist/inspector/selection/resolveSelection.js +60 -0
- package/dist/inspector/selection/selectionStore.d.ts +41 -0
- package/dist/inspector/selection/selectionStore.js +262 -0
- package/dist/inspector/selection/selectionTarget.d.ts +5 -0
- package/dist/inspector/selection/selectionTarget.js +50 -0
- package/dist/inspector/selection/sourceSite.d.ts +2 -0
- package/dist/inspector/selection/sourceSite.js +12 -0
- package/dist/inspector/selection/styleSelection.d.ts +22 -0
- package/dist/inspector/selection/styleSelection.js +104 -0
- package/dist/inspector/session/index.d.ts +3 -0
- package/dist/inspector/session/index.js +2 -0
- package/dist/inspector/session/lifecycle.d.ts +22 -0
- package/dist/inspector/session/lifecycle.js +63 -0
- package/dist/inspector/session/sessionContext.d.ts +21 -0
- package/dist/inspector/session/sessionContext.js +28 -0
- package/dist/inspector/session/sessionFactory.d.ts +29 -0
- package/dist/inspector/session/sessionFactory.js +73 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +12 -0
- package/dist/inspector/settings/SettingsDialog.js +46 -0
- package/dist/inspector/shell/AppShell.d.ts +2 -0
- package/dist/inspector/shell/AppShell.js +9 -0
- package/dist/inspector/shell/ChangesLog.d.ts +6 -0
- package/dist/inspector/shell/ChangesLog.js +92 -0
- package/dist/inspector/shell/CopyPromptButton.d.ts +9 -0
- package/dist/inspector/shell/CopyPromptButton.js +159 -0
- package/dist/inspector/shell/DomNavigation.d.ts +8 -0
- package/dist/inspector/shell/DomNavigation.js +30 -0
- package/dist/inspector/shell/EmptyState.d.ts +2 -0
- package/dist/inspector/shell/EmptyState.js +15 -0
- package/dist/inspector/shell/InspectorShell.d.ts +11 -0
- package/dist/inspector/shell/InspectorShell.js +268 -0
- package/dist/inspector/shell/openStore.d.ts +6 -0
- package/dist/inspector/shell/openStore.js +29 -0
- package/dist/inspector/shell/panelLayout.d.ts +2 -0
- package/dist/inspector/shell/panelLayout.js +29 -0
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +25 -0
- package/dist/inspector/shell/stateValue.d.ts +3 -0
- package/dist/inspector/shell/stateValue.js +34 -0
- package/dist/inspector/shell/styleState.d.ts +7 -0
- package/dist/inspector/shell/styleState.js +13 -0
- package/dist/inspector/spacing/projection.d.ts +43 -0
- package/dist/inspector/spacing/projection.js +136 -0
- package/dist/inspector/styleEditors/AppearanceSection.d.ts +13 -0
- package/dist/inspector/styleEditors/AppearanceSection.js +6 -0
- package/dist/inspector/styleEditors/AspectRatioField.d.ts +17 -0
- package/dist/inspector/styleEditors/AspectRatioField.js +10 -0
- package/dist/inspector/styleEditors/BorderEditor.d.ts +13 -0
- package/dist/inspector/styleEditors/BorderEditor.js +295 -0
- package/dist/inspector/styleEditors/BorderRadiusEditor.d.ts +14 -0
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +108 -0
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +13 -0
- package/dist/inspector/styleEditors/BoxShadowEditor.js +13 -0
- package/dist/inspector/styleEditors/ColorPicker.d.ts +15 -0
- package/dist/inspector/styleEditors/ColorPicker.js +73 -0
- package/dist/inspector/styleEditors/GapField.d.ts +17 -0
- package/dist/inspector/styleEditors/GapField.js +12 -0
- package/dist/inspector/styleEditors/GridChildSection.d.ts +14 -0
- package/dist/inspector/styleEditors/GridChildSection.js +91 -0
- package/dist/inspector/styleEditors/GridPicker.d.ts +13 -0
- package/dist/inspector/styleEditors/GridPicker.js +179 -0
- package/dist/inspector/styleEditors/GridSection.d.ts +17 -0
- package/dist/inspector/styleEditors/GridSection.js +27 -0
- package/dist/inspector/styleEditors/GridValueField.d.ts +17 -0
- package/dist/inspector/styleEditors/GridValueField.js +70 -0
- package/dist/inspector/styleEditors/InsetSection.d.ts +15 -0
- package/dist/inspector/styleEditors/InsetSection.js +15 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +6 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +6 -0
- package/dist/inspector/styleEditors/LayoutComboField.d.ts +18 -0
- package/dist/inspector/styleEditors/LayoutComboField.js +138 -0
- package/dist/inspector/styleEditors/LayoutDropdown.d.ts +14 -0
- package/dist/inspector/styleEditors/LayoutDropdown.js +34 -0
- package/dist/inspector/styleEditors/LayoutSection.d.ts +13 -0
- package/dist/inspector/styleEditors/LayoutSection.js +342 -0
- package/dist/inspector/styleEditors/OpacityEditor.d.ts +13 -0
- package/dist/inspector/styleEditors/OpacityEditor.js +34 -0
- package/dist/inspector/styleEditors/PositionInsets.d.ts +15 -0
- package/dist/inspector/styleEditors/PositionInsets.js +12 -0
- package/dist/inspector/styleEditors/SpacingBox.d.ts +29 -0
- package/dist/inspector/styleEditors/SpacingBox.js +189 -0
- package/dist/inspector/styleEditors/Typography.d.ts +13 -0
- package/dist/inspector/styleEditors/Typography.js +137 -0
- package/dist/inspector/styleEditors/completeCssValue.d.ts +8 -0
- package/dist/inspector/styleEditors/completeCssValue.js +34 -0
- package/dist/inspector/styleEditors/gridChildModel.d.ts +74 -0
- package/dist/inspector/styleEditors/gridChildModel.js +138 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +24 -0
- package/dist/inspector/styleEditors/layoutValue.js +103 -0
- package/dist/inspector/styleEditors/nudgeValue.d.ts +8 -0
- package/dist/inspector/styleEditors/nudgeValue.js +71 -0
- package/dist/inspector/styleEditors/rowLookup.d.ts +11 -0
- package/dist/inspector/styleEditors/rowLookup.js +13 -0
- package/dist/inspector/styleEditors/stringRecord.d.ts +4 -0
- package/dist/inspector/styleEditors/stringRecord.js +1 -0
- package/dist/inspector/styleEditors/valuePolicy.d.ts +20 -0
- package/dist/inspector/styleEditors/valuePolicy.js +82 -0
- package/dist/inspector/testing.d.ts +10 -0
- package/dist/inspector/testing.js +9 -0
- package/dist/inspector/tokens/TokenChip.d.ts +24 -0
- package/dist/inspector/tokens/TokenChip.js +23 -0
- package/dist/inspector/tokens/TokenDropdown.d.ts +13 -0
- package/dist/inspector/tokens/TokenDropdown.js +59 -0
- package/dist/inspector/tokens/TokenField.d.ts +68 -0
- package/dist/inspector/tokens/TokenField.js +485 -0
- package/dist/inspector/tokens/TokenLabel.d.ts +7 -0
- package/dist/inspector/tokens/TokenLabel.js +23 -0
- package/dist/inspector/tokens/TokensPanel.d.ts +5 -0
- package/dist/inspector/tokens/TokensPanel.js +47 -0
- package/dist/inspector/tokens/catalog.d.ts +51 -0
- package/dist/inspector/tokens/catalog.js +184 -0
- package/dist/inspector/tokens/editActions.d.ts +35 -0
- package/dist/inspector/tokens/editActions.js +164 -0
- package/dist/inspector/tokens/resolution/cascade.d.ts +14 -0
- package/dist/inspector/tokens/resolution/cascade.js +31 -0
- package/dist/inspector/tokens/resolution/cssomCollector.d.ts +43 -0
- package/dist/inspector/tokens/resolution/cssomCollector.js +528 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +12 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.js +106 -0
- package/dist/inspector/tokens/resolution.d.ts +56 -0
- package/dist/inspector/tokens/resolution.js +1401 -0
- package/dist/inspector/tokens/tokenEdits.d.ts +3 -0
- package/dist/inspector/tokens/tokenEdits.js +22 -0
- package/dist/inspector/tokens/valueSemanticsAdapter.d.ts +6 -0
- package/dist/inspector/tokens/valueSemanticsAdapter.js +68 -0
- package/dist/inspector/ui/AtRuleContext.d.ts +18 -0
- package/dist/inspector/ui/AtRuleContext.js +54 -0
- package/dist/inspector/ui/Button.d.ts +9 -0
- package/dist/inspector/ui/Button.js +4 -0
- package/dist/inspector/ui/ColorSwatch.d.ts +7 -0
- package/dist/inspector/ui/ColorSwatch.js +6 -0
- package/dist/inspector/ui/ControlSurface.d.ts +14 -0
- package/dist/inspector/ui/ControlSurface.js +10 -0
- package/dist/inspector/ui/FieldRow.d.ts +14 -0
- package/dist/inspector/ui/FieldRow.js +15 -0
- package/dist/inspector/ui/IconButton.d.ts +14 -0
- package/dist/inspector/ui/IconButton.js +5 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +17 -0
- package/dist/inspector/ui/InspectorPopover.js +13 -0
- package/dist/inspector/ui/PopoverListbox.d.ts +37 -0
- package/dist/inspector/ui/PopoverListbox.js +21 -0
- package/dist/inspector/ui/SegmentedControl.d.ts +24 -0
- package/dist/inspector/ui/SegmentedControl.js +16 -0
- package/dist/inspector/ui/Select.d.ts +25 -0
- package/dist/inspector/ui/Select.js +56 -0
- package/dist/inspector/ui/SideValuesField.d.ts +44 -0
- package/dist/inspector/ui/SideValuesField.js +76 -0
- package/dist/inspector/ui/StatusCallout.d.ts +9 -0
- package/dist/inspector/ui/StatusCallout.js +4 -0
- package/dist/inspector/ui/TextInput.d.ts +12 -0
- package/dist/inspector/ui/TextInput.js +5 -0
- package/dist/inspector/ui/ToggleButton.d.ts +11 -0
- package/dist/inspector/ui/ToggleButton.js +6 -0
- package/dist/inspector/ui/assets/grid.svg +14 -0
- package/dist/inspector/ui/css.d.ts +9 -0
- package/dist/inspector/ui/labels.d.ts +1 -0
- package/dist/inspector/ui/labels.js +7 -0
- package/dist/inspector/ui/styles.d.ts +1 -0
- package/dist/inspector/ui/styles.js +68 -0
- package/dist/inspector/virtual-modules.d.ts +7 -0
- package/dist/inspector/vite-env.d.ts +10 -0
- package/dist/internal/agent-protocol.d.ts +232 -0
- package/dist/internal/agent-protocol.js +237 -0
- package/dist/nudge-ui.mjs +15722 -0
- package/dist/project/files.d.ts +19 -0
- package/dist/project/files.js +226 -0
- package/dist/project/identity.d.ts +6 -0
- package/dist/project/identity.js +19 -0
- package/dist/project/pathPolicy.d.ts +8 -0
- package/dist/project/pathPolicy.js +12 -0
- package/dist/project/tokens.d.ts +36 -0
- package/dist/project/tokens.js +223 -0
- package/dist/transport/index.d.ts +8 -0
- package/dist/transport/index.js +8 -0
- package/dist/transport/routes.d.ts +13 -0
- package/dist/transport/routes.js +16 -0
- package/package.json +137 -0
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
import { readTailwindV4AlphaUtility, tailwindV4AlphaExpression } from "../../css/dialects/index.js";
|
|
2
|
+
import { INTERACTION_STATES } from "../shell/styleState.js";
|
|
3
|
+
import { getElementComputedStyle } from "../runtime/domRealm.js";
|
|
4
|
+
import { collectRules as collectCssomRules, documentRevisions as getDocumentRevisions, registerResolutionElement, } from "./resolution/cssomCollector.js";
|
|
5
|
+
import { computeSpecificity } from "./resolution/selectorSemantics.js";
|
|
6
|
+
import { compareAuthorCascade } from "./resolution/cascade.js";
|
|
7
|
+
export { invalidateStyleResolutionCache } from "./resolution/cssomCollector.js";
|
|
8
|
+
import { interpretValue, normalizeOpacityPercent, } from "../../css/value-semantics/index.js";
|
|
9
|
+
import { createInspectorValueContext, inspectorTokenOrigin } from "./valueSemanticsAdapter.js";
|
|
10
|
+
import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
11
|
+
/**
|
|
12
|
+
* Builds (or reuses) the document's CSSOM rule snapshot ahead of a resolution
|
|
13
|
+
* sweep. Idempotent: the walk runs once per stylesheet revision and is a cache
|
|
14
|
+
* hit afterwards, so a scheduler can warm it in its own frame to keep the
|
|
15
|
+
* subsequent resolution sweep's blocking time small.
|
|
16
|
+
*/
|
|
17
|
+
export function prewarmCssomRuleSnapshot(doc) {
|
|
18
|
+
collectCssomRules(doc);
|
|
19
|
+
}
|
|
20
|
+
const MAX_PROPERTIES = 100;
|
|
21
|
+
const EMPTY_LOCAL_ALIASES = new Map();
|
|
22
|
+
const tokenTableMemo = new WeakMap();
|
|
23
|
+
export function buildTokenTable(entries) {
|
|
24
|
+
const cached = tokenTableMemo.get(entries);
|
|
25
|
+
if (cached)
|
|
26
|
+
return cached;
|
|
27
|
+
const table = {};
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
table[entry.name] = entry;
|
|
30
|
+
if (entry.cssName)
|
|
31
|
+
table[entry.cssName] = entry;
|
|
32
|
+
}
|
|
33
|
+
tokenTableMemo.set(entries, table);
|
|
34
|
+
return table;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns true when a `calc()` expression is safe to treat as a simple
|
|
38
|
+
* numeric value. We exclude percentages, viewport units, and font-relative
|
|
39
|
+
* units because those depend on context the browser cannot freeze into a
|
|
40
|
+
* single pixel value safely.
|
|
41
|
+
*/
|
|
42
|
+
function canBecomeNumeric(value) {
|
|
43
|
+
const v = value.trim();
|
|
44
|
+
if (/\b(?:min|max|clamp|env|anchor-size)\s*\(/i.test(v))
|
|
45
|
+
return false;
|
|
46
|
+
const m = /^calc\s*\(/i.exec(v);
|
|
47
|
+
if (!m)
|
|
48
|
+
return false;
|
|
49
|
+
const inner = v.slice(m[0].length, -1).trim();
|
|
50
|
+
const withoutVars = inner.replace(/var\([^)]+\)/g, "");
|
|
51
|
+
return !/\b\d+(?:\.\d+)?(?:%|vw|vh|vmin|vmax|dvw|dvh|sv[lw]h|lv[lw]h|[ce]m|ex|ch)\b/i.test(withoutVars);
|
|
52
|
+
}
|
|
53
|
+
let tableCache = null;
|
|
54
|
+
let tableSource = null;
|
|
55
|
+
export function getTokenTable() {
|
|
56
|
+
const { tokens } = getNudgeUiRuntimeConfig();
|
|
57
|
+
if (tableCache !== null && tableSource === tokens)
|
|
58
|
+
return tableCache;
|
|
59
|
+
tableCache = buildTokenTable(tokens);
|
|
60
|
+
tableSource = tokens;
|
|
61
|
+
return tableCache;
|
|
62
|
+
}
|
|
63
|
+
function isCustomPropertyToken(definition) {
|
|
64
|
+
return definition.cssName.startsWith("--");
|
|
65
|
+
}
|
|
66
|
+
function entryFromDefinition(definition, value) {
|
|
67
|
+
return {
|
|
68
|
+
name: definition.name,
|
|
69
|
+
cssName: definition.cssName,
|
|
70
|
+
value,
|
|
71
|
+
source: definition.declarations[0]?.source ?? "",
|
|
72
|
+
cssValue: definition.cssValue,
|
|
73
|
+
adapter: definition.adapter,
|
|
74
|
+
origin: definition.origin,
|
|
75
|
+
editable: definition.editable,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const tokenEntriesCache = new WeakMap();
|
|
79
|
+
function registerWithAncestors(el) {
|
|
80
|
+
let current = el;
|
|
81
|
+
while (current) {
|
|
82
|
+
registerResolutionElement(current);
|
|
83
|
+
current = current.parentElement;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The build-time catalog is intentionally an inventory of every project token.
|
|
88
|
+
* Element edits must instead use only custom properties resolved in that
|
|
89
|
+
* element's cascade; a token defined by a lazy stylesheet is not usable until
|
|
90
|
+
* that stylesheet is attached to the document. Entries are cached per element
|
|
91
|
+
* until the element or stylesheet revision changes; the derived table reuses
|
|
92
|
+
* `buildTokenTable`'s input-identity memo.
|
|
93
|
+
*/
|
|
94
|
+
export function getAvailableTokenEntriesForElement(el, definitions = getNudgeUiRuntimeConfig().tokenCatalog) {
|
|
95
|
+
const revisions = getDocumentRevisions(el.ownerDocument ?? document);
|
|
96
|
+
const cached = tokenEntriesCache.get(el);
|
|
97
|
+
if (cached && cached.elementRevision === revisions.element
|
|
98
|
+
&& cached.stylesheetRevision === revisions.stylesheet
|
|
99
|
+
&& cached.definitions === definitions) {
|
|
100
|
+
return cached.entries;
|
|
101
|
+
}
|
|
102
|
+
registerWithAncestors(el);
|
|
103
|
+
const computed = getElementComputedStyle(el);
|
|
104
|
+
const available = definitions.flatMap((definition) => {
|
|
105
|
+
if (!isCustomPropertyToken(definition)) {
|
|
106
|
+
// Literal adapters (for example Tailwind v3) do not have a browser
|
|
107
|
+
// custom property to probe. Their adapter owns their availability.
|
|
108
|
+
return [entryFromDefinition(definition, definition.declarations[0]?.value ?? "")];
|
|
109
|
+
}
|
|
110
|
+
const value = computed.getPropertyValue(definition.cssName).trim();
|
|
111
|
+
return value ? [entryFromDefinition(definition, value)] : [];
|
|
112
|
+
});
|
|
113
|
+
const intermediateTable = buildTokenTable(available);
|
|
114
|
+
const entries = available.map((entry) => ({
|
|
115
|
+
...entry,
|
|
116
|
+
value: resolveTokenValue(entry.value, intermediateTable).resolvedValue,
|
|
117
|
+
}));
|
|
118
|
+
tokenEntriesCache.set(el, {
|
|
119
|
+
elementRevision: revisions.element,
|
|
120
|
+
stylesheetRevision: revisions.stylesheet,
|
|
121
|
+
definitions,
|
|
122
|
+
entries,
|
|
123
|
+
});
|
|
124
|
+
return entries;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Replaces build-time token declaration hints with declarations serialized by
|
|
128
|
+
* the browser from the stylesheets currently attached to this document.
|
|
129
|
+
*/
|
|
130
|
+
function hydrateTokenCatalogFromCssom(definitions, doc = document) {
|
|
131
|
+
const needsHydration = (definition) => definition.declarations.length === 0
|
|
132
|
+
|| (definition.adapter === "vanilla-extract" && definition.declarations.every((declaration) => declaration.value.trim() === `var(${definition.cssName})`
|
|
133
|
+
&& !declaration.context.selector
|
|
134
|
+
&& !declaration.context.wrappers?.length));
|
|
135
|
+
const known = new Set(definitions.filter(needsHydration).map((definition) => definition.cssName));
|
|
136
|
+
const declarations = new Map();
|
|
137
|
+
for (const rule of collectCssomRules(doc).rules) {
|
|
138
|
+
if (rule.source === "#nudge-ui-styles")
|
|
139
|
+
continue;
|
|
140
|
+
for (const declaration of rule.declarations) {
|
|
141
|
+
if (!known.has(declaration.property))
|
|
142
|
+
continue;
|
|
143
|
+
const wrappers = [
|
|
144
|
+
...(rule.layer ? [{ kind: "layer", params: rule.layer }] : []),
|
|
145
|
+
...(rule.atRules ?? []).flatMap((atRule) => atRule.kind === "media" || atRule.kind === "supports"
|
|
146
|
+
? [{ kind: atRule.kind, params: atRule.params }]
|
|
147
|
+
: []),
|
|
148
|
+
];
|
|
149
|
+
const list = declarations.get(declaration.property) ?? [];
|
|
150
|
+
list.push({
|
|
151
|
+
id: `${declaration.property}\u0000${rule.source ?? "cssom"}\u0000${rule.sourceOrder ?? list.length}`,
|
|
152
|
+
order: rule.sourceOrder,
|
|
153
|
+
value: declaration.value,
|
|
154
|
+
source: rule.source ?? "cssom",
|
|
155
|
+
important: Boolean(declaration.important),
|
|
156
|
+
context: {
|
|
157
|
+
selector: rule.selectorText,
|
|
158
|
+
...(wrappers.length > 0 ? { wrappers } : {}),
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
declarations.set(declaration.property, list);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return definitions.map((definition) => {
|
|
165
|
+
if (!needsHydration(definition))
|
|
166
|
+
return definition;
|
|
167
|
+
const accepted = declarations.get(definition.cssName);
|
|
168
|
+
return accepted?.length ? { ...definition, declarations: accepted } : definition;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function sourceFile(source) {
|
|
172
|
+
return source.replace(/:\d+$/, "").split(/[?#]/, 1)[0] ?? source;
|
|
173
|
+
}
|
|
174
|
+
function normalizedPath(value) {
|
|
175
|
+
return decodeURIComponent(value).replace(/\\/g, "/").replace(/^file:\/\//, "").replace(/\/+$/, "");
|
|
176
|
+
}
|
|
177
|
+
function loadedStylesheetSources(doc) {
|
|
178
|
+
const sourceIdentityByPath = new Map();
|
|
179
|
+
for (const node of doc.querySelectorAll('style[data-vite-dev-id], link[rel~="stylesheet"][href]')) {
|
|
180
|
+
const providesAuthoredIdentity = node.tagName === "STYLE";
|
|
181
|
+
const source = providesAuthoredIdentity
|
|
182
|
+
? node.dataset.viteDevId
|
|
183
|
+
: node.getAttribute("href");
|
|
184
|
+
if (!source)
|
|
185
|
+
continue;
|
|
186
|
+
const path = normalizedPath(source.split(/[?#]/, 1)[0] ?? source);
|
|
187
|
+
sourceIdentityByPath.set(path, providesAuthoredIdentity || sourceIdentityByPath.get(path) === true);
|
|
188
|
+
}
|
|
189
|
+
return Array.from(sourceIdentityByPath, ([path, providesAuthoredIdentity]) => ({
|
|
190
|
+
path,
|
|
191
|
+
providesAuthoredIdentity,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
function isLoadedCssSource(source, loadedSources) {
|
|
195
|
+
const file = sourceFile(source);
|
|
196
|
+
if (!/\.css$/i.test(file) || loadedSources.length === 0)
|
|
197
|
+
return true;
|
|
198
|
+
const normalizedFile = normalizedPath(file);
|
|
199
|
+
return loadedSources.some((loaded) => loaded === normalizedFile
|
|
200
|
+
|| loaded.endsWith(`/${normalizedFile}`)
|
|
201
|
+
|| normalizedFile.endsWith(`/${loaded}`));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Returns stylesheet identities only when the browser gave us a complete,
|
|
205
|
+
* authored-source mapping. A compiled host can expose a mixture of source-like
|
|
206
|
+
* and opaque CSS URLs; treating the mapped subset as complete would make
|
|
207
|
+
* declarations behind the opaque URLs look lazy even while their variables are
|
|
208
|
+
* live in the document.
|
|
209
|
+
*/
|
|
210
|
+
function confidentlyLoadedStylesheetSources(definitions, loadedSources) {
|
|
211
|
+
const cssSources = definitions.flatMap((definition) => definition.declarations
|
|
212
|
+
.map((declaration) => declaration.source)
|
|
213
|
+
.filter((source) => /\.css$/i.test(sourceFile(source))));
|
|
214
|
+
const known = new Set();
|
|
215
|
+
for (const loaded of loadedSources) {
|
|
216
|
+
if (loaded.providesAuthoredIdentity
|
|
217
|
+
|| cssSources.some((source) => isLoadedCssSource(source, [loaded.path]))) {
|
|
218
|
+
known.add(loaded.path);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// One opaque compiled URL can contain any authored declaration, so the
|
|
222
|
+
// document does not provide enough evidence to remove unloaded sources.
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
return [...known];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Produces the page catalog used by the global Tokens settings section. It retains the
|
|
229
|
+
* build-time inventory separately, while removing declarations from CSS files
|
|
230
|
+
* that have not loaded when the document exposes mappable source identities
|
|
231
|
+
* (such as Vite's lazy route stylesheets). Opaque compiled URLs do not narrow
|
|
232
|
+
* the adapter-supplied catalog.
|
|
233
|
+
*/
|
|
234
|
+
export function getAvailableTokenCatalog(root = document.documentElement, definitions = getNudgeUiRuntimeConfig().tokenCatalog) {
|
|
235
|
+
const computed = getElementComputedStyle(root);
|
|
236
|
+
const hydrated = hydrateTokenCatalogFromCssom(definitions, root.ownerDocument ?? document);
|
|
237
|
+
const loadedSources = confidentlyLoadedStylesheetSources(hydrated, loadedStylesheetSources(root.ownerDocument ?? document));
|
|
238
|
+
return hydrated.flatMap((definition) => {
|
|
239
|
+
if (!isCustomPropertyToken(definition))
|
|
240
|
+
return [definition];
|
|
241
|
+
if (!computed.getPropertyValue(definition.cssName).trim())
|
|
242
|
+
return [];
|
|
243
|
+
const declarations = definition.declarations.filter((declaration) => isLoadedCssSource(declaration.source, loadedSources));
|
|
244
|
+
return declarations.length > 0 ? [{ ...definition, declarations }] : [];
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function normalizeInElementContext(el, property, value) {
|
|
248
|
+
const doc = el.ownerDocument;
|
|
249
|
+
const probe = doc.createElement(el.tagName.toLowerCase());
|
|
250
|
+
probe.setAttribute("data-nudge-ui", "attribution-probe");
|
|
251
|
+
probe.style.setProperty(property, value, "important");
|
|
252
|
+
probe.style.setProperty("position", "fixed", "important");
|
|
253
|
+
probe.style.setProperty("visibility", "hidden", "important");
|
|
254
|
+
el.parentElement?.insertBefore(probe, el.nextSibling);
|
|
255
|
+
if (!probe.isConnected)
|
|
256
|
+
doc.body.appendChild(probe);
|
|
257
|
+
const normalized = getElementComputedStyle(probe).getPropertyValue(property).trim();
|
|
258
|
+
probe.remove();
|
|
259
|
+
return normalized;
|
|
260
|
+
}
|
|
261
|
+
function candidateMatchesPainted(el, row, painted) {
|
|
262
|
+
if (!row.tokenName || !/^var\(\s*--[\w-]+\s*\)$/.test(row.declaredValue))
|
|
263
|
+
return false;
|
|
264
|
+
return normalizeInElementContext(el, row.property, row.declaredValue) === painted.trim();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Resolves one authored value through the value-semantics Module using the
|
|
268
|
+
* inspector's integration context. Used by the token-availability path; the
|
|
269
|
+
* value Module is the sole interpretation authority.
|
|
270
|
+
*/
|
|
271
|
+
function resolveTokenValue(value, tokenTable, localAliases = EMPTY_LOCAL_ALIASES) {
|
|
272
|
+
return interpretValue("--nudge-ui-token", value, createInspectorValueContext(tokenTable, localAliases))[0];
|
|
273
|
+
}
|
|
274
|
+
function directionalityFromComputed(el, computed) {
|
|
275
|
+
return {
|
|
276
|
+
direction: computed?.direction || el?.dir || "ltr",
|
|
277
|
+
writingMode: computed?.getPropertyValue("writing-mode").trim() || "horizontal-tb",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function elementDirectionality(el) {
|
|
281
|
+
return directionalityFromComputed(el, getElementComputedStyle(el));
|
|
282
|
+
}
|
|
283
|
+
function isLogicalBoxProperty(property) {
|
|
284
|
+
return /^(?:margin|padding|inset)-(?:inline|block)(?:-(?:start|end))?$/i.test(property);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Coordinates cascade facts with the value-semantics Module. All property-family
|
|
288
|
+
* decomposition (logical sides, border, font, radius corners, physical spacing)
|
|
289
|
+
* lives in `interpretValue`; this function only supplies the token
|
|
290
|
+
* context and directionality facts, then projects the Module's fields onto the
|
|
291
|
+
* `ResolvedDeclaration` shape.
|
|
292
|
+
*/
|
|
293
|
+
function resolveDeclaration(declaration, tokenTable, localAliases = EMPTY_LOCAL_ALIASES, directionality) {
|
|
294
|
+
const fields = interpretValue(declaration.property, declaration.value, createInspectorValueContext(tokenTable, localAliases, directionality));
|
|
295
|
+
return fields.map((field) => ({
|
|
296
|
+
property: field.property,
|
|
297
|
+
declaredValue: field.declaredValue,
|
|
298
|
+
sourceProperty: field.sourceProperty,
|
|
299
|
+
tokenName: field.tokenName,
|
|
300
|
+
resolvedValue: field.resolvedValue,
|
|
301
|
+
important: declaration.important,
|
|
302
|
+
tokens: field.tokens,
|
|
303
|
+
opacity: field.opacity,
|
|
304
|
+
propertyOpacity: field.propertyOpacity,
|
|
305
|
+
color: field.color,
|
|
306
|
+
modifiers: field.modifiers,
|
|
307
|
+
capability: field.capability,
|
|
308
|
+
resolvedTokenValue: field.resolvedValue,
|
|
309
|
+
...(field.structure ? { structure: field.structure } : {}),
|
|
310
|
+
...(field.diagnostic ? { diagnostic: field.diagnostic } : {}),
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
function hydratePropertyOpacity(row, computedValue) {
|
|
314
|
+
if (row.property.toLowerCase() !== "opacity")
|
|
315
|
+
return;
|
|
316
|
+
const value = normalizeOpacityPercent(computedValue);
|
|
317
|
+
if (value === null)
|
|
318
|
+
return;
|
|
319
|
+
const existing = row.propertyOpacity;
|
|
320
|
+
const token = existing?.token
|
|
321
|
+
?? (row.tokenName ? row.tokens?.find((reference) => reference.name === row.tokenName) : undefined);
|
|
322
|
+
row.propertyOpacity = {
|
|
323
|
+
value,
|
|
324
|
+
authoredValue: existing?.authoredValue ?? row.authored ?? row.declaredValue,
|
|
325
|
+
tokenName: existing?.tokenName ?? row.tokenName,
|
|
326
|
+
...(token ? { token } : {}),
|
|
327
|
+
editable: existing?.editable ?? false,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function hydratePropertyOpacityRows(rows, computed) {
|
|
331
|
+
for (const row of rows)
|
|
332
|
+
hydratePropertyOpacity(row, computed.getPropertyValue(row.property));
|
|
333
|
+
}
|
|
334
|
+
function capabilityFor(property, value, tokenTable) {
|
|
335
|
+
return interpretValue(property, value, createInspectorValueContext(tokenTable))[0].capability;
|
|
336
|
+
}
|
|
337
|
+
let containerProbeSequence = 0;
|
|
338
|
+
/**
|
|
339
|
+
* Container conditions are element-relative. CSSOM exposes their text but has
|
|
340
|
+
* no `matches` API, so ask the browser by applying an inert custom property to
|
|
341
|
+
* the selected element inside an equivalent temporary @container wrapper.
|
|
342
|
+
*/
|
|
343
|
+
function matchesContainerQuery(el, params) {
|
|
344
|
+
const doc = el.ownerDocument;
|
|
345
|
+
if (!doc.head || !params)
|
|
346
|
+
return false;
|
|
347
|
+
const id = ++containerProbeSequence;
|
|
348
|
+
const marker = `data-container-probe-${id}`;
|
|
349
|
+
const property = `--container-probe-${id}`;
|
|
350
|
+
const style = doc.createElement("style");
|
|
351
|
+
style.setAttribute("data-nudge-ui", "container-probe");
|
|
352
|
+
style.textContent = `@container ${params} { [${marker}] { ${property}: 1; } }`;
|
|
353
|
+
el.setAttribute(marker, "");
|
|
354
|
+
doc.head.appendChild(style);
|
|
355
|
+
try {
|
|
356
|
+
return getElementComputedStyle(el).getPropertyValue(property).trim() === "1";
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
style.remove();
|
|
363
|
+
el.removeAttribute(marker);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function atRulesApplyToElement(el, atRules) {
|
|
367
|
+
const view = el.ownerDocument.defaultView;
|
|
368
|
+
return (atRules ?? []).every((atRule) => {
|
|
369
|
+
if (atRule.kind === "container")
|
|
370
|
+
return matchesContainerQuery(el, atRule.params);
|
|
371
|
+
if (atRule.kind === "supports")
|
|
372
|
+
return view?.CSS?.supports(atRule.params) ?? false;
|
|
373
|
+
return true;
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
function specificityForBranch(rule, branch) {
|
|
377
|
+
// Single-branch selectors reuse the specificity collected from CSSOM; the
|
|
378
|
+
// matched branch is the whole selector. Multi-branch selectors need the
|
|
379
|
+
// matched branch's own weight, so recompute per branch.
|
|
380
|
+
return rule.selectorText.includes(",") ? computeSpecificity(branch) : rule.specificity;
|
|
381
|
+
}
|
|
382
|
+
function collectLocalAliases(el, rules, ruleApplies) {
|
|
383
|
+
// Custom properties inherit independently of the property being resolved.
|
|
384
|
+
// Resolve the winning declaration separately for each element in the
|
|
385
|
+
// ancestor chain, then let the nearest declaration override inherited ones.
|
|
386
|
+
// This covers local aliases such as --color-error and inherited page tokens
|
|
387
|
+
// such as --color-ink without widening the build-time global catalog.
|
|
388
|
+
const lineage = [];
|
|
389
|
+
let current = el;
|
|
390
|
+
while (current) {
|
|
391
|
+
lineage.push(current);
|
|
392
|
+
current = current.parentElement;
|
|
393
|
+
}
|
|
394
|
+
const aliases = new Map();
|
|
395
|
+
for (const element of lineage.reverse()) {
|
|
396
|
+
const candidates = new Map();
|
|
397
|
+
rules.forEach((rule, index) => {
|
|
398
|
+
if (rule.active === false)
|
|
399
|
+
return;
|
|
400
|
+
if (!ruleApplies(rule))
|
|
401
|
+
return;
|
|
402
|
+
const branch = matchingSelectorBranch(element, rule.selectorText);
|
|
403
|
+
if (!branch)
|
|
404
|
+
return;
|
|
405
|
+
const sourceOrder = rule.sourceOrder ?? index;
|
|
406
|
+
const specificity = specificityForBranch(rule, branch);
|
|
407
|
+
for (const declaration of rule.declarations) {
|
|
408
|
+
if (!declaration.property.startsWith("--"))
|
|
409
|
+
continue;
|
|
410
|
+
const candidate = {
|
|
411
|
+
value: declaration.value.trim(),
|
|
412
|
+
important: declaration.important,
|
|
413
|
+
layer: rule.layer,
|
|
414
|
+
layerOrder: rule.layerOrder,
|
|
415
|
+
specificity,
|
|
416
|
+
sourceOrder,
|
|
417
|
+
};
|
|
418
|
+
const previous = candidates.get(declaration.property);
|
|
419
|
+
if (!previous || compareAuthorCascade(candidate, previous) >= 0) {
|
|
420
|
+
candidates.set(declaration.property, candidate);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
// Inline custom properties are also inherited by descendants. They are
|
|
425
|
+
// explicit cascade winners for their element, so include them at inline
|
|
426
|
+
// specificity without changing the managed-style rule contract.
|
|
427
|
+
for (const property of Array.from(element.style)) {
|
|
428
|
+
if (!property.startsWith("--"))
|
|
429
|
+
continue;
|
|
430
|
+
const candidate = {
|
|
431
|
+
value: element.style.getPropertyValue(property).trim(),
|
|
432
|
+
important: element.style.getPropertyPriority(property) === "important",
|
|
433
|
+
inline: true,
|
|
434
|
+
specificity: 100000000,
|
|
435
|
+
sourceOrder: Number.MAX_SAFE_INTEGER,
|
|
436
|
+
};
|
|
437
|
+
const previous = candidates.get(property);
|
|
438
|
+
if (!previous || compareAuthorCascade(candidate, previous) >= 0) {
|
|
439
|
+
candidates.set(property, candidate);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (const [name, candidate] of candidates)
|
|
443
|
+
aliases.set(name, candidate.value);
|
|
444
|
+
}
|
|
445
|
+
return aliases;
|
|
446
|
+
}
|
|
447
|
+
function makeRuleApplies(el) {
|
|
448
|
+
const matchingContexts = new Map();
|
|
449
|
+
return (rule) => {
|
|
450
|
+
const atRules = rule.atRules;
|
|
451
|
+
if (!atRules || atRules.length === 0)
|
|
452
|
+
return true;
|
|
453
|
+
const key = JSON.stringify(atRules);
|
|
454
|
+
const cached = matchingContexts.get(key);
|
|
455
|
+
if (cached !== undefined)
|
|
456
|
+
return cached;
|
|
457
|
+
const result = atRulesApplyToElement(el, atRules);
|
|
458
|
+
matchingContexts.set(key, result);
|
|
459
|
+
return result;
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
const TRANSIENT_SELECTOR = /:(?:hover|active|focus|focus-visible|focus-within|visited|target)(?:\b|\()/;
|
|
463
|
+
const transformedRulesMemo = new WeakMap();
|
|
464
|
+
function rulesForTransform(rules, transform) {
|
|
465
|
+
let byTransform = transformedRulesMemo.get(rules);
|
|
466
|
+
if (!byTransform) {
|
|
467
|
+
byTransform = new Map();
|
|
468
|
+
transformedRulesMemo.set(rules, byTransform);
|
|
469
|
+
}
|
|
470
|
+
const cached = byTransform.get(transform);
|
|
471
|
+
if (cached)
|
|
472
|
+
return cached;
|
|
473
|
+
let transformed;
|
|
474
|
+
if (transform === "live") {
|
|
475
|
+
transformed = rules.map((rule) => ({ rule, selectorText: rule.selectorText }));
|
|
476
|
+
}
|
|
477
|
+
else if (transform === "stable") {
|
|
478
|
+
transformed = rules
|
|
479
|
+
.filter((rule) => !TRANSIENT_SELECTOR.test(rule.selectorText))
|
|
480
|
+
.map((rule) => ({ rule, selectorText: rule.selectorText }));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
transformed = rules.flatMap((rule) => {
|
|
484
|
+
const selectorText = selectorForState(rule.selectorText, transform);
|
|
485
|
+
return selectorText ? [{ rule, selectorText }] : [];
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
byTransform.set(transform, transformed);
|
|
489
|
+
return transformed;
|
|
490
|
+
}
|
|
491
|
+
function isElementSensitiveSelector(selector) {
|
|
492
|
+
let attributeDepth = 0;
|
|
493
|
+
let quote = null;
|
|
494
|
+
let escaped = false;
|
|
495
|
+
for (let index = 0; index < selector.length; index++) {
|
|
496
|
+
const character = selector[index];
|
|
497
|
+
if (quote) {
|
|
498
|
+
if (escaped)
|
|
499
|
+
escaped = false;
|
|
500
|
+
else if (character === "\\")
|
|
501
|
+
escaped = true;
|
|
502
|
+
else if (character === quote)
|
|
503
|
+
quote = null;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (attributeDepth > 0) {
|
|
507
|
+
if (character === '"' || character === "'")
|
|
508
|
+
quote = character;
|
|
509
|
+
else if (character === "[")
|
|
510
|
+
attributeDepth++;
|
|
511
|
+
else if (character === "]")
|
|
512
|
+
attributeDepth--;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (character === "[") {
|
|
516
|
+
attributeDepth = 1;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (character === "+" || character === "~")
|
|
520
|
+
return true;
|
|
521
|
+
if (character !== ":")
|
|
522
|
+
continue;
|
|
523
|
+
if (selector[index + 1] === ":")
|
|
524
|
+
return true;
|
|
525
|
+
const pseudo = /^[A-Za-z-]+/.exec(selector.slice(index + 1))?.[0];
|
|
526
|
+
// :root is represented by the complete ancestor lineage. Other
|
|
527
|
+
// pseudo-classes can vary per concrete element without an attribute or
|
|
528
|
+
// sibling mutation (for example :checked, :visited, and :target).
|
|
529
|
+
if (pseudo && pseudo.toLowerCase() !== "root")
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
const sourceSiteMatchBucketsMemo = new WeakMap();
|
|
535
|
+
function sourceSiteMatchBuckets(rules, transform) {
|
|
536
|
+
let byTransform = sourceSiteMatchBucketsMemo.get(rules);
|
|
537
|
+
if (!byTransform) {
|
|
538
|
+
byTransform = new Map();
|
|
539
|
+
sourceSiteMatchBucketsMemo.set(rules, byTransform);
|
|
540
|
+
}
|
|
541
|
+
const cached = byTransform.get(transform);
|
|
542
|
+
if (cached)
|
|
543
|
+
return cached;
|
|
544
|
+
const cacheable = [];
|
|
545
|
+
const elementSensitive = [];
|
|
546
|
+
for (const entry of rulesForTransform(rules, transform)) {
|
|
547
|
+
(isElementSensitiveSelector(entry.selectorText) ? elementSensitive : cacheable).push(entry);
|
|
548
|
+
}
|
|
549
|
+
const buckets = { cacheable, elementSensitive };
|
|
550
|
+
byTransform.set(transform, buckets);
|
|
551
|
+
return buckets;
|
|
552
|
+
}
|
|
553
|
+
const SOURCE_SITE_CACHE_MAX = 4096;
|
|
554
|
+
let sourceSiteMatchCaches = new WeakMap();
|
|
555
|
+
let sourceSiteMatchCacheEntries = 0;
|
|
556
|
+
const concreteElementMatchCaches = new WeakMap();
|
|
557
|
+
// The "all matched" set (inactive rules included) is matched fresh against
|
|
558
|
+
// every lineage element on every resolution otherwise, duplicating the
|
|
559
|
+
// cached sweep's cost. State-independent selectors use the same revision-keyed
|
|
560
|
+
// memo shape as the active sweep. Element-sensitive selectors stay fresh: a
|
|
561
|
+
// checked state, sibling relationship, or similar browser state can change
|
|
562
|
+
// without a DOM mutation that this registry observes.
|
|
563
|
+
const allMatchesMemo = new WeakMap();
|
|
564
|
+
/** Test hook: clears the per-source-site matched-rule cache. */
|
|
565
|
+
export function resetSourceSiteMatchCache() {
|
|
566
|
+
sourceSiteMatchCaches = new WeakMap();
|
|
567
|
+
sourceSiteMatchCacheEntries = 0;
|
|
568
|
+
}
|
|
569
|
+
/** Test hook: number of distinct cached source-site match sets. */
|
|
570
|
+
export function sourceSiteMatchCacheSize() {
|
|
571
|
+
return sourceSiteMatchCacheEntries;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Captures selector-relevant state without serializing the whole document.
|
|
575
|
+
* Element and ancestor attributes cover the common source-site selectors while
|
|
576
|
+
* child/sibling-sensitive selectors are handled conservatively below.
|
|
577
|
+
*/
|
|
578
|
+
function sourceSiteContextKey(el) {
|
|
579
|
+
const lineage = [];
|
|
580
|
+
let current = el;
|
|
581
|
+
while (current) {
|
|
582
|
+
const attributes = Array.from(current.attributes)
|
|
583
|
+
.filter((attribute) => attribute.name !== "data-renderer-id")
|
|
584
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
585
|
+
.map((attribute) => `${attribute.name}=${attribute.value}`)
|
|
586
|
+
.join("\u0002");
|
|
587
|
+
lineage.push(`${current.tagName}\u0003${attributes}`);
|
|
588
|
+
current = current.parentElement;
|
|
589
|
+
}
|
|
590
|
+
return lineage.join("\u0001");
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Source-site identity key for the matched-rule cache. The element's class is
|
|
594
|
+
* part of the key because sibling instances of a source site can carry
|
|
595
|
+
* different classes (for example rows in a large list), which changes which
|
|
596
|
+
* rules match even though their `data-cid`/`data-src` are identical.
|
|
597
|
+
*/
|
|
598
|
+
function sourceSiteKey(el, transform) {
|
|
599
|
+
const cid = el.getAttribute("data-cid") ?? "";
|
|
600
|
+
const src = el.getAttribute("data-src") ?? "";
|
|
601
|
+
const instance = el.getAttribute("data-projection-instance") ?? "";
|
|
602
|
+
const className = typeof el.className === "string" ? el.className : "";
|
|
603
|
+
return `${cid}\u0000${src}\u0000${instance}\u0000${className}\u0000${transform}`;
|
|
604
|
+
}
|
|
605
|
+
function matchRuleForElement(el, entry) {
|
|
606
|
+
const branch = matchingSelectorBranch(el, entry.selectorText);
|
|
607
|
+
if (!branch)
|
|
608
|
+
return null;
|
|
609
|
+
return {
|
|
610
|
+
rule: entry.rule,
|
|
611
|
+
selectorText: entry.selectorText,
|
|
612
|
+
branch,
|
|
613
|
+
specificity: specificityForBranch({ ...entry.rule, selectorText: entry.selectorText }, branch),
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Captures the current match outcome for selectors whose truth can change
|
|
618
|
+
* without a document revision. This keeps the expensive resolved-row cache
|
|
619
|
+
* usable while invalidating it when states such as `checked` actually change.
|
|
620
|
+
*/
|
|
621
|
+
function elementSensitiveMatchKey(el, rules, transform) {
|
|
622
|
+
const entries = sourceSiteMatchBuckets(rules, transform).elementSensitive;
|
|
623
|
+
if (entries.length === 0)
|
|
624
|
+
return "";
|
|
625
|
+
const matches = [];
|
|
626
|
+
let current = el;
|
|
627
|
+
let depth = 0;
|
|
628
|
+
while (current) {
|
|
629
|
+
for (const entry of entries) {
|
|
630
|
+
const branch = matchingSelectorBranch(current, entry.selectorText);
|
|
631
|
+
if (branch)
|
|
632
|
+
matches.push(`${depth}:${entry.rule.sourceOrder ?? 0}:${branch}`);
|
|
633
|
+
}
|
|
634
|
+
current = current.parentElement;
|
|
635
|
+
depth++;
|
|
636
|
+
}
|
|
637
|
+
return matches.join("\u0001");
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Matches every transformed rule against one element. For elements carrying a
|
|
641
|
+
* source-site identity (`data-cid`) the selector-match result is cached per
|
|
642
|
+
* `(document, data-cid, data-src, data-projection-instance, transform, element and
|
|
643
|
+
* stylesheet revision, context)` so repeated selections and equivalent sibling instances of the
|
|
644
|
+
* same source site reuse the matched rule set. Elements without a stable
|
|
645
|
+
* identity, or selectors that depend on unsupported relationships, are matched
|
|
646
|
+
* fresh on every call.
|
|
647
|
+
*/
|
|
648
|
+
function getCachedElementMatches(el, rules, transform) {
|
|
649
|
+
const { cacheable, elementSensitive } = sourceSiteMatchBuckets(rules, transform);
|
|
650
|
+
const collect = (entries) => {
|
|
651
|
+
const matched = [];
|
|
652
|
+
for (const entry of entries) {
|
|
653
|
+
if (entry.rule.active === false)
|
|
654
|
+
continue;
|
|
655
|
+
const match = matchRuleForElement(el, entry);
|
|
656
|
+
if (match)
|
|
657
|
+
matched.push(match);
|
|
658
|
+
}
|
|
659
|
+
return matched;
|
|
660
|
+
};
|
|
661
|
+
const cid = el.getAttribute("data-cid");
|
|
662
|
+
const doc = el.ownerDocument ?? document;
|
|
663
|
+
const revisions = getDocumentRevisions(doc);
|
|
664
|
+
if (!cid || cacheable.length === 0) {
|
|
665
|
+
let cachedMatches = [];
|
|
666
|
+
if (cacheable.length > 0) {
|
|
667
|
+
let cache = concreteElementMatchCaches.get(el);
|
|
668
|
+
if (!cache) {
|
|
669
|
+
cache = new Map();
|
|
670
|
+
concreteElementMatchCaches.set(el, cache);
|
|
671
|
+
}
|
|
672
|
+
const cached = cache.get(transform);
|
|
673
|
+
if (cached
|
|
674
|
+
&& cached.elementRevision === revisions.element
|
|
675
|
+
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
676
|
+
cachedMatches = cached.matched;
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
cachedMatches = collect(cacheable);
|
|
680
|
+
cache.set(transform, {
|
|
681
|
+
elementRevision: revisions.element,
|
|
682
|
+
stylesheetRevision: revisions.stylesheet,
|
|
683
|
+
matched: cachedMatches,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const sensitiveMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
688
|
+
return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
|
|
689
|
+
}
|
|
690
|
+
const key = `${sourceSiteKey(el, transform)}\u0000${sourceSiteContextKey(el)}`;
|
|
691
|
+
let cache = sourceSiteMatchCaches.get(doc);
|
|
692
|
+
if (!cache) {
|
|
693
|
+
cache = new Map();
|
|
694
|
+
sourceSiteMatchCaches.set(doc, cache);
|
|
695
|
+
}
|
|
696
|
+
const cached = cache.get(key);
|
|
697
|
+
let cachedMatches;
|
|
698
|
+
if (cached
|
|
699
|
+
&& cached.elementRevision === revisions.element
|
|
700
|
+
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
701
|
+
cachedMatches = cached.matched;
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
cachedMatches = collect(cacheable);
|
|
705
|
+
if (sourceSiteMatchCacheEntries >= SOURCE_SITE_CACHE_MAX) {
|
|
706
|
+
sourceSiteMatchCaches = new WeakMap();
|
|
707
|
+
sourceSiteMatchCacheEntries = 0;
|
|
708
|
+
cache = new Map();
|
|
709
|
+
sourceSiteMatchCaches.set(doc, cache);
|
|
710
|
+
}
|
|
711
|
+
if (!cache.has(key))
|
|
712
|
+
sourceSiteMatchCacheEntries++;
|
|
713
|
+
cache.set(key, {
|
|
714
|
+
elementRevision: revisions.element,
|
|
715
|
+
stylesheetRevision: revisions.stylesheet,
|
|
716
|
+
matched: cachedMatches,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
// Match element-sensitive selectors on every resolution. Browser state such
|
|
720
|
+
// as `checked`, `target`, or a sibling relationship can change without a DOM
|
|
721
|
+
// or stylesheet revision, so a revision-keyed entry can return stale rows.
|
|
722
|
+
const elementMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
723
|
+
return elementMatches.length === 0 ? cachedMatches : [...cachedMatches, ...elementMatches];
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Matches selectors without applying conditional wrappers. Resolution still
|
|
727
|
+
* uses the active-only set for cascade decisions; this set preserves inactive
|
|
728
|
+
* media-query alternatives for property attribution in the inspector.
|
|
729
|
+
* Memoized per (element, transform, revisions) like the cached sweep for
|
|
730
|
+
* state-independent selectors. Element-sensitive selectors stay fresh because
|
|
731
|
+
* their state can change without a revision bump (for example, `:checked`),
|
|
732
|
+
* and the "live" transform always keeps its raw selector set fresh.
|
|
733
|
+
*/
|
|
734
|
+
function getAllElementMatches(el, rules, transform) {
|
|
735
|
+
if (transform === "live") {
|
|
736
|
+
return rulesForTransform(rules, transform).flatMap((entry) => {
|
|
737
|
+
const match = matchRuleForElement(el, entry);
|
|
738
|
+
return match ? [match] : [];
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
const { cacheable, elementSensitive } = sourceSiteMatchBuckets(rules, transform);
|
|
742
|
+
const collect = (entries) => entries.flatMap((entry) => {
|
|
743
|
+
const match = matchRuleForElement(el, entry);
|
|
744
|
+
return match ? [match] : [];
|
|
745
|
+
});
|
|
746
|
+
const doc = el.ownerDocument ?? document;
|
|
747
|
+
const revisions = getDocumentRevisions(doc);
|
|
748
|
+
let cachedMatches = [];
|
|
749
|
+
if (cacheable.length > 0) {
|
|
750
|
+
let byTransform = allMatchesMemo.get(el);
|
|
751
|
+
if (!byTransform) {
|
|
752
|
+
byTransform = new Map();
|
|
753
|
+
allMatchesMemo.set(el, byTransform);
|
|
754
|
+
}
|
|
755
|
+
const cached = byTransform.get(transform);
|
|
756
|
+
if (cached
|
|
757
|
+
&& cached.elementRevision === revisions.element
|
|
758
|
+
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
759
|
+
cachedMatches = cached.matched;
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
cachedMatches = collect(cacheable);
|
|
763
|
+
byTransform.set(transform, {
|
|
764
|
+
elementRevision: revisions.element,
|
|
765
|
+
stylesheetRevision: revisions.stylesheet,
|
|
766
|
+
matched: cachedMatches,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const sensitiveMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
771
|
+
return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* One matching pass over the rules for the element and its ancestors, with the
|
|
775
|
+
* selector-match results shared between the element's own resolution and the
|
|
776
|
+
* inherited phase. Local aliases are collected per element so no ancestor
|
|
777
|
+
* re-walks the lineage × rules.
|
|
778
|
+
*/
|
|
779
|
+
function resolveLineage(el, rules, transform) {
|
|
780
|
+
const lineage = [];
|
|
781
|
+
let current = el;
|
|
782
|
+
while (current) {
|
|
783
|
+
lineage.push(current);
|
|
784
|
+
current = current.parentElement;
|
|
785
|
+
}
|
|
786
|
+
lineage.reverse();
|
|
787
|
+
const selectorMatches = new Map();
|
|
788
|
+
const allSelectorMatches = new Map();
|
|
789
|
+
for (const element of lineage) {
|
|
790
|
+
selectorMatches.set(element, getCachedElementMatches(element, rules, transform));
|
|
791
|
+
allSelectorMatches.set(element, getAllElementMatches(element, rules, transform));
|
|
792
|
+
}
|
|
793
|
+
const byElement = new Map();
|
|
794
|
+
for (let index = 0; index < lineage.length; index++) {
|
|
795
|
+
const element = lineage[index];
|
|
796
|
+
const ruleApplies = makeRuleApplies(element);
|
|
797
|
+
const aliases = new Map();
|
|
798
|
+
for (const lineageElement of lineage.slice(0, index + 1)) {
|
|
799
|
+
const candidates = new Map();
|
|
800
|
+
for (const match of selectorMatches.get(lineageElement) ?? []) {
|
|
801
|
+
if (!ruleApplies(match.rule))
|
|
802
|
+
continue;
|
|
803
|
+
const sourceOrder = match.rule.sourceOrder ?? 0;
|
|
804
|
+
for (const declaration of match.rule.declarations) {
|
|
805
|
+
if (!declaration.property.startsWith("--"))
|
|
806
|
+
continue;
|
|
807
|
+
const candidate = {
|
|
808
|
+
value: declaration.value.trim(),
|
|
809
|
+
important: declaration.important,
|
|
810
|
+
layer: match.rule.layer,
|
|
811
|
+
layerOrder: match.rule.layerOrder,
|
|
812
|
+
specificity: match.specificity,
|
|
813
|
+
sourceOrder,
|
|
814
|
+
};
|
|
815
|
+
const previous = candidates.get(declaration.property);
|
|
816
|
+
if (!previous || compareAuthorCascade(candidate, previous) >= 0) {
|
|
817
|
+
candidates.set(declaration.property, candidate);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
for (const property of Array.from(lineageElement.style)) {
|
|
822
|
+
if (!property.startsWith("--"))
|
|
823
|
+
continue;
|
|
824
|
+
const candidate = {
|
|
825
|
+
value: lineageElement.style.getPropertyValue(property).trim(),
|
|
826
|
+
important: lineageElement.style.getPropertyPriority(property) === "important",
|
|
827
|
+
inline: true,
|
|
828
|
+
specificity: 100000000,
|
|
829
|
+
sourceOrder: Number.MAX_SAFE_INTEGER,
|
|
830
|
+
};
|
|
831
|
+
const previous = candidates.get(property);
|
|
832
|
+
if (!previous || compareAuthorCascade(candidate, previous) >= 0) {
|
|
833
|
+
candidates.set(property, candidate);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
for (const [name, candidate] of candidates)
|
|
837
|
+
aliases.set(name, candidate.value);
|
|
838
|
+
}
|
|
839
|
+
const matched = [];
|
|
840
|
+
for (const match of selectorMatches.get(element) ?? []) {
|
|
841
|
+
if (!ruleApplies(match.rule))
|
|
842
|
+
continue;
|
|
843
|
+
matched.push(match);
|
|
844
|
+
}
|
|
845
|
+
byElement.set(element, {
|
|
846
|
+
element,
|
|
847
|
+
matched,
|
|
848
|
+
allMatched: allSelectorMatches.get(element) ?? matched,
|
|
849
|
+
aliases,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
return { lineage, byElement };
|
|
853
|
+
}
|
|
854
|
+
function rowsFromMatches(el, matched, aliases, tokenTable, allMatched = matched) {
|
|
855
|
+
const map = new Map();
|
|
856
|
+
const mediaCandidatesByProperty = new Map();
|
|
857
|
+
// Sort by source order ascending so rules are processed lowest-first.
|
|
858
|
+
// Map.set() naturally overwrites: higher specificity rules processed later win,
|
|
859
|
+
// and equal-specificity rules get "last in stylesheet order wins" (stable sort).
|
|
860
|
+
const sorted = [...matched].sort((a, b) => (a.rule.sourceOrder ?? 0) - (b.rule.sourceOrder ?? 0));
|
|
861
|
+
const needsDirectionality = sorted.some(({ rule }) => rule.declarations.some(({ property }) => isLogicalBoxProperty(property)));
|
|
862
|
+
const directionality = needsDirectionality ? elementDirectionality(el) : undefined;
|
|
863
|
+
for (const match of allMatched) {
|
|
864
|
+
const mediaAtRules = match.rule.atRules?.filter((atRule) => atRule.kind === "media") ?? [];
|
|
865
|
+
if (mediaAtRules.length === 0)
|
|
866
|
+
continue;
|
|
867
|
+
for (const declaration of match.rule.declarations) {
|
|
868
|
+
for (const resolved of resolveDeclaration(declaration, tokenTable, aliases, directionality)) {
|
|
869
|
+
let candidates = mediaCandidatesByProperty.get(resolved.property);
|
|
870
|
+
if (!candidates) {
|
|
871
|
+
candidates = new Map();
|
|
872
|
+
mediaCandidatesByProperty.set(resolved.property, candidates);
|
|
873
|
+
}
|
|
874
|
+
for (const atRule of mediaAtRules) {
|
|
875
|
+
candidates.set(`${atRule.kind}\u0000${atRule.params}`, atRule);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
for (const m of sorted) {
|
|
881
|
+
const { rule, branch, specificity } = m;
|
|
882
|
+
for (const decl of rule.declarations) {
|
|
883
|
+
for (const resolved of resolveDeclaration(decl, tokenTable, aliases, directionality)) {
|
|
884
|
+
const candidate = {
|
|
885
|
+
property: resolved.property,
|
|
886
|
+
tokenName: resolved.tokenName,
|
|
887
|
+
declaredValue: resolved.declaredValue,
|
|
888
|
+
sourceProperty: resolved.sourceProperty,
|
|
889
|
+
resolvedValue: resolved.resolvedValue,
|
|
890
|
+
authored: resolved.declaredValue,
|
|
891
|
+
computed: "",
|
|
892
|
+
tokens: resolved.tokens,
|
|
893
|
+
opacity: resolved.opacity,
|
|
894
|
+
propertyOpacity: resolved.propertyOpacity,
|
|
895
|
+
color: resolved.color,
|
|
896
|
+
modifiers: resolved.modifiers,
|
|
897
|
+
capability: resolved.capability,
|
|
898
|
+
resolvedTokenValue: resolved.resolvedTokenValue,
|
|
899
|
+
diagnostic: resolved.diagnostic,
|
|
900
|
+
structure: resolved.structure,
|
|
901
|
+
atRules: rule.atRules,
|
|
902
|
+
confidence: resolved.tokenName ? "probable" : "unknown",
|
|
903
|
+
evidence: {
|
|
904
|
+
selector: branch,
|
|
905
|
+
sourceOrder: rule.sourceOrder,
|
|
906
|
+
specificity,
|
|
907
|
+
important: Boolean(resolved.important),
|
|
908
|
+
layer: rule.layer,
|
|
909
|
+
layerOrder: rule.layerOrder,
|
|
910
|
+
reason: resolved.tokenName ? "authored declaration references a catalog token" : "no catalog token reference",
|
|
911
|
+
},
|
|
912
|
+
};
|
|
913
|
+
const previous = map.get(resolved.property);
|
|
914
|
+
if (!previous || compareCandidate(candidate, previous) >= 0)
|
|
915
|
+
map.set(resolved.property, candidate);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
const rows = Array.from(map.values()).slice(0, MAX_PROPERTIES);
|
|
920
|
+
for (const row of rows) {
|
|
921
|
+
const candidates = mediaCandidatesByProperty.get(row.property);
|
|
922
|
+
if (!candidates || candidates.size === 0)
|
|
923
|
+
continue;
|
|
924
|
+
const winningMedia = (row.atRules ?? []).filter((atRule) => atRule.kind === "media");
|
|
925
|
+
const winningKeys = new Set(winningMedia.map((atRule) => `${atRule.kind}\u0000${atRule.params}`));
|
|
926
|
+
const allCandidates = [];
|
|
927
|
+
let insertedWinningStack = false;
|
|
928
|
+
for (const atRule of candidates.values()) {
|
|
929
|
+
const key = `${atRule.kind}\u0000${atRule.params}`;
|
|
930
|
+
if (winningKeys.has(key)) {
|
|
931
|
+
if (!insertedWinningStack) {
|
|
932
|
+
allCandidates.push(...winningMedia.map((winningAtRule) => ({ ...winningAtRule, active: true })));
|
|
933
|
+
insertedWinningStack = true;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
allCandidates.push({ ...atRule, active: false });
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (!insertedWinningStack) {
|
|
941
|
+
allCandidates.push(...winningMedia.map((winningAtRule) => ({ ...winningAtRule, active: true })));
|
|
942
|
+
}
|
|
943
|
+
row.atRuleCandidates = allCandidates;
|
|
944
|
+
}
|
|
945
|
+
inferTailwindV4ColorOpacity(el, tokenTable, rows);
|
|
946
|
+
for (const row of rows) {
|
|
947
|
+
if (row.capability !== "raw" || row.diagnostic)
|
|
948
|
+
continue;
|
|
949
|
+
const value = row.authored ?? row.declaredValue ?? "";
|
|
950
|
+
if (!canBecomeNumeric(value))
|
|
951
|
+
continue;
|
|
952
|
+
const numeric = row.resolvedValue ?? row.computed ?? "";
|
|
953
|
+
if (!numeric)
|
|
954
|
+
continue;
|
|
955
|
+
row.capability = capabilityFor(row.property, numeric, tokenTable);
|
|
956
|
+
}
|
|
957
|
+
return rows;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Package-private cascade fixture seam. This exercises selector, layer,
|
|
961
|
+
* importance, and interaction-state coordination while still crossing the
|
|
962
|
+
* public value-semantics Interface through `resolveDeclaration`.
|
|
963
|
+
* It is intentionally absent from the package exports.
|
|
964
|
+
*/
|
|
965
|
+
export function resolveRuleFixture(el, rules, tokenTable) {
|
|
966
|
+
const ruleApplies = makeRuleApplies(el);
|
|
967
|
+
const matched = [];
|
|
968
|
+
const allMatched = [];
|
|
969
|
+
for (const rule of rules) {
|
|
970
|
+
const match = matchRuleForElement(el, { rule, selectorText: rule.selectorText });
|
|
971
|
+
if (!match)
|
|
972
|
+
continue;
|
|
973
|
+
allMatched.push(match);
|
|
974
|
+
if (rule.active === false)
|
|
975
|
+
continue;
|
|
976
|
+
if (!ruleApplies(rule))
|
|
977
|
+
continue;
|
|
978
|
+
matched.push(match);
|
|
979
|
+
}
|
|
980
|
+
const localAliases = collectLocalAliases(el, rules, ruleApplies);
|
|
981
|
+
return rowsFromMatches(el, matched, localAliases, tokenTable, allMatched);
|
|
982
|
+
}
|
|
983
|
+
// Selector branch splits are pure functions of the selector text and repeat
|
|
984
|
+
// for every element and resolution sweep. Memoize them so repeated matching
|
|
985
|
+
// passes skip re-scanning the same selector strings. Bounded like the other
|
|
986
|
+
// match caches; the snapshot's selector set is finite per stylesheet revision.
|
|
987
|
+
const SELECTOR_SPLIT_CACHE_MAX = 8192;
|
|
988
|
+
const selectorSplitMemo = new Map();
|
|
989
|
+
// The matched branch for one (element, selector) pair is a pure function of
|
|
990
|
+
// the element's selector-relevant state and the CSSOM snapshot — except for
|
|
991
|
+
// selectors whose match depends on transient state the revisions cannot see
|
|
992
|
+
// (`:hover`, `:checked`, sibling combinators, …). Those keep the freshness
|
|
993
|
+
// contract of the element-sensitive bucket and are matched uncached.
|
|
994
|
+
// Resolution sweeps re-match the same state-independent selector strings
|
|
995
|
+
// against the same lineage elements across cascades (authored base, stable,
|
|
996
|
+
// interaction states) and across the active/all rule sets, so memoizing the
|
|
997
|
+
// branch turns repeat sweeps into cache hits. Invalidation follows the
|
|
998
|
+
// document revisions, the same strategy as the per-element match caches.
|
|
999
|
+
const ELEMENT_BRANCH_CACHE_MAX = 16384;
|
|
1000
|
+
const elementBranchMemo = new WeakMap();
|
|
1001
|
+
function isBranchMemoizable(selectorText) {
|
|
1002
|
+
return !TRANSIENT_SELECTOR.test(selectorText) && !isElementSensitiveSelector(selectorText);
|
|
1003
|
+
}
|
|
1004
|
+
function matchingSelectorBranch(el, selectorText) {
|
|
1005
|
+
const doc = el.ownerDocument ?? document;
|
|
1006
|
+
const revisions = getDocumentRevisions(doc);
|
|
1007
|
+
let bySelector = elementBranchMemo.get(el);
|
|
1008
|
+
const memoizable = isBranchMemoizable(selectorText);
|
|
1009
|
+
if (memoizable) {
|
|
1010
|
+
if (!bySelector) {
|
|
1011
|
+
bySelector = new Map();
|
|
1012
|
+
elementBranchMemo.set(el, bySelector);
|
|
1013
|
+
}
|
|
1014
|
+
const cached = bySelector.get(selectorText);
|
|
1015
|
+
if (cached
|
|
1016
|
+
&& cached.elementRevision === revisions.element
|
|
1017
|
+
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
1018
|
+
return cached.branch;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
let branches = selectorSplitMemo.get(selectorText);
|
|
1022
|
+
if (!branches) {
|
|
1023
|
+
branches = splitTopLevel(selectorText, ",");
|
|
1024
|
+
if (selectorSplitMemo.size >= SELECTOR_SPLIT_CACHE_MAX)
|
|
1025
|
+
selectorSplitMemo.clear();
|
|
1026
|
+
selectorSplitMemo.set(selectorText, branches);
|
|
1027
|
+
}
|
|
1028
|
+
let branch = null;
|
|
1029
|
+
for (const candidate of branches) {
|
|
1030
|
+
try {
|
|
1031
|
+
if (el.matches(candidate.trim())) {
|
|
1032
|
+
branch = candidate.trim();
|
|
1033
|
+
break;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
catch { /* invalid/unsupported selector */ }
|
|
1037
|
+
}
|
|
1038
|
+
if (memoizable) {
|
|
1039
|
+
if (bySelector.size >= ELEMENT_BRANCH_CACHE_MAX)
|
|
1040
|
+
bySelector.clear();
|
|
1041
|
+
bySelector.set(selectorText, {
|
|
1042
|
+
elementRevision: revisions.element,
|
|
1043
|
+
stylesheetRevision: revisions.stylesheet,
|
|
1044
|
+
branch,
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
return branch;
|
|
1048
|
+
}
|
|
1049
|
+
function compareCandidate(a, b) {
|
|
1050
|
+
return compareAuthorCascade({
|
|
1051
|
+
important: a.evidence.important,
|
|
1052
|
+
inline: a.evidence.selector === "[style]",
|
|
1053
|
+
layer: a.evidence.layer,
|
|
1054
|
+
layerOrder: a.evidence.layerOrder,
|
|
1055
|
+
specificity: a.evidence.specificity ?? 0,
|
|
1056
|
+
sourceOrder: a.evidence.sourceOrder ?? 0,
|
|
1057
|
+
}, {
|
|
1058
|
+
important: b.evidence.important,
|
|
1059
|
+
inline: b.evidence.selector === "[style]",
|
|
1060
|
+
layer: b.evidence.layer,
|
|
1061
|
+
layerOrder: b.evidence.layerOrder,
|
|
1062
|
+
specificity: b.evidence.specificity ?? 0,
|
|
1063
|
+
sourceOrder: b.evidence.sourceOrder ?? 0,
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
function inferTailwindV4ColorOpacity(el, tokenTable, rows) {
|
|
1067
|
+
const row = rows.find((candidate) => candidate.property === "background-color" || candidate.property === "background");
|
|
1068
|
+
if (!row || row.tokenName)
|
|
1069
|
+
return;
|
|
1070
|
+
for (const className of Array.from(el.classList)) {
|
|
1071
|
+
const utility = readTailwindV4AlphaUtility(className);
|
|
1072
|
+
if (!utility)
|
|
1073
|
+
continue;
|
|
1074
|
+
const { baseName, alpha } = utility;
|
|
1075
|
+
const entry = tokenTable[baseName];
|
|
1076
|
+
if (!entry || entry.adapter !== "tailwind-v4")
|
|
1077
|
+
continue;
|
|
1078
|
+
const authored = tailwindV4AlphaExpression(baseName, alpha);
|
|
1079
|
+
row.tokenName = baseName;
|
|
1080
|
+
row.declaredValue = authored;
|
|
1081
|
+
row.authored = authored;
|
|
1082
|
+
row.tokens = [{ name: baseName, origin: inspectorTokenOrigin(entry) }];
|
|
1083
|
+
const colorInterpretation = resolveTokenValue(authored, tokenTable, EMPTY_LOCAL_ALIASES);
|
|
1084
|
+
row.opacity = colorInterpretation.opacity;
|
|
1085
|
+
row.color = colorInterpretation.color;
|
|
1086
|
+
row.modifiers = [{ kind: "alpha", value: alpha }];
|
|
1087
|
+
row.capability = "color";
|
|
1088
|
+
row.resolvedTokenValue = entry.value;
|
|
1089
|
+
row.evidence.reason = "Tailwind v4 opacity utility mapped to its base catalog token";
|
|
1090
|
+
row.confidence = "probable";
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
// Splits CSS values at top-level combinators or an optional delimiter.
|
|
1095
|
+
function splitTopLevel(s, sep) {
|
|
1096
|
+
const parts = [];
|
|
1097
|
+
let depth = 0;
|
|
1098
|
+
let start = 0;
|
|
1099
|
+
for (let i = 0; i < s.length; i++) {
|
|
1100
|
+
if (s[i] === "(")
|
|
1101
|
+
depth++;
|
|
1102
|
+
else if (s[i] === ")")
|
|
1103
|
+
depth--;
|
|
1104
|
+
else if (depth === 0 && sep && s[i] === sep) {
|
|
1105
|
+
parts.push(s.slice(start, i));
|
|
1106
|
+
start = i + 1;
|
|
1107
|
+
}
|
|
1108
|
+
else if (depth === 0 && !sep && (s[i] === " " || s[i] === ">" || s[i] === "+" || s[i] === "~")) {
|
|
1109
|
+
// Only split on combinators that have non-space around them
|
|
1110
|
+
// If previous char was also a combinator, skip
|
|
1111
|
+
if (i > start) {
|
|
1112
|
+
parts.push(s.slice(start, i).trim());
|
|
1113
|
+
}
|
|
1114
|
+
start = i + 1;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const last = s.slice(start).trim();
|
|
1118
|
+
if (last)
|
|
1119
|
+
parts.push(last);
|
|
1120
|
+
return parts;
|
|
1121
|
+
}
|
|
1122
|
+
const stateResolutionSnapshots = new WeakMap();
|
|
1123
|
+
const INHERITED_PROPERTIES = new Set([
|
|
1124
|
+
"color", "font", "font-family", "font-size", "font-style", "font-variant", "font-weight",
|
|
1125
|
+
"letter-spacing", "line-height", "text-align", "text-indent", "text-transform", "visibility",
|
|
1126
|
+
"white-space", "word-spacing", "cursor",
|
|
1127
|
+
]);
|
|
1128
|
+
function resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible) {
|
|
1129
|
+
const computed = getElementComputedStyle(el);
|
|
1130
|
+
const seenProperties = new Set(result.map((p) => p.property));
|
|
1131
|
+
let ancestor = el.parentElement;
|
|
1132
|
+
while (ancestor) {
|
|
1133
|
+
const entry = lineage.byElement.get(ancestor);
|
|
1134
|
+
if (entry) {
|
|
1135
|
+
const ancestorComputed = getElementComputedStyle(ancestor);
|
|
1136
|
+
for (const candidate of rowsFromMatches(ancestor, entry.matched, entry.aliases, tokenTable, entry.allMatched)) {
|
|
1137
|
+
if (seenProperties.has(candidate.property))
|
|
1138
|
+
continue;
|
|
1139
|
+
if (!INHERITED_PROPERTIES.has(candidate.property) && !candidate.property.startsWith("--"))
|
|
1140
|
+
continue;
|
|
1141
|
+
const ancestorVal = ancestorComputed.getPropertyValue(candidate.property).trim();
|
|
1142
|
+
const elVal = computed.getPropertyValue(candidate.property).trim();
|
|
1143
|
+
if (ancestorVal && ancestorVal === elVal) {
|
|
1144
|
+
hydratePropertyOpacity(candidate, ancestorVal);
|
|
1145
|
+
result.push({
|
|
1146
|
+
...candidate,
|
|
1147
|
+
resolvedValue: elVal,
|
|
1148
|
+
confidence: candidate.tokenName && candidateMatchesPainted(ancestor, candidate, ancestorVal) && !inaccessible && !candidate.evidence.layer ? "exact" : candidate.tokenName ? "probable" : "unknown",
|
|
1149
|
+
evidence: { ...candidate.evidence, inheritedFrom: ancestor.tagName.toLowerCase(), inaccessibleStylesheet: inaccessible || undefined, reason: "inherited property traced through the ancestor cascade" },
|
|
1150
|
+
});
|
|
1151
|
+
seenProperties.add(candidate.property);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
ancestor = ancestor.parentElement;
|
|
1156
|
+
}
|
|
1157
|
+
return result;
|
|
1158
|
+
}
|
|
1159
|
+
export function getResolvedProperties(el, tokenTable) {
|
|
1160
|
+
const doc = el.ownerDocument ?? document;
|
|
1161
|
+
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1162
|
+
const lineage = resolveLineage(el, rules, "live");
|
|
1163
|
+
const entry = lineage.byElement.get(el);
|
|
1164
|
+
const result = rowsFromMatches(el, entry.matched, entry.aliases, tokenTable, entry.allMatched);
|
|
1165
|
+
const computed = getElementComputedStyle(el);
|
|
1166
|
+
for (const prop of result) {
|
|
1167
|
+
const cv = computed.getPropertyValue(prop.property);
|
|
1168
|
+
if (cv && !/\b(?:var|calc)\s*\(/.test(cv)) {
|
|
1169
|
+
prop.resolvedValue = cv;
|
|
1170
|
+
prop.computed = cv;
|
|
1171
|
+
hydratePropertyOpacity(prop, cv);
|
|
1172
|
+
const validated = candidateMatchesPainted(el, prop, cv);
|
|
1173
|
+
prop.confidence = validated && !inaccessible && !prop.evidence.layer ? "exact" : prop.tokenName ? "probable" : "unknown";
|
|
1174
|
+
prop.evidence.inaccessibleStylesheet = inaccessible || undefined;
|
|
1175
|
+
prop.evidence.reason = prop.tokenName
|
|
1176
|
+
? (validated ? "authored token declaration validated against computed style" : "authored token candidate could not be proven uniquely")
|
|
1177
|
+
: "painted value has no attributable catalog token";
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
1181
|
+
hydratePropertyOpacityRows(result, computed);
|
|
1182
|
+
promoteNumericCalcRows(result, tokenTable);
|
|
1183
|
+
return resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
1184
|
+
}
|
|
1185
|
+
function applyInlineDeclarations(el, result, tokenTable, computed, inaccessible) {
|
|
1186
|
+
// Inline declarations participate in the cascade and are explicit evidence,
|
|
1187
|
+
// but token attribution is only exact when the authored inline value uses a known token.
|
|
1188
|
+
const inlineProperties = Array.from(el.style);
|
|
1189
|
+
const directionality = inlineProperties.some(isLogicalBoxProperty)
|
|
1190
|
+
? directionalityFromComputed(el, computed)
|
|
1191
|
+
: undefined;
|
|
1192
|
+
for (const property of inlineProperties) {
|
|
1193
|
+
const value = el.style.getPropertyValue(property);
|
|
1194
|
+
const declarations = resolveDeclaration({
|
|
1195
|
+
property,
|
|
1196
|
+
value,
|
|
1197
|
+
important: el.style.getPropertyPriority(property) === "important",
|
|
1198
|
+
}, tokenTable, EMPTY_LOCAL_ALIASES, directionality);
|
|
1199
|
+
for (const declaration of declarations) {
|
|
1200
|
+
const painted = computed.getPropertyValue(declaration.property);
|
|
1201
|
+
const row = {
|
|
1202
|
+
property: declaration.property,
|
|
1203
|
+
tokenName: declaration.tokenName,
|
|
1204
|
+
declaredValue: declaration.declaredValue,
|
|
1205
|
+
sourceProperty: declaration.sourceProperty,
|
|
1206
|
+
resolvedValue: painted,
|
|
1207
|
+
authored: declaration.declaredValue,
|
|
1208
|
+
computed: painted,
|
|
1209
|
+
tokens: declaration.tokens,
|
|
1210
|
+
opacity: declaration.opacity,
|
|
1211
|
+
propertyOpacity: declaration.propertyOpacity,
|
|
1212
|
+
color: declaration.color,
|
|
1213
|
+
modifiers: declaration.modifiers,
|
|
1214
|
+
capability: declaration.capability,
|
|
1215
|
+
resolvedTokenValue: declaration.resolvedValue,
|
|
1216
|
+
diagnostic: declaration.diagnostic,
|
|
1217
|
+
structure: declaration.structure,
|
|
1218
|
+
confidence: "unknown",
|
|
1219
|
+
evidence: { selector: "[style]", specificity: 100000000, important: Boolean(declaration.important), inaccessibleStylesheet: inaccessible || undefined, reason: declaration.tokenName ? "inline token declaration validated against computed style" : "inline declaration contains no catalog token" },
|
|
1220
|
+
};
|
|
1221
|
+
if (declaration.tokenName && candidateMatchesPainted(el, row, painted)) {
|
|
1222
|
+
row.confidence = inaccessible ? "probable" : "exact";
|
|
1223
|
+
row.evidence.reason = "inline token declaration validated against computed style";
|
|
1224
|
+
}
|
|
1225
|
+
const index = result.findIndex((item) => item.property === declaration.property);
|
|
1226
|
+
if (index < 0)
|
|
1227
|
+
result.push(row);
|
|
1228
|
+
else if (compareCandidate(row, result[index]) >= 0)
|
|
1229
|
+
result[index] = row;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
function promoteNumericCalcRows(result, tokenTable) {
|
|
1234
|
+
// When a calc() expression only references static tokens the browser has
|
|
1235
|
+
// already resolved it to a pixel value. Reclassify the row so the UI
|
|
1236
|
+
// shows the pixel value instead of the raw calc() string and allows
|
|
1237
|
+
// numeric editing (nudge / token swap).
|
|
1238
|
+
for (const prop of result) {
|
|
1239
|
+
if (prop.capability !== "raw" || prop.diagnostic)
|
|
1240
|
+
continue;
|
|
1241
|
+
const value = prop.authored ?? prop.declaredValue ?? "";
|
|
1242
|
+
if (!canBecomeNumeric(value))
|
|
1243
|
+
continue;
|
|
1244
|
+
const numeric = prop.computed ?? prop.resolvedValue ?? "";
|
|
1245
|
+
if (!numeric)
|
|
1246
|
+
continue;
|
|
1247
|
+
prop.capability = capabilityFor(prop.property, numeric, tokenTable);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
const INTERACTION_SELECTOR = /:(hover|active|focus-visible|focus|disabled)(?:\b|\()/g;
|
|
1251
|
+
function selectorForState(selector, state) {
|
|
1252
|
+
const states = new Set();
|
|
1253
|
+
INTERACTION_SELECTOR.lastIndex = 0;
|
|
1254
|
+
let match;
|
|
1255
|
+
while ((match = INTERACTION_SELECTOR.exec(selector)) !== null)
|
|
1256
|
+
states.add(match[1]);
|
|
1257
|
+
if (state === "base")
|
|
1258
|
+
return states.size === 0 ? selector : null;
|
|
1259
|
+
if (!states.has(state))
|
|
1260
|
+
return states.size === 0 ? selector : null;
|
|
1261
|
+
// CSSOM cannot ask the browser whether a hypothetical pseudo-class matches.
|
|
1262
|
+
// Removing interaction pseudo-classes gives the authored rule a stable
|
|
1263
|
+
// element match for inspection. The real rule remains untouched.
|
|
1264
|
+
return selector.replace(INTERACTION_SELECTOR, "");
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Resolve the authored cascade for a chosen interaction state without relying
|
|
1268
|
+
* on the pointer's current location. This is intentionally attribution-first:
|
|
1269
|
+
* values with no authored declaration still fall back to the browser's live
|
|
1270
|
+
* computed value in the field layer.
|
|
1271
|
+
*/
|
|
1272
|
+
export function getResolvedPropertiesForState(el, tokenTable, state) {
|
|
1273
|
+
const doc = el.ownerDocument ?? document;
|
|
1274
|
+
const revisions = getDocumentRevisions(doc);
|
|
1275
|
+
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1276
|
+
const dynamicMatchKey = elementSensitiveMatchKey(el, rules, state);
|
|
1277
|
+
let snapshots = stateResolutionSnapshots.get(el);
|
|
1278
|
+
if (!snapshots) {
|
|
1279
|
+
snapshots = new Map();
|
|
1280
|
+
stateResolutionSnapshots.set(el, snapshots);
|
|
1281
|
+
}
|
|
1282
|
+
const cached = snapshots.get(state);
|
|
1283
|
+
if (cached
|
|
1284
|
+
&& cached.elementRevision === revisions.element
|
|
1285
|
+
&& cached.stylesheetRevision === revisions.stylesheet
|
|
1286
|
+
&& cached.tokenTable === tokenTable
|
|
1287
|
+
&& cached.dynamicMatchKey === dynamicMatchKey) {
|
|
1288
|
+
return cached.rows;
|
|
1289
|
+
}
|
|
1290
|
+
const lineage = resolveLineage(el, rules, state);
|
|
1291
|
+
const entry = lineage.byElement.get(el);
|
|
1292
|
+
const result = rowsFromMatches(el, entry.matched, entry.aliases, tokenTable, entry.allMatched);
|
|
1293
|
+
const computed = getElementComputedStyle(el);
|
|
1294
|
+
for (const prop of result) {
|
|
1295
|
+
const cv = computed.getPropertyValue(prop.property);
|
|
1296
|
+
if (cv && !/\b(?:var|calc)\s*\(/.test(cv)) {
|
|
1297
|
+
// A selected interaction state is hypothetical: the element may still
|
|
1298
|
+
// be painting its Base styles. `rowsFromMatches` has already resolved
|
|
1299
|
+
// the authored declaration for the requested state, so preserve that
|
|
1300
|
+
// value for editor controls and keep the live CSSOM value in `computed`.
|
|
1301
|
+
if (state === "base" || !prop.resolvedValue)
|
|
1302
|
+
prop.resolvedValue = cv;
|
|
1303
|
+
prop.computed = cv;
|
|
1304
|
+
const validated = candidateMatchesPainted(el, prop, cv);
|
|
1305
|
+
prop.confidence = validated && !inaccessible && !prop.evidence.layer ? "exact" : prop.tokenName ? "probable" : "unknown";
|
|
1306
|
+
prop.evidence.inaccessibleStylesheet = inaccessible || undefined;
|
|
1307
|
+
prop.evidence.reason = prop.tokenName
|
|
1308
|
+
? (validated ? "authored token declaration validated against computed style" : "authored token candidate could not be proven uniquely")
|
|
1309
|
+
: "painted value has no attributable catalog token";
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
1313
|
+
hydratePropertyOpacityRows(result, computed);
|
|
1314
|
+
promoteNumericCalcRows(result, tokenTable);
|
|
1315
|
+
const rows = resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
1316
|
+
registerWithAncestors(el);
|
|
1317
|
+
snapshots.set(state, {
|
|
1318
|
+
elementRevision: revisions.element,
|
|
1319
|
+
stylesheetRevision: revisions.stylesheet,
|
|
1320
|
+
tokenTable,
|
|
1321
|
+
dynamicMatchKey,
|
|
1322
|
+
rows,
|
|
1323
|
+
});
|
|
1324
|
+
return rows;
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Interaction states whose pseudo-class substring appears in at least one
|
|
1328
|
+
* snapshot selector, memoized per CSSOM snapshot. The availability check for a
|
|
1329
|
+
* state is `matched.some((m) => m.rule.selectorText.includes(":state"))`, so a
|
|
1330
|
+
* state whose substring appears nowhere can never become available for any
|
|
1331
|
+
* element; skipping its per-state match sweep is exactly equivalent and
|
|
1332
|
+
* removes the dominant cost of re-scanning interaction states after every
|
|
1333
|
+
* stylesheet revision.
|
|
1334
|
+
*/
|
|
1335
|
+
const statesWithPseudoInSnapshot = new WeakMap();
|
|
1336
|
+
function statesWithPseudoInSelectors(rules) {
|
|
1337
|
+
let present = statesWithPseudoInSnapshot.get(rules);
|
|
1338
|
+
if (!present) {
|
|
1339
|
+
present = new Set();
|
|
1340
|
+
for (const rule of rules) {
|
|
1341
|
+
for (const state of INTERACTION_STATES) {
|
|
1342
|
+
if (!present.has(state) && rule.selectorText.includes(`:${state}`)) {
|
|
1343
|
+
present.add(state);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
statesWithPseudoInSnapshot.set(rules, present);
|
|
1348
|
+
}
|
|
1349
|
+
return present;
|
|
1350
|
+
}
|
|
1351
|
+
export function getAvailableInteractionStates(el) {
|
|
1352
|
+
const doc = el.ownerDocument ?? document;
|
|
1353
|
+
const { rules } = collectCssomRules(doc);
|
|
1354
|
+
const present = statesWithPseudoInSelectors(rules);
|
|
1355
|
+
const available = ["base"];
|
|
1356
|
+
for (const state of INTERACTION_STATES) {
|
|
1357
|
+
if (!present.has(state))
|
|
1358
|
+
continue;
|
|
1359
|
+
const matched = getCachedElementMatches(el, rules, state);
|
|
1360
|
+
const relevant = matched.some((m) => m.rule.selectorText.includes(`:${state}`));
|
|
1361
|
+
if (relevant)
|
|
1362
|
+
available.push(state);
|
|
1363
|
+
}
|
|
1364
|
+
return available;
|
|
1365
|
+
}
|
|
1366
|
+
const stableTokenCache = new WeakMap();
|
|
1367
|
+
/**
|
|
1368
|
+
* Resolves the cascade with transient interaction selectors removed. Used when
|
|
1369
|
+
* an editor must stay linked to the stable token beneath a live :hover/:focus
|
|
1370
|
+
* paint. Memoized per (element, revisions, tokenTable).
|
|
1371
|
+
*/
|
|
1372
|
+
export function getResolvedPropertiesStable(el, tokenTable) {
|
|
1373
|
+
const doc = el.ownerDocument ?? document;
|
|
1374
|
+
const revisions = getDocumentRevisions(doc);
|
|
1375
|
+
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1376
|
+
const dynamicMatchKey = elementSensitiveMatchKey(el, rules, "stable");
|
|
1377
|
+
const cached = stableTokenCache.get(el);
|
|
1378
|
+
if (cached
|
|
1379
|
+
&& cached.elementRevision === revisions.element
|
|
1380
|
+
&& cached.stylesheetRevision === revisions.stylesheet
|
|
1381
|
+
&& cached.tokenTable === tokenTable
|
|
1382
|
+
&& cached.dynamicMatchKey === dynamicMatchKey) {
|
|
1383
|
+
return cached.rows;
|
|
1384
|
+
}
|
|
1385
|
+
const lineage = resolveLineage(el, rules, "stable");
|
|
1386
|
+
const entry = lineage.byElement.get(el);
|
|
1387
|
+
const result = rowsFromMatches(el, entry.matched, entry.aliases, tokenTable, entry.allMatched);
|
|
1388
|
+
const computed = getElementComputedStyle(el);
|
|
1389
|
+
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
1390
|
+
hydratePropertyOpacityRows(result, computed);
|
|
1391
|
+
promoteNumericCalcRows(result, tokenTable);
|
|
1392
|
+
const rows = resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
1393
|
+
stableTokenCache.set(el, {
|
|
1394
|
+
elementRevision: revisions.element,
|
|
1395
|
+
stylesheetRevision: revisions.stylesheet,
|
|
1396
|
+
tokenTable,
|
|
1397
|
+
dynamicMatchKey,
|
|
1398
|
+
rows,
|
|
1399
|
+
});
|
|
1400
|
+
return rows;
|
|
1401
|
+
}
|