@bastani/atomic 0.9.13 → 0.9.14-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +3 -3
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/README.md +6 -4
- package/dist/builtin/subagents/agents/code-simplifier.md +1 -1
- package/dist/builtin/subagents/agents/codebase-analyzer.md +1 -1
- package/dist/builtin/subagents/agents/codebase-locator.md +1 -1
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +1 -1
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +1 -1
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +1 -1
- package/dist/builtin/subagents/agents/codebase-research-locator.md +1 -1
- package/dist/builtin/subagents/agents/debugger.md +1 -1
- package/dist/builtin/subagents/agents/worker.md +1 -1
- package/dist/builtin/subagents/package.json +6 -5
- package/dist/builtin/subagents/src/agents/agent-discovery.ts +17 -11
- package/dist/builtin/subagents/src/agents/agent-loaders.ts +115 -43
- package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +10 -8
- package/dist/builtin/subagents/src/agents/agent-management.ts +8 -7
- package/dist/builtin/subagents/src/agents/agent-overrides.ts +2 -2
- package/dist/builtin/subagents/src/agents/agent-scope.ts +1 -1
- package/dist/builtin/subagents/src/agents/agent-selection.ts +1 -1
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +24 -3
- package/dist/builtin/subagents/src/agents/agent-types.ts +3 -1
- package/dist/builtin/subagents/src/agents/agents.ts +7 -5
- package/dist/builtin/subagents/src/agents/frontmatter.ts +67 -22
- package/dist/builtin/subagents/src/agents/identity.ts +1 -1
- package/dist/builtin/subagents/src/agents/skills-paths.ts +1 -1
- package/dist/builtin/subagents/src/agents/skills.ts +64 -6
- package/dist/builtin/subagents/src/extension/config.ts +1 -1
- package/dist/builtin/subagents/src/extension/control-notices.ts +2 -2
- package/dist/builtin/subagents/src/extension/doctor.ts +15 -5
- package/dist/builtin/subagents/src/extension/fanout-child.ts +12 -12
- package/dist/builtin/subagents/src/extension/index.ts +23 -23
- package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
- package/dist/builtin/subagents/src/extension/schemas.ts +1 -1
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +3 -3
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +3 -3
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +1 -1
- package/dist/builtin/subagents/src/intercom/supervisor-authorization.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/completion-notification.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-intercom-detach.ts +3 -3
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-updates.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/execution-utils.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +27 -11
- package/dist/builtin/subagents/src/runs/foreground/notify.ts +3 -3
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +11 -11
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +3 -3
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +15 -14
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +19 -19
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +14 -13
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +9 -9
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +7 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-worktree.ts +6 -6
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +24 -23
- package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +1 -1
- package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +3 -3
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +3 -3
- package/dist/builtin/subagents/src/runs/inprocess/prompt-behavior.ts +17 -8
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +60 -12
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-api.ts +8 -8
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-control.ts +3 -3
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-core.ts +4 -4
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-projection.ts +4 -4
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-registry.ts +5 -5
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-rendering.ts +3 -3
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-sanitize.ts +3 -3
- package/dist/builtin/subagents/src/runs/inprocess.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +3 -3
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +2 -2
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +1 -1
- package/dist/builtin/subagents/src/shared/artifacts.ts +2 -2
- package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +1 -1
- package/dist/builtin/subagents/src/shared/fast-mode.ts +1 -1
- package/dist/builtin/subagents/src/shared/formatters.ts +2 -2
- package/dist/builtin/subagents/src/shared/jsonl-writer.ts +1 -1
- package/dist/builtin/subagents/src/shared/model-resolution.ts +1 -1
- package/dist/builtin/subagents/src/shared/session-tokens.ts +1 -1
- package/dist/builtin/subagents/src/shared/settings.ts +4 -4
- package/dist/builtin/subagents/src/shared/status-format.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-config.ts +16 -4
- package/dist/builtin/subagents/src/shared/types-depth.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-nested.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-output.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-results.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-runtime.ts +1 -1
- package/dist/builtin/subagents/src/shared/types.ts +6 -6
- package/dist/builtin/subagents/src/shared/utils.ts +3 -3
- package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +1 -1
- package/dist/builtin/subagents/src/slash/slash-bridge.ts +3 -3
- package/dist/builtin/subagents/src/slash/slash-commands.ts +6 -6
- package/dist/builtin/subagents/src/slash/slash-live-state.ts +3 -3
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-layout.ts +1 -1
- package/dist/builtin/subagents/src/tui/render-progress.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +7 -7
- package/dist/builtin/subagents/src/tui/render-result.ts +10 -10
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +5 -5
- package/dist/builtin/subagents/src/tui/render.ts +4 -4
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +57 -0
- package/dist/builtin/workflows/README.md +39 -8
- package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +16 -5
- package/dist/builtin/workflows/builtin/adversarial-verification.ts +4 -0
- package/dist/builtin/workflows/builtin/classify-and-act.ts +4 -0
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +4 -0
- package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +12 -4
- package/dist/builtin/workflows/builtin/generate-and-filter.ts +4 -0
- package/dist/builtin/workflows/builtin/goal-models.ts +8 -10
- package/dist/builtin/workflows/builtin/goal.ts +4 -0
- package/dist/builtin/workflows/builtin/index.d.ts +0 -4
- package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +4 -2
- package/dist/builtin/workflows/builtin/loop-until-done.ts +4 -0
- package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +305 -0
- package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +115 -171
- package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +9 -25
- package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +19 -86
- package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -6
- package/dist/builtin/workflows/builtin/open-claude-design.d.ts +0 -5
- package/dist/builtin/workflows/builtin/open-claude-design.ts +11 -15
- package/dist/builtin/workflows/builtin/ralph-models.ts +24 -29
- package/dist/builtin/workflows/builtin/ralph.ts +4 -0
- package/dist/builtin/workflows/builtin/tournament-runner.ts +6 -3
- package/dist/builtin/workflows/builtin/tournament.ts +4 -0
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/skills/impeccable/SKILL.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/agents/impeccable_asset_producer.toml +25 -31
- package/dist/builtin/workflows/skills/impeccable/agents/impeccable_documenter.toml +1 -0
- package/dist/builtin/workflows/skills/impeccable/agents/impeccable_finish_reviewer.toml +16 -10
- package/dist/builtin/workflows/skills/impeccable/agents/impeccable_manual_edit_applier.toml +2 -2
- package/dist/builtin/workflows/skills/impeccable/reference/android.md +6 -0
- package/dist/builtin/workflows/skills/impeccable/reference/animate.md +3 -0
- package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +3 -1
- package/dist/builtin/workflows/skills/impeccable/reference/craft-floor.md +6 -1
- package/dist/builtin/workflows/skills/impeccable/reference/critique.md +18 -2
- package/dist/builtin/workflows/skills/impeccable/reference/degraded/asset-producer.md +24 -30
- package/dist/builtin/workflows/skills/impeccable/reference/degraded/documenter.md +1 -0
- package/dist/builtin/workflows/skills/impeccable/reference/degraded/finish-reviewer.md +16 -10
- package/dist/builtin/workflows/skills/impeccable/reference/degraded/manual-edit-applier.md +2 -2
- package/dist/builtin/workflows/skills/impeccable/reference/distill.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/doctor.md +1 -0
- package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/extract.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +12 -6
- package/dist/builtin/workflows/skills/impeccable/reference/init.md +12 -3
- package/dist/builtin/workflows/skills/impeccable/reference/ios.md +6 -0
- package/dist/builtin/workflows/skills/impeccable/reference/live-setup.md +102 -0
- package/dist/builtin/workflows/skills/impeccable/reference/live.md +113 -521
- package/dist/builtin/workflows/skills/impeccable/reference/new-work.md +39 -26
- package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/polish.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/visualize.md +25 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/concept-seed.mjs +332 -221
- package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +10 -19
- package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +91 -38
- package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +19 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +56 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +10 -16
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +140 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +1174 -694
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns.mjs +1 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +5 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +431 -45
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +10 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +47 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/node/file-system.mjs +23 -22
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +0 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +384 -384
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/color.mjs +466 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/doctor.mjs +2 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/embed-prompt.mjs +270 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/generate-image.mjs +51 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +70 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +336 -90
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/composition-catalog.mjs +38 -3
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/concept-catalog.mjs +77 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/design-parser.mjs +102 -19
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +44 -59
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +3 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/open-system-browser.mjs +57 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/roll-selection.mjs +369 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness-deep.mjs +31 -3
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness.mjs +73 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live/accept-css.mjs +617 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/accept-verify.mjs +71 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/browser-script-parts.mjs +29 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live/event-validation.mjs +54 -7
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/astro.mjs +47 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/detect-utils.mjs +157 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/index.mjs +143 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/journal.mjs +205 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/nextjs.mjs +49 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/nuxt.mjs +170 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/script-src.mjs +26 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/static-html.mjs +26 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/sveltekit.mjs +71 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/tag-strategy.mjs +247 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/tanstack-start.mjs +70 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/frameworks/vite-generic.mjs +42 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/instructions.mjs +169 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/roots.mjs +670 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/session-store.mjs +232 -35
- package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-ast.mjs +969 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +621 -74
- package/dist/builtin/workflows/skills/impeccable/scripts/live/sveltekit-adapter.mjs +66 -28
- package/dist/builtin/workflows/skills/impeccable/scripts/live/tanstack-adapter.mjs +12 -12
- package/dist/builtin/workflows/skills/impeccable/scripts/live/ui-surfaces.mjs +75 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/vocabulary.mjs +135 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +23 -37
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +945 -115
- package/dist/builtin/workflows/skills/impeccable/scripts/live-complete.mjs +33 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/live-copy-edit-agent.mjs +132 -15
- package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +173 -425
- package/dist/builtin/workflows/skills/impeccable/scripts/live-insert.mjs +2 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +16 -3
- package/dist/builtin/workflows/skills/impeccable/scripts/live-resume.mjs +39 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +194 -27
- package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +10 -4
- package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +50 -31
- package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +91 -28
- package/dist/builtin/workflows/skills/impeccable/scripts/pin.mjs +8 -5
- package/dist/builtin/workflows/skills/impeccable/scripts/serve-question.mjs +759 -109
- package/dist/builtin/workflows/src/authoring/workflow.ts +9 -0
- package/dist/builtin/workflows/src/authoring.d.ts +3 -0
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +23 -2
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +30 -17
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +14 -0
- package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +60 -57
- package/dist/builtin/workflows/src/durable/dbos-process-owner.ts +87 -0
- package/dist/builtin/workflows/src/durable/dbos-registration-diagnostics.ts +113 -0
- package/dist/builtin/workflows/src/durable/factory.ts +11 -8
- package/dist/builtin/workflows/src/durable/tool-failure-checkpoint.ts +5 -0
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +186 -20
- package/dist/builtin/workflows/src/durable/types.ts +4 -0
- package/dist/builtin/workflows/src/durable/workflow-heartbeat-anchor.ts +153 -0
- package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +28 -0
- package/dist/builtin/workflows/src/engine/run-tool-control-registry.ts +9 -1
- package/dist/builtin/workflows/src/engine/run.ts +1 -0
- package/dist/builtin/workflows/src/extension/adopt-session-run-state.ts +20 -0
- package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +9 -30
- package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
- package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +28 -10
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +138 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +3365 -1341
- package/dist/builtin/workflows/src/extension/workflow-heartbeat-delivery.ts +197 -0
- package/dist/builtin/workflows/src/extension/workflow-heartbeat-notice.ts +115 -0
- package/dist/builtin/workflows/src/extension/workflow-heartbeat-scheduler.ts +962 -0
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +4 -1
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +29 -5
- package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +32 -1
- package/dist/builtin/workflows/src/runs/background/cancellation-registry.ts +10 -1
- package/dist/builtin/workflows/src/runs/background/job-tracker.ts +10 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +16 -11
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +31 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +41 -10
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-messages.ts +48 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +4 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-structured-output.ts +45 -12
- package/dist/builtin/workflows/src/sdk-surface.ts +9 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +10 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +10 -0
- package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +14 -2
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +12 -0
- package/dist/builtin/workflows/src/shared/prompt-answer.ts +130 -0
- package/dist/builtin/workflows/src/shared/session-scoped-singleton.ts +98 -0
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +9 -1
- package/dist/builtin/workflows/src/shared/store-factory.ts +9 -1
- package/dist/builtin/workflows/src/shared/store-public-types.ts +2 -1
- package/dist/builtin/workflows/src/shared/store-tool-node-methods.ts +7 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +18 -1
- package/dist/builtin/workflows/src/shared/tool-payload-bounds.ts +576 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +1 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +1 -0
- package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
- package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.ts +25 -0
- package/dist/builtin/workflows/src/tui/graph-theme.ts +8 -1
- package/dist/builtin/workflows/src/tui/graph-view-constants.ts +11 -0
- package/dist/builtin/workflows/src/tui/graph-view-input.ts +100 -5
- package/dist/builtin/workflows/src/tui/graph-view-layout.ts +42 -11
- package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +55 -6
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +71 -6
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +43 -0
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +5 -1
- package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +11 -2
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +36 -10
- package/dist/builtin/workflows/src/tui/switcher.ts +11 -4
- package/dist/builtin/workflows/src/tui/tool-detail.ts +348 -0
- package/dist/builtin/workflows/src/tui/workflow-status.ts +5 -2
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-check.d.ts +1 -1
- package/dist/cli/auth-check.js.map +1 -1
- package/dist/cli/credential-print.d.ts +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/cli/list-models.d.ts +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/startup-ui.d.ts +9 -0
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +7 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/core/agent-session-custom-message-commit.js +2 -2
- package/dist/core/agent-session-custom-message-commit.js.map +1 -1
- package/dist/core/agent-session-export.d.ts +6 -1
- package/dist/core/agent-session-export.d.ts.map +1 -1
- package/dist/core/agent-session-export.js +6 -3
- package/dist/core/agent-session-export.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +6 -4
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +5 -2
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +12 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +31 -10
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-runtime-auth.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-skill-block.d.ts +1 -0
- package/dist/core/agent-session-skill-block.d.ts.map +1 -1
- package/dist/core/agent-session-skill-block.js +18 -2
- package/dist/core/agent-session-skill-block.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +2 -1
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +2 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/builtin-packages.d.ts.map +1 -1
- package/dist/core/builtin-packages.js +0 -6
- package/dist/core/builtin-packages.js.map +1 -1
- package/dist/core/cloudflare-gateway-binding.d.ts +11 -0
- package/dist/core/cloudflare-gateway-binding.d.ts.map +1 -0
- package/dist/core/cloudflare-gateway-binding.js +11 -0
- package/dist/core/cloudflare-gateway-binding.js.map +1 -0
- package/dist/core/codex-fast-mode-transport.d.ts +12 -0
- package/dist/core/codex-fast-mode-transport.d.ts.map +1 -0
- package/dist/core/codex-fast-mode-transport.js +130 -0
- package/dist/core/codex-fast-mode-transport.js.map +1 -0
- package/dist/core/codex-fast-mode.d.ts +20 -6
- package/dist/core/codex-fast-mode.d.ts.map +1 -1
- package/dist/core/codex-fast-mode.js +111 -3
- package/dist/core/codex-fast-mode.js.map +1 -1
- package/dist/core/diagnostics.d.ts +4 -0
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/diagnostics.js.map +1 -1
- package/dist/core/event-bus.d.ts +12 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +22 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/experimental.d.ts +25 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +24 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/export-html/template-js/tree-filter-render.js +4 -3
- package/dist/core/export-html/template.js +4 -3
- package/dist/core/extension-session-state.d.ts +28 -0
- package/dist/core/extension-session-state.d.ts.map +1 -0
- package/dist/core/extension-session-state.js +55 -0
- package/dist/core/extension-session-state.js.map +1 -0
- package/dist/core/extensions/api-types.d.ts +6 -0
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +4 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -0
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-api.d.ts +1 -1
- package/dist/core/extensions/loader-api.d.ts.map +1 -1
- package/dist/core/extensions/loader-api.js +16 -10
- package/dist/core/extensions/loader-api.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +12 -1
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +2 -0
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +8 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +3 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/ui-types.d.ts +26 -0
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js +17 -1
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -0
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts +24 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +6 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver-cli.d.ts +1 -1
- package/dist/core/model-resolver-cli.js.map +1 -1
- package/dist/core/model-resolver-defaults.js +3 -3
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-resolver-initial.d.ts +1 -1
- package/dist/core/model-resolver-initial.js.map +1 -1
- package/dist/core/model-resolver-scope.d.ts +1 -1
- package/dist/core/model-resolver-scope.js.map +1 -1
- package/dist/core/model-runtime.d.ts +54 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +96 -13
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/oauth-login.js.map +1 -1
- package/dist/core/project-trust.d.ts +10 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +12 -0
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/resource-loader-assets.d.ts.map +1 -1
- package/dist/core/resource-loader-assets.js +14 -5
- package/dist/core/resource-loader-assets.js.map +1 -1
- package/dist/core/resource-loader-core.d.ts +3 -0
- package/dist/core/resource-loader-core.d.ts.map +1 -1
- package/dist/core/resource-loader-core.js +5 -0
- package/dist/core/resource-loader-core.js.map +1 -1
- package/dist/core/resource-loader-internals.d.ts +2 -0
- package/dist/core/resource-loader-internals.d.ts.map +1 -1
- package/dist/core/resource-loader-internals.js.map +1 -1
- package/dist/core/resource-loader-reload.d.ts.map +1 -1
- package/dist/core/resource-loader-reload.js +2 -0
- package/dist/core/resource-loader-reload.js.map +1 -1
- package/dist/core/resource-loader-types.d.ts +2 -0
- package/dist/core/resource-loader-types.d.ts.map +1 -1
- package/dist/core/resource-loader-types.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +1 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk-types.d.ts +7 -6
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +17 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +7 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +11 -2
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +2 -2
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +6 -4
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js +5 -1
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +25 -2
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +8 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager-ui-accessors.d.ts +3 -1
- package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-ui-accessors.js +9 -0
- package/dist/core/settings-manager-ui-accessors.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +4 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/skill-catalog.d.ts +35 -0
- package/dist/core/skill-catalog.d.ts.map +1 -0
- package/dist/core/skill-catalog.js +264 -0
- package/dist/core/skill-catalog.js.map +1 -0
- package/dist/core/skills-async.d.ts.map +1 -1
- package/dist/core/skills-async.js +12 -6
- package/dist/core/skills-async.js.map +1 -1
- package/dist/core/skills.d.ts +2 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -5
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -5
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.js +2 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts +25 -2
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js +47 -18
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.js +5 -2
- package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.js +3 -1
- package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +8 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -0
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +3 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/hashline-engine/format.d.ts +2 -0
- package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
- package/dist/core/tools/hashline-engine/format.js +4 -2
- package/dist/core/tools/hashline-engine/format.js.map +1 -1
- package/dist/core/tools/hashline-engine/messages.d.ts +2 -0
- package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
- package/dist/core/tools/hashline-engine/messages.js +4 -2
- package/dist/core/tools/hashline-engine/messages.js.map +1 -1
- package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
- package/dist/core/tools/hashline-engine/parser.js +16 -4
- package/dist/core/tools/hashline-engine/parser.js.map +1 -1
- package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
- package/dist/core/tools/hashline-engine/tokenizer.js +37 -27
- package/dist/core/tools/hashline-engine/tokenizer.js.map +1 -1
- package/dist/core/tools/hashline.d.ts.map +1 -1
- package/dist/core/tools/hashline.js +8 -2
- package/dist/core/tools/hashline.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +2 -0
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +8 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +2 -0
- package/dist/core/tools/search.js.map +1 -1
- package/dist/core/tools/todos.d.ts +3 -12
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +19 -4
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/main-runtime-api-key.d.ts +1 -1
- package/dist/main-runtime-api-key.js.map +1 -1
- package/dist/main-session-options.d.ts +1 -1
- package/dist/main-session-options.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -2
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.js +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.d.ts +8 -0
- package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.js +8 -0
- package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +26 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +38 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts +37 -0
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js +54 -1
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/fast-mode-selector.js +1 -1
- package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +2 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +1 -1
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/reserved-bottom-overlay.d.ts +95 -0
- package/dist/modes/interactive/components/reserved-bottom-overlay.d.ts.map +1 -0
- package/dist/modes/interactive/components/reserved-bottom-overlay.js +234 -0
- package/dist/modes/interactive/components/reserved-bottom-overlay.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +4 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +4 -0
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.js +3 -0
- package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.js +7 -0
- package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +5 -4
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-bash-compact.js +2 -2
- package/dist/modes/interactive/interactive-bash-compact.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +94 -6
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +41 -3
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +80 -11
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +9 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-model-catalog-startup.d.ts +8 -1
- package/dist/modes/interactive/interactive-model-catalog-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-catalog-startup.js +13 -3
- package/dist/modes/interactive/interactive-model-catalog-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +3 -4
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +6 -0
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-resource-disclosure.js +2 -2
- package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +7 -3
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +7 -1
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +2 -2
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +34 -9
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-tui.d.ts +21 -1
- package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-tui.js +140 -38
- package/dist/modes/interactive/interactive-tui.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +17 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +84 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-class.d.ts +12 -3
- package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +6 -1
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +23 -10
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-loading.js +1 -0
- package/dist/modes/interactive/theme/theme-loading.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.d.ts +8 -0
- package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.js +3 -1
- package/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts +2 -0
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
- package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
- package/dist/modes/interactive-engine/protocol.d.ts +1 -0
- package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
- package/dist/modes/interactive-engine/protocol.js +1 -0
- package/dist/modes/interactive-engine/protocol.js.map +1 -1
- package/dist/modes/interactive-engine/remote-component.d.ts +3 -1
- package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-component.js +3 -2
- package/dist/modes/interactive-engine/remote-component.js.map +1 -1
- package/dist/modes/json-event.d.ts +6 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +17 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +5 -4
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
- package/dist/modes/rpc/rpc-responses.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +11 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/utils/abort.d.ts +9 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +19 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +16 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +19 -19
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/custom-provider.md +9 -8
- package/docs/development.md +0 -1
- package/docs/docs.json +0 -1
- package/docs/environment-variables.md +3 -0
- package/docs/extensions.md +100 -2
- package/docs/json.md +3 -1
- package/docs/keybindings.md +8 -3
- package/docs/models/artificial-analysis-index.md +2 -2
- package/docs/models/model-selection.md +32 -23
- package/docs/models/pareto-efficiency.md +25 -24
- package/docs/providers.md +95 -2
- package/docs/quickstart.md +4 -2
- package/docs/rpc.md +21 -7
- package/docs/sdk.md +26 -1
- package/docs/session-format.md +5 -2
- package/docs/settings.md +34 -3
- package/docs/skills.md +16 -1
- package/docs/terminal-setup.md +15 -2
- package/docs/themes.md +22 -4
- package/docs/tools.md +3 -1
- package/docs/tui.md +2 -0
- package/docs/usage.md +5 -4
- package/docs/workflows.md +96 -19
- package/npm-shrinkwrap.json +211 -232
- package/package.json +8 -8
- package/dist/builtin/i-have-adhd/CHANGELOG.md +0 -30
- package/dist/builtin/i-have-adhd/LICENSE +0 -21
- package/dist/builtin/i-have-adhd/README.md +0 -18
- package/dist/builtin/i-have-adhd/index.ts +0 -238
- package/dist/builtin/i-have-adhd/package.json +0 -56
- package/dist/builtin/i-have-adhd/skills/i-have-adhd/SKILL.md +0 -140
- package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +0 -359
- package/dist/builtin/workflows/skills/impeccable/scripts/live/ui-core.mjs +0 -180
- package/docs/i-have-adhd.md +0 -36
|
@@ -11,14 +11,21 @@ import {
|
|
|
11
11
|
isBrandFontOnOwnDomain,
|
|
12
12
|
} from '../shared/constants.mjs';
|
|
13
13
|
import {
|
|
14
|
+
CSS_NAMED_COLORS,
|
|
14
15
|
colorToHex,
|
|
16
|
+
compositeColorOver,
|
|
15
17
|
contrastRatio,
|
|
16
18
|
getHue,
|
|
17
19
|
hasChroma,
|
|
18
20
|
isNeutralColor,
|
|
21
|
+
isNoPaintColorValue,
|
|
22
|
+
oklchToRgb,
|
|
23
|
+
parseAnyColor,
|
|
24
|
+
parseColorMix,
|
|
19
25
|
parseGradientColors,
|
|
20
26
|
parseRgb,
|
|
21
27
|
relativeLuminance,
|
|
28
|
+
splitTopLevelCommas,
|
|
22
29
|
} from '../shared/color.mjs';
|
|
23
30
|
import { extractGoogleFontFamilies } from '../shared/fonts.mjs';
|
|
24
31
|
import { stripHtmlBlocks } from '../shared/page.mjs';
|
|
@@ -71,6 +78,34 @@ function checkBorders(tag, widths, colors, radius, opts = {}) {
|
|
|
71
78
|
return findings;
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
// ─── Scoped ignores: data-impeccable-ignore ─────────────────────────────────
|
|
82
|
+
//
|
|
83
|
+
// An element-scoped waiver that travels with the markup: any element carrying
|
|
84
|
+
// `data-impeccable-ignore="rule-a rule-b"` (or `*`, or an empty value, for
|
|
85
|
+
// every rule) suppresses matching findings from itself and its entire subtree,
|
|
86
|
+
// in every engine that walks elements — the browser overlay, the extension,
|
|
87
|
+
// and the static scan. This is the DOM twin of the line-based
|
|
88
|
+
// `impeccable-disable` comment directives, which the browser cannot apply (a
|
|
89
|
+
// live DOM has no line numbers), and the generalization of the one-off
|
|
90
|
+
// `data-impeccable-allow-kickers` opt-out.
|
|
91
|
+
//
|
|
92
|
+
// The intended use is curated exhibits: a page that documents anti-patterns by
|
|
93
|
+
// example, or renders a deliberate "before" specimen, marks the container once
|
|
94
|
+
// and every engine skips it while still scanning the page around it.
|
|
95
|
+
function scopedIgnoreActive(el, ruleId) {
|
|
96
|
+
const rule = String(ruleId || '').toLowerCase();
|
|
97
|
+
let cur = el;
|
|
98
|
+
while (cur && cur.nodeType === 1) {
|
|
99
|
+
const attr = typeof cur.getAttribute === 'function' ? cur.getAttribute('data-impeccable-ignore') : null;
|
|
100
|
+
if (attr != null) {
|
|
101
|
+
const rules = String(attr).trim().toLowerCase().split(/[\s,]+/).filter(Boolean);
|
|
102
|
+
if (rules.length === 0 || rules.includes('*') || rules.includes(rule)) return true;
|
|
103
|
+
}
|
|
104
|
+
cur = cur.parentElement;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
74
109
|
// Returns true if the given text is composed entirely of emoji characters
|
|
75
110
|
// (plus whitespace / variation selectors). Emojis render as multicolor glyphs
|
|
76
111
|
// regardless of CSS `color`, so contrast checks against the element's text
|
|
@@ -638,6 +673,26 @@ function cssTextHasDarkRootBg(content, customProps) {
|
|
|
638
673
|
return false;
|
|
639
674
|
}
|
|
640
675
|
|
|
676
|
+
// Best-effort extraction of the CSS selector whose declaration block contains
|
|
677
|
+
// the given index in raw CSS text. Lets CSS-text findings carry a live-DOM
|
|
678
|
+
// anchor, so the browser pass can resolve scoped ignores against the actual
|
|
679
|
+
// element and drop patterns that render nowhere on the page. Returns null for
|
|
680
|
+
// @-rule preludes, keyframe steps, nested blocks, and anything that does not
|
|
681
|
+
// read as a selector; those findings stay page-level.
|
|
682
|
+
function enclosingCssSelector(cssText, index) {
|
|
683
|
+
if (!cssText || !Number.isFinite(index)) return null;
|
|
684
|
+
const open = cssText.lastIndexOf('{', index);
|
|
685
|
+
if (open === -1) return null;
|
|
686
|
+
const prevClose = Math.max(cssText.lastIndexOf('}', open - 1), cssText.lastIndexOf(';', open - 1));
|
|
687
|
+
const raw = cssText.slice(prevClose + 1, open).trim().replace(/\s+/g, ' ');
|
|
688
|
+
if (!raw || raw.startsWith('@') || /^\d/.test(raw) || /[{}<]/.test(raw)) return null;
|
|
689
|
+
// Keyframe steps: percentage steps fail the digit test above, but `from`
|
|
690
|
+
// and `to` would read as (never-matching) type selectors and get a valid
|
|
691
|
+
// finding wrongly dropped by the zero-match rule downstream.
|
|
692
|
+
if (/^(?:from|to)(?:\s*,\s*(?:from|to))*$/i.test(raw)) return null;
|
|
693
|
+
return raw;
|
|
694
|
+
}
|
|
695
|
+
|
|
641
696
|
function scanCssTextForGlow(content) {
|
|
642
697
|
const customProps = collectCssCustomProps(content);
|
|
643
698
|
const hasDarkBg = cssTextHasDarkRootBg(content, customProps);
|
|
@@ -949,6 +1004,7 @@ function scanCssTextForPseudoStripe(rawContent) {
|
|
|
949
1004
|
id: 'side-tab',
|
|
950
1005
|
snippet: `${selector} — absolute ${thicknessPx}px pseudo-element stripe (${edge}: 0)`,
|
|
951
1006
|
index: selectorStart,
|
|
1007
|
+
selector,
|
|
952
1008
|
});
|
|
953
1009
|
}
|
|
954
1010
|
return findings;
|
|
@@ -1011,6 +1067,7 @@ function scanCssTextForInsetStripe(content) {
|
|
|
1011
1067
|
findings.push({
|
|
1012
1068
|
id: 'side-tab',
|
|
1013
1069
|
snippet: `${selector} — inset box-shadow ${ay === 0 ? ax : ay}px stripe (${edge})`,
|
|
1070
|
+
selector,
|
|
1014
1071
|
});
|
|
1015
1072
|
break;
|
|
1016
1073
|
}
|
|
@@ -1063,10 +1120,12 @@ function collectMarqueeKeyframes(content) {
|
|
|
1063
1120
|
// bound to a keyframe loop that travels a large horizontal distance.
|
|
1064
1121
|
// Rotation/opacity animations never qualify (no X travel); JS-driven
|
|
1065
1122
|
// carousels with user controls have no infinite CSS X-loop to match.
|
|
1066
|
-
|
|
1123
|
+
// `content` is CSS-bearing text; `markup` (defaulting to the same string
|
|
1124
|
+
// for single-corpus callers) is where the <marquee> tag itself lives.
|
|
1125
|
+
function scanCssTextForMarquee(content, markup = content) {
|
|
1067
1126
|
const findings = [];
|
|
1068
|
-
if (/<marquee\b/i.test(
|
|
1069
|
-
findings.push({ id: 'marquee', snippet: '<marquee> element' });
|
|
1127
|
+
if (/<marquee\b/i.test(markup)) {
|
|
1128
|
+
findings.push({ id: 'marquee', snippet: '<marquee> element', selector: 'marquee' });
|
|
1070
1129
|
}
|
|
1071
1130
|
const marqueeKeyframes = collectMarqueeKeyframes(content);
|
|
1072
1131
|
if (marqueeKeyframes.size === 0) return findings;
|
|
@@ -1081,7 +1140,7 @@ function scanCssTextForMarquee(content) {
|
|
|
1081
1140
|
const key = `${selector} ${name}`;
|
|
1082
1141
|
if (seen.has(key)) continue;
|
|
1083
1142
|
seen.add(key);
|
|
1084
|
-
findings.push({ id: 'marquee', snippet: `${selector} — infinite horizontal loop animation "${name}"
|
|
1143
|
+
findings.push({ id: 'marquee', snippet: `${selector} — infinite horizontal loop animation "${name}"`, selector });
|
|
1085
1144
|
}
|
|
1086
1145
|
}
|
|
1087
1146
|
return findings;
|
|
@@ -1244,10 +1303,14 @@ function selectorHitsLandmark(content, selector, ranges) {
|
|
|
1244
1303
|
// element sits inside a header/nav landmark is the hero liveness cliché
|
|
1245
1304
|
// and is promoted to error severity; occurrences elsewhere keep the
|
|
1246
1305
|
// registry default severity.
|
|
1247
|
-
|
|
1306
|
+
//
|
|
1307
|
+
// `content` is CSS-bearing text (rules and keyframes); `markup` — defaulting
|
|
1308
|
+
// to the same string for single-corpus callers like the regex source
|
|
1309
|
+
// engine — is where landmark ranges and Tailwind class attributes live.
|
|
1310
|
+
function scanCssTextForPulsingDot(content, markup = content) {
|
|
1248
1311
|
const customProps = collectCssCustomProps(content);
|
|
1249
1312
|
const keyframes = collectPulseKeyframes(content);
|
|
1250
|
-
const heroRanges = landmarkSourceRanges(
|
|
1313
|
+
const heroRanges = landmarkSourceRanges(markup);
|
|
1251
1314
|
const findings = [];
|
|
1252
1315
|
const seen = new Set();
|
|
1253
1316
|
|
|
@@ -1296,7 +1359,7 @@ function scanCssTextForPulsingDot(content) {
|
|
|
1296
1359
|
|
|
1297
1360
|
if (seen.has(selector)) continue;
|
|
1298
1361
|
seen.add(selector);
|
|
1299
|
-
const inLandmark = selectorHitsLandmark(
|
|
1362
|
+
const inLandmark = selectorHitsLandmark(markup, selector, heroRanges);
|
|
1300
1363
|
findings.push({
|
|
1301
1364
|
id: 'pulsing-dot',
|
|
1302
1365
|
snippet: `${selector} — ${w}x${h}px dot with infinite "${pulseName}" animation${inLandmark ? ' in header/nav' : ''}`,
|
|
@@ -1306,10 +1369,11 @@ function scanCssTextForPulsingDot(content) {
|
|
|
1306
1369
|
}
|
|
1307
1370
|
|
|
1308
1371
|
// Tailwind utilities: animate-ping / animate-pulse on a tiny rounded-full
|
|
1309
|
-
// element declared entirely in the class attribute.
|
|
1372
|
+
// element declared entirely in the class attribute. Scanned in the markup
|
|
1373
|
+
// corpus so the match index lines up with the landmark ranges.
|
|
1310
1374
|
const classRe = /class\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
|
|
1311
1375
|
let cm;
|
|
1312
|
-
while ((cm = classRe.exec(
|
|
1376
|
+
while ((cm = classRe.exec(markup)) !== null) {
|
|
1313
1377
|
const cls = cm[1] || cm[2] || '';
|
|
1314
1378
|
const anim = cls.match(/\banimate-(ping|pulse)\b/);
|
|
1315
1379
|
if (!anim) continue;
|
|
@@ -1390,36 +1454,82 @@ function scanHtmlForShapeAssembledIllustration(html) {
|
|
|
1390
1454
|
return findings;
|
|
1391
1455
|
}
|
|
1392
1456
|
|
|
1457
|
+
// Scoped scan corpora for the page-level pattern checks. CSS-property
|
|
1458
|
+
// regexes run over the whole source string fire on documentation ABOUT
|
|
1459
|
+
// css — `<code>background-clip: text</code>` prose, <pre> samples, HTML
|
|
1460
|
+
// comments — so the checks scan only the strings that actually style the
|
|
1461
|
+
// page:
|
|
1462
|
+
// styleText — <style> block contents plus style="…" attribute values.
|
|
1463
|
+
// Attribute values keep their `style="…"` form so block-scoped
|
|
1464
|
+
// scanners (grid background) keep treating each attribute as one
|
|
1465
|
+
// declaration block, exactly as they did against raw source. Engines
|
|
1466
|
+
// that already read more CSS (linked stylesheets) prepend it.
|
|
1467
|
+
// classText — class attribute values, for utility-class scans.
|
|
1468
|
+
// Markup-shaped checks (inline <svg> scenes, <img> tags, <marquee>,
|
|
1469
|
+
// landmark ranges) and rendered-text checks (theater phrases) keep the
|
|
1470
|
+
// full source. This extraction serves callers without a parsed document
|
|
1471
|
+
// (the browser bundle scanning outerHTML); attribute reads are tag-scoped
|
|
1472
|
+
// so escaped code samples (<div style="…">) never contribute. The
|
|
1473
|
+
// static engine passes richer corpora built from its parsed document.
|
|
1474
|
+
// Bare CSS input (no markup at all) is its own style text, which keeps
|
|
1475
|
+
// direct checkHtmlPatterns(css) callers behaving as before.
|
|
1476
|
+
function buildHtmlPatternCorpora(html) {
|
|
1477
|
+
const source = String(html || '');
|
|
1478
|
+
if (!/<[a-zA-Z!/]/.test(source)) {
|
|
1479
|
+
return { styleText: source, classText: source };
|
|
1480
|
+
}
|
|
1481
|
+
const styleParts = [];
|
|
1482
|
+
const classParts = [];
|
|
1483
|
+
const styleBlockRe = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
1484
|
+
let m;
|
|
1485
|
+
while ((m = styleBlockRe.exec(source)) !== null) styleParts.push(m[1]);
|
|
1486
|
+
const tagRe = /<[a-zA-Z][^>]*>/g;
|
|
1487
|
+
while ((m = tagRe.exec(source)) !== null) {
|
|
1488
|
+
const tag = m[0];
|
|
1489
|
+
const sm = tag.match(/\bstyle\s*=\s*("[^"]*"|'[^']*')/i);
|
|
1490
|
+
if (sm) styleParts.push(`style=${sm[1]}`);
|
|
1491
|
+
const cm = tag.match(/\bclass\s*=\s*(?:"([^"]*)"|'([^']*)')/i);
|
|
1492
|
+
if (cm) classParts.push(cm[1] ?? cm[2] ?? '');
|
|
1493
|
+
}
|
|
1494
|
+
return { styleText: styleParts.join('\n'), classText: classParts.join('\n') };
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1393
1497
|
/**
|
|
1394
1498
|
* Regex-on-HTML checks shared between browser and Node page-level detection.
|
|
1395
|
-
* These don't need DOM access, just the raw HTML string.
|
|
1499
|
+
* These don't need DOM access, just the raw HTML string. CSS-property and
|
|
1500
|
+
* utility-class patterns scan the scoped corpora (styleText / classText —
|
|
1501
|
+
* see buildHtmlPatternCorpora) so prose about css never flags; only the
|
|
1502
|
+
* markup-shaped and rendered-text checks read the full source.
|
|
1396
1503
|
*/
|
|
1397
|
-
function checkHtmlPatterns(html) {
|
|
1504
|
+
function checkHtmlPatterns(html, corpora) {
|
|
1505
|
+
const { styleText, classText } = corpora || buildHtmlPatternCorpora(html);
|
|
1398
1506
|
const findings = [];
|
|
1399
1507
|
|
|
1400
1508
|
// --- Color ---
|
|
1401
1509
|
|
|
1402
1510
|
// AI color palette: purple/violet
|
|
1403
1511
|
const purpleHexRe = /#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9|6366f1|764ba2|667eea)\b/gi;
|
|
1404
|
-
if (purpleHexRe.test(
|
|
1512
|
+
if (purpleHexRe.test(styleText)) {
|
|
1405
1513
|
const purpleTextRe = /(?:(?:^|;)\s*color\s*:\s*(?:.*?)(?:#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9))|gradient.*?#(?:7c3aed|8b5cf6|a855f7|764ba2|667eea))/gi;
|
|
1406
|
-
|
|
1407
|
-
|
|
1514
|
+
purpleTextRe.lastIndex = 0;
|
|
1515
|
+
const purpleMatch = purpleTextRe.exec(styleText);
|
|
1516
|
+
if (purpleMatch) {
|
|
1517
|
+
findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet accent colors detected', selector: enclosingCssSelector(styleText, purpleMatch.index + 1) || undefined });
|
|
1408
1518
|
}
|
|
1409
1519
|
}
|
|
1410
1520
|
|
|
1411
1521
|
// Gradient text (background-clip: text + gradient)
|
|
1412
1522
|
const gradientRe = /(?:-webkit-)?background-clip\s*:\s*text/gi;
|
|
1413
1523
|
let gm;
|
|
1414
|
-
while ((gm = gradientRe.exec(
|
|
1524
|
+
while ((gm = gradientRe.exec(styleText)) !== null) {
|
|
1415
1525
|
const start = Math.max(0, gm.index - 200);
|
|
1416
|
-
const context =
|
|
1526
|
+
const context = styleText.substring(start, gm.index + gm[0].length + 200);
|
|
1417
1527
|
if (/gradient/i.test(context)) {
|
|
1418
|
-
findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient' });
|
|
1528
|
+
findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient', selector: enclosingCssSelector(styleText, gm.index) || undefined });
|
|
1419
1529
|
break;
|
|
1420
1530
|
}
|
|
1421
1531
|
}
|
|
1422
|
-
if (/\bbg-clip-text\b/.test(
|
|
1532
|
+
if (/\bbg-clip-text\b/.test(classText) && /\bbg-gradient-to-/.test(classText)) {
|
|
1423
1533
|
findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
|
|
1424
1534
|
}
|
|
1425
1535
|
|
|
@@ -1428,10 +1538,10 @@ function checkHtmlPatterns(html) {
|
|
|
1428
1538
|
// Side-tab accent stripe drawn as an absolutely-positioned pseudo-element
|
|
1429
1539
|
// (no border property involved, so the element-level border checks and
|
|
1430
1540
|
// the border-left regexes never see it).
|
|
1431
|
-
findings.push(...scanCssTextForPseudoStripe(
|
|
1541
|
+
findings.push(...scanCssTextForPseudoStripe(styleText));
|
|
1432
1542
|
|
|
1433
1543
|
// Side-tab accent stripe drawn as a single-edge inset box-shadow.
|
|
1434
|
-
findings.push(...scanCssTextForInsetStripe(
|
|
1544
|
+
findings.push(...scanCssTextForInsetStripe(styleText));
|
|
1435
1545
|
|
|
1436
1546
|
// --- Layout ---
|
|
1437
1547
|
|
|
@@ -1439,20 +1549,20 @@ function checkHtmlPatterns(html) {
|
|
|
1439
1549
|
const spacingValues = [];
|
|
1440
1550
|
const spacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi;
|
|
1441
1551
|
let sm;
|
|
1442
|
-
while ((sm = spacingRe.exec(
|
|
1552
|
+
while ((sm = spacingRe.exec(styleText)) !== null) {
|
|
1443
1553
|
const v = parseInt(sm[1], 10);
|
|
1444
1554
|
if (v > 0 && v < 200) spacingValues.push(v);
|
|
1445
1555
|
}
|
|
1446
1556
|
const gapRe = /gap\s*:\s*(\d+)px/gi;
|
|
1447
|
-
while ((sm = gapRe.exec(
|
|
1557
|
+
while ((sm = gapRe.exec(styleText)) !== null) {
|
|
1448
1558
|
spacingValues.push(parseInt(sm[1], 10));
|
|
1449
1559
|
}
|
|
1450
1560
|
const twSpaceRe = /\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g;
|
|
1451
|
-
while ((sm = twSpaceRe.exec(
|
|
1561
|
+
while ((sm = twSpaceRe.exec(classText)) !== null) {
|
|
1452
1562
|
spacingValues.push(parseInt(sm[1], 10) * 4);
|
|
1453
1563
|
}
|
|
1454
1564
|
const remSpacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi;
|
|
1455
|
-
while ((sm = remSpacingRe.exec(
|
|
1565
|
+
while ((sm = remSpacingRe.exec(styleText)) !== null) {
|
|
1456
1566
|
const v = Math.round(parseFloat(sm[1]) * 16);
|
|
1457
1567
|
if (v > 0 && v < 200) spacingValues.push(v);
|
|
1458
1568
|
}
|
|
@@ -1476,21 +1586,21 @@ function checkHtmlPatterns(html) {
|
|
|
1476
1586
|
|
|
1477
1587
|
// Bounce/elastic animation names
|
|
1478
1588
|
const bounceRe = /animation(?:-name)?\s*:\s*([^;{}]*(?:bounce|elastic|wobble|jiggle|spring)[^;{}]*)/gi;
|
|
1479
|
-
const bounceMatch = bounceRe.exec(
|
|
1589
|
+
const bounceMatch = bounceRe.exec(styleText);
|
|
1480
1590
|
if (bounceMatch) {
|
|
1481
1591
|
const animationToken = bounceMatch[1]
|
|
1482
1592
|
.split(/[,\s]+/)
|
|
1483
1593
|
.find((part) => /bounce|elastic|wobble|jiggle|spring/i.test(part));
|
|
1484
|
-
findings.push({ id: 'bounce-easing', snippet: `animation: ${animationToken || bounceMatch[1].trim()}
|
|
1594
|
+
findings.push({ id: 'bounce-easing', snippet: `animation: ${animationToken || bounceMatch[1].trim()}`, selector: enclosingCssSelector(styleText, bounceMatch.index) || undefined });
|
|
1485
1595
|
}
|
|
1486
1596
|
|
|
1487
1597
|
// Overshoot cubic-bezier
|
|
1488
1598
|
const bezierRe = /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g;
|
|
1489
1599
|
let bm;
|
|
1490
|
-
while ((bm = bezierRe.exec(
|
|
1600
|
+
while ((bm = bezierRe.exec(styleText)) !== null) {
|
|
1491
1601
|
const y1 = parseFloat(bm[2]), y2 = parseFloat(bm[4]);
|
|
1492
1602
|
if (y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1) {
|
|
1493
|
-
findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${bm[1]}, ${bm[2]}, ${bm[3]}, ${bm[4]})
|
|
1603
|
+
findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${bm[1]}, ${bm[2]}, ${bm[3]}, ${bm[4]})`, selector: enclosingCssSelector(styleText, bm.index) || undefined });
|
|
1494
1604
|
break;
|
|
1495
1605
|
}
|
|
1496
1606
|
}
|
|
@@ -1498,7 +1608,7 @@ function checkHtmlPatterns(html) {
|
|
|
1498
1608
|
// Layout property transitions
|
|
1499
1609
|
const transRe = /transition(?:-property)?\s*:\s*([^;{}]+)/gi;
|
|
1500
1610
|
let tm;
|
|
1501
|
-
while ((tm = transRe.exec(
|
|
1611
|
+
while ((tm = transRe.exec(styleText)) !== null) {
|
|
1502
1612
|
const val = tm[1].toLowerCase();
|
|
1503
1613
|
if (/\ball\b/.test(val)) continue;
|
|
1504
1614
|
const found = val.match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
|
|
@@ -1508,31 +1618,36 @@ function checkHtmlPatterns(html) {
|
|
|
1508
1618
|
}
|
|
1509
1619
|
}
|
|
1510
1620
|
|
|
1511
|
-
// Pulsing status dots (tiny circular elements on infinite pulse animations)
|
|
1512
|
-
|
|
1621
|
+
// Pulsing status dots (tiny circular elements on infinite pulse animations).
|
|
1622
|
+
// The CSS rules come from styleText; the markup carries the landmark
|
|
1623
|
+
// ranges and Tailwind class attributes.
|
|
1624
|
+
findings.push(...scanCssTextForPulsingDot(styleText, html));
|
|
1513
1625
|
|
|
1514
1626
|
// Shape-assembled illustrations (large pictorial SVGs built from primitives)
|
|
1515
1627
|
findings.push(...scanHtmlForShapeAssembledIllustration(html));
|
|
1516
1628
|
|
|
1517
1629
|
// Auto-scrolling marquees (<marquee> or infinite horizontal loop animations)
|
|
1518
|
-
findings.push(...scanCssTextForMarquee(html));
|
|
1630
|
+
findings.push(...scanCssTextForMarquee(styleText, html));
|
|
1519
1631
|
|
|
1520
1632
|
// --- Dark glow / chromatic halo shadows ---
|
|
1521
1633
|
|
|
1522
|
-
const glowHits = scanCssTextForGlow(
|
|
1634
|
+
const glowHits = scanCssTextForGlow(styleText);
|
|
1523
1635
|
if (glowHits.length > 0) {
|
|
1524
|
-
findings.push({ id: 'dark-glow', snippet: glowHits[0].snippet });
|
|
1636
|
+
findings.push({ id: 'dark-glow', snippet: glowHits[0].snippet, selector: enclosingCssSelector(styleText, glowHits[0].index) || undefined });
|
|
1525
1637
|
}
|
|
1526
1638
|
|
|
1527
1639
|
// Radial-gradient background halo (gradient-drawn sibling of dark-glow)
|
|
1528
|
-
const haloHits = scanCssTextForRadialHalo(
|
|
1640
|
+
const haloHits = scanCssTextForRadialHalo(styleText);
|
|
1529
1641
|
if (haloHits.length > 0) {
|
|
1530
|
-
findings.push({ id: 'radial-halo', snippet: haloHits[0].snippet });
|
|
1642
|
+
findings.push({ id: 'radial-halo', snippet: haloHits[0].snippet, selector: enclosingCssSelector(styleText, haloHits[0].index) || undefined });
|
|
1531
1643
|
}
|
|
1532
1644
|
|
|
1533
1645
|
// --- Generated-UI tells: repeating-gradient stripes ---
|
|
1534
|
-
|
|
1535
|
-
|
|
1646
|
+
{
|
|
1647
|
+
const stripesMatch = /repeating-(?:linear|radial|conic)-gradient\s*\(/i.exec(styleText);
|
|
1648
|
+
if (stripesMatch) {
|
|
1649
|
+
findings.push({ id: 'repeating-stripes-gradient', snippet: 'repeating-gradient decorative stripes', selector: enclosingCssSelector(styleText, stripesMatch.index) || undefined });
|
|
1650
|
+
}
|
|
1536
1651
|
}
|
|
1537
1652
|
|
|
1538
1653
|
// --- Generated-UI tells: two-axis grid-line background ---
|
|
@@ -1548,9 +1663,9 @@ function checkHtmlPatterns(html) {
|
|
|
1548
1663
|
// in for the second axis. Colors like `oklch(96% 0.012 82 / 0.055)` carry
|
|
1549
1664
|
// nested parens, so match the hairline stop directly rather than parsing
|
|
1550
1665
|
// whole gradient layers.
|
|
1551
|
-
const gridHits = scanCssTextForGridBackground(
|
|
1666
|
+
const gridHits = scanCssTextForGridBackground(styleText);
|
|
1552
1667
|
if (gridHits.length > 0) {
|
|
1553
|
-
findings.push({ id: 'codex-grid-background', snippet: gridHits[0].snippet });
|
|
1668
|
+
findings.push({ id: 'codex-grid-background', snippet: gridHits[0].snippet, selector: enclosingCssSelector(styleText, gridHits[0].index) || undefined });
|
|
1554
1669
|
}
|
|
1555
1670
|
|
|
1556
1671
|
// --- Generated-copy tells: "X theater" framing copy ---
|
|
@@ -1567,8 +1682,11 @@ function checkHtmlPatterns(html) {
|
|
|
1567
1682
|
// hover:rotate / hover:translate utility on an <img>. Each distinct
|
|
1568
1683
|
// mechanism is its own finding.
|
|
1569
1684
|
const imgHoverCss = /\bimg\b[^,{}]*:hover\b[^{}]*\{[^}]*\btransform\s*:\s*(?:scale|rotate|translate|matrix|skew)/i;
|
|
1570
|
-
|
|
1571
|
-
|
|
1685
|
+
{
|
|
1686
|
+
const imgHoverMatch = imgHoverCss.exec(styleText);
|
|
1687
|
+
if (imgHoverMatch) {
|
|
1688
|
+
findings.push({ id: 'image-hover-transform', snippet: 'img:hover { transform } rule', selector: enclosingCssSelector(styleText, imgHoverMatch.index + imgHoverMatch[0].indexOf('{') + 1) || undefined });
|
|
1689
|
+
}
|
|
1572
1690
|
}
|
|
1573
1691
|
const imgTagRe = /<img\b[^>]*\bclass\s*=\s*"([^"]*)"/gi;
|
|
1574
1692
|
let im;
|
|
@@ -1615,7 +1733,46 @@ function readOwnBackgroundColor(el, computedStyle) {
|
|
|
1615
1733
|
return bg;
|
|
1616
1734
|
}
|
|
1617
1735
|
|
|
1618
|
-
|
|
1736
|
+
// One element's background-color as the cascade walk sees it: computed style
|
|
1737
|
+
// first (with the modern-color fallback), then, in static mode only,
|
|
1738
|
+
// custom-prop resolution and the inline-shorthand peek. Shared by
|
|
1739
|
+
// resolveBackgroundInfo and resolveGradientStops so both walks read the same
|
|
1740
|
+
// surfaces.
|
|
1741
|
+
function readCascadeBackgroundColor(current, style, customPropMap) {
|
|
1742
|
+
let bg = parseRgb(style.backgroundColor) || parseAnyColor(style.backgroundColor);
|
|
1743
|
+
if (!DETECTOR_IS_BROWSER && (!bg || bg.a < 0.1)) {
|
|
1744
|
+
// The static engine can return literal "var(--X)" / "oklch(...)" strings.
|
|
1745
|
+
// Resolve through customPropMap so Tailwind v4 color tokens become RGB.
|
|
1746
|
+
if (customPropMap) {
|
|
1747
|
+
bg = parseColorResolved(style.backgroundColor, customPropMap);
|
|
1748
|
+
}
|
|
1749
|
+
if (!bg || bg.a < 0.1) {
|
|
1750
|
+
// Inline-style fallback for colors the static cascade did not surface
|
|
1751
|
+
// on backgroundColor.
|
|
1752
|
+
const rawStyle = current.getAttribute?.('style') || '';
|
|
1753
|
+
const bgMatch = rawStyle.match(/background(?:-color)?\s*:\s*([^;]+)/i);
|
|
1754
|
+
const inlineBg = bgMatch ? bgMatch[1].trim() : '';
|
|
1755
|
+
if (inlineBg && !/gradient/i.test(inlineBg) && !/url\s*\(/i.test(inlineBg)) {
|
|
1756
|
+
bg = parseColorResolved(inlineBg, customPropMap) || parseAnyColor(inlineBg);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return bg;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// Walk up for the surface the element's text is painted on.
|
|
1764
|
+
//
|
|
1765
|
+
// Returns { color, unresolved }:
|
|
1766
|
+
// • color set — the effective surface, overlays composited in.
|
|
1767
|
+
// • unresolved: true — a layer on the way up paints a color this parser
|
|
1768
|
+
// cannot read, so the surface is unknown. Callers
|
|
1769
|
+
// must SKIP their contrast checks. Guessing white
|
|
1770
|
+
// here is what flooded dark themes with false
|
|
1771
|
+
// "on #ffffff" findings: one abstention costs a
|
|
1772
|
+
// single finding, one wrong guess costs a hundred.
|
|
1773
|
+
// • both null/false — no solid color, but a gradient or image is in
|
|
1774
|
+
// play; callers fall back to its color stops.
|
|
1775
|
+
function resolveBackgroundInfo(el, win, customPropMap) {
|
|
1619
1776
|
let current = el;
|
|
1620
1777
|
// Translucent layers (0.1 < a < 1) found on the way down to an opaque
|
|
1621
1778
|
// base. A browser composites these over the base; the old behavior
|
|
@@ -1643,67 +1800,114 @@ function resolveBackground(el, win, customPropMap) {
|
|
|
1643
1800
|
// body backgrounds.
|
|
1644
1801
|
// Real browsers serialize wide-gamut computed values as oklab()/oklch()
|
|
1645
1802
|
// (e.g. any color-mix() result), which plain parseRgb misses.
|
|
1646
|
-
let bg =
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
bg = parseColorResolved(inlineBg, customPropMap) || parseAnyColor(inlineBg);
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1803
|
+
let bg = readCascadeBackgroundColor(current, style, customPropMap);
|
|
1804
|
+
|
|
1805
|
+
// `background-color: currentcolor` paints with the element's own text
|
|
1806
|
+
// color — real paint whose value we know. Real browsers resolve the
|
|
1807
|
+
// keyword before getComputedStyle output; jsdom hands it through
|
|
1808
|
+
// verbatim, and without this substitution the layer would read as
|
|
1809
|
+
// unparseable and force a needless abstention.
|
|
1810
|
+
if ((!bg || bg.a < 0.1) && /^currentcolor$/i.test(String(style.backgroundColor || '').trim())) {
|
|
1811
|
+
// The static cascade resolves var() text tokens before checks run, so
|
|
1812
|
+
// style.color is normally already an rgb string here; parseColorResolved
|
|
1813
|
+
// is defense in depth for any future caller that passes a live
|
|
1814
|
+
// customPropMap (it matches the text-color path in checkElementColors
|
|
1815
|
+
// and reduces to parseAnyColor when the map is null or absent).
|
|
1816
|
+
bg = parseRgb(style.color) || parseColorResolved(style.color, customPropMap);
|
|
1663
1817
|
}
|
|
1664
1818
|
|
|
1665
1819
|
if (bg && bg.a > 0.1) {
|
|
1666
|
-
if (bg.a >= 0.99) return flatten(bg);
|
|
1820
|
+
if (bg.a >= 0.99) return { color: flatten(bg), unresolved: false };
|
|
1667
1821
|
overlays.push(bg);
|
|
1822
|
+
} else if (!bg && !isNoPaintColorValue(style.backgroundColor)) {
|
|
1823
|
+
// This layer names a color we could not parse (a color space we do not
|
|
1824
|
+
// model, an unresolved var(), a syntax newer than the parser). It may
|
|
1825
|
+
// well be opaque, which would make every ancestor below it invisible —
|
|
1826
|
+
// so the surface is unknown and the walk stops here rather than
|
|
1827
|
+
// reporting an ancestor the visitor never sees.
|
|
1828
|
+
return { color: null, unresolved: true };
|
|
1668
1829
|
}
|
|
1669
|
-
// No solid bg-color at this level
|
|
1670
|
-
//
|
|
1671
|
-
//
|
|
1672
|
-
//
|
|
1673
|
-
//
|
|
1674
|
-
//
|
|
1675
|
-
//
|
|
1676
|
-
//
|
|
1677
|
-
//
|
|
1678
|
-
//
|
|
1679
|
-
//
|
|
1680
|
-
//
|
|
1830
|
+
// No solid bg-color at this level, but this level paints an image. CSS
|
|
1831
|
+
// stacks background-image layers first-on-top, so which layer leads
|
|
1832
|
+
// decides what the visitor sees:
|
|
1833
|
+
// • gradient on top — the gradient is the surface. Hand the caller a
|
|
1834
|
+
// null color so it falls back to the gradient's own stops (body
|
|
1835
|
+
// grounds, gradient buttons, hero sections).
|
|
1836
|
+
// • url() on top — the surface is an image whose pixels this engine
|
|
1837
|
+
// cannot read, and it may fully cover every layer and ancestor
|
|
1838
|
+
// beneath it. Same contract as an unparseable color: abstain, so
|
|
1839
|
+
// the gradient-stop fallback never measures a gradient the image
|
|
1840
|
+
// hides (the shipped miss: `url(photo), linear-gradient(...)`
|
|
1841
|
+
// reported low-contrast against the invisible gradient's stops).
|
|
1681
1842
|
if (hasGradientOrUrl) {
|
|
1682
|
-
|
|
1683
|
-
|
|
1843
|
+
const layers = splitTopLevelCommas(bgImage);
|
|
1844
|
+
const topPaintLayer = layers.find(
|
|
1845
|
+
(layer) => /gradient\s*\(/i.test(layer) || /url\s*\(/i.test(layer),
|
|
1846
|
+
);
|
|
1847
|
+
const gradientOnTop = !!topPaintLayer
|
|
1848
|
+
&& /gradient\s*\(/i.test(topPaintLayer)
|
|
1849
|
+
&& !/^\s*url\s*\(/i.test(topPaintLayer);
|
|
1850
|
+
if (!gradientOnTop) return { color: null, unresolved: true };
|
|
1851
|
+
// Gradient on top of a url() layer: the image shows through wherever
|
|
1852
|
+
// the gradient is not fully opaque, so a translucent wash like
|
|
1853
|
+
// `linear-gradient(rgba(0,0,0,.2), rgba(0,0,0,.2)), url(photo)` paints
|
|
1854
|
+
// a blend with pixels this engine cannot read. Only a gradient whose
|
|
1855
|
+
// every readable stop is opaque provably covers the image; otherwise
|
|
1856
|
+
// the surface is unknown — abstain rather than hand callers gradient
|
|
1857
|
+
// stops (or a stop average) the visitor never sees unmixed.
|
|
1858
|
+
const urlBeneath = layers.some(
|
|
1859
|
+
(layer) => layer !== topPaintLayer && /url\s*\(/i.test(layer),
|
|
1860
|
+
);
|
|
1861
|
+
if (urlBeneath) {
|
|
1862
|
+
const topStops = parseGradientColors(topPaintLayer);
|
|
1863
|
+
const provablyOpaque = topStops.length > 0 && topStops.every((s) => (s.a ?? 1) >= 0.99);
|
|
1864
|
+
if (!provablyOpaque) return { color: null, unresolved: true };
|
|
1684
1865
|
}
|
|
1685
|
-
return null;
|
|
1866
|
+
return { color: null, unresolved: false };
|
|
1686
1867
|
}
|
|
1687
1868
|
current = current.parentElement;
|
|
1688
1869
|
}
|
|
1689
|
-
|
|
1870
|
+
// Every layer up to the document root was genuinely see-through, so the
|
|
1871
|
+
// browser paints its default canvas. This is the ONLY case that earns the
|
|
1872
|
+
// white assumption.
|
|
1873
|
+
return { color: flatten({ r: 255, g: 255, b: 255, a: 1 }), unresolved: false };
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function resolveBackground(el, win, customPropMap) {
|
|
1877
|
+
return resolveBackgroundInfo(el, win, customPropMap).color;
|
|
1690
1878
|
}
|
|
1691
1879
|
|
|
1692
1880
|
// Walk parents looking for a gradient background and return its color stops.
|
|
1693
1881
|
// Used as a fallback when resolveBackground() returns null because the
|
|
1694
1882
|
// effective background is a gradient (no single solid color to compare against).
|
|
1883
|
+
// Translucent solid layers found between the element and the gradient (frosted
|
|
1884
|
+
// panels, glass washes) are composited over every stop, the same way
|
|
1885
|
+
// resolveBackground flattens them over a solid base — raw stops alone would
|
|
1886
|
+
// false-flag dark text on a light frosted wash over a dark gradient, and miss
|
|
1887
|
+
// the inverse.
|
|
1695
1888
|
function resolveGradientStops(el, win, customPropMap) {
|
|
1696
1889
|
let current = el;
|
|
1890
|
+
const overlays = [];
|
|
1697
1891
|
while (current && current.nodeType === 1) {
|
|
1698
1892
|
const style = DETECTOR_IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
|
|
1699
1893
|
const bgImage = style.backgroundImage || '';
|
|
1894
|
+
// A url() layer anywhere in the stack — alone, or alongside a gradient in
|
|
1895
|
+
// the same declaration (a translucent wash over a texture photo) — paints
|
|
1896
|
+
// pixels the analytic walk cannot know. Measuring the gradient stops over
|
|
1897
|
+
// the wrong base flagged dark ink sitting on a bright gold-leaf image at
|
|
1898
|
+
// 2.6:1; skipping beats a wrong ratio, and the screenshot subsystem owns
|
|
1899
|
+
// image-backed text.
|
|
1900
|
+
if (bgImage && bgImage !== 'none' && /url\s*\(/i.test(bgImage)) return null;
|
|
1700
1901
|
let stops = null;
|
|
1701
1902
|
if (bgImage && bgImage !== 'none' && /gradient/i.test(bgImage)) {
|
|
1903
|
+
// parseGradientColors (shared) reads modern-space stops too — oklch,
|
|
1904
|
+
// color-mix and friends via balanced-paren token capture — so browser
|
|
1905
|
+
// computed values that keep the authored syntax stay measurable.
|
|
1702
1906
|
const parsed = parseGradientColors(bgImage);
|
|
1703
1907
|
if (parsed.length > 0) stops = parsed;
|
|
1704
1908
|
}
|
|
1705
1909
|
if (!stops && !DETECTOR_IS_BROWSER) {
|
|
1706
|
-
//
|
|
1910
|
+
// Static mode: peek at the raw inline style for gradients the cascade did not surface
|
|
1707
1911
|
const rawStyle = current.getAttribute?.('style') || '';
|
|
1708
1912
|
const bgMatch = rawStyle.match(/background(?:-image)?\s*:\s*([^;]+)/i);
|
|
1709
1913
|
if (bgMatch && /gradient/i.test(bgMatch[1])) {
|
|
@@ -1711,7 +1915,23 @@ function resolveGradientStops(el, win, customPropMap) {
|
|
|
1711
1915
|
if (parsed.length > 0) stops = parsed;
|
|
1712
1916
|
}
|
|
1713
1917
|
}
|
|
1714
|
-
if (stops)
|
|
1918
|
+
if (stops) {
|
|
1919
|
+
const composited = compositeGradientStops(stops, current, win, customPropMap);
|
|
1920
|
+
if (!composited || overlays.length === 0) return composited;
|
|
1921
|
+
return composited.map(stop => {
|
|
1922
|
+
let acc = stop;
|
|
1923
|
+
for (let i = overlays.length - 1; i >= 0; i--) acc = compositeColorOver(overlays[i], acc);
|
|
1924
|
+
return acc;
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
const bg = readCascadeBackgroundColor(current, style, customPropMap);
|
|
1928
|
+
if (bg && bg.a > 0.1) {
|
|
1929
|
+
// An opaque surface above the gradient means the gradient never shows
|
|
1930
|
+
// through here; resolveBackground would have returned it, so reaching
|
|
1931
|
+
// this is defensive — bail rather than measure the wrong layer.
|
|
1932
|
+
if (bg.a >= 0.99) return null;
|
|
1933
|
+
overlays.push(bg);
|
|
1934
|
+
}
|
|
1715
1935
|
current = current.parentElement;
|
|
1716
1936
|
}
|
|
1717
1937
|
return null;
|
|
@@ -1931,15 +2151,25 @@ function checkElementColorsDOM(el) {
|
|
|
1931
2151
|
const rect = el.getBoundingClientRect();
|
|
1932
2152
|
if (rect.width < 10 || rect.height < 10) return [];
|
|
1933
2153
|
const style = getComputedStyle(el);
|
|
2154
|
+
// Invisible at rest: hidden scene variants (opacity-0 carousels, swap
|
|
2155
|
+
// decks) are not user-visible, and measuring their inherited colors against
|
|
2156
|
+
// whatever surface happens to sit behind the stack is noise, not audit.
|
|
2157
|
+
if (style.visibility === 'hidden' || effectiveOpacityDOM(el) <= 0.02) return [];
|
|
1934
2158
|
const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
|
|
1935
2159
|
const hasDirectText = directText.trim().length > 0;
|
|
1936
|
-
|
|
2160
|
+
const bgInfo = resolveBackgroundInfo(el);
|
|
2161
|
+
let effectiveBg = bgInfo.color;
|
|
2162
|
+
// An unreadable surface anywhere up the chain: skip the gradient-stop
|
|
2163
|
+
// fallback too, so nothing downstream measures against a ground we never
|
|
2164
|
+
// resolved.
|
|
2165
|
+
let surfaceUnresolved = bgInfo.unresolved;
|
|
1937
2166
|
let ownBg = readOwnBackgroundColor(el, style);
|
|
1938
2167
|
if (!ownBg || (ownBg.a ?? 1) <= 0.5) {
|
|
1939
2168
|
const pseudoSurface = readPseudoSurfaceDOM(el, rect);
|
|
1940
2169
|
if (pseudoSurface) {
|
|
1941
2170
|
ownBg = pseudoSurface;
|
|
1942
2171
|
effectiveBg = pseudoSurface;
|
|
2172
|
+
surfaceUnresolved = false;
|
|
1943
2173
|
}
|
|
1944
2174
|
}
|
|
1945
2175
|
return checkColors({
|
|
@@ -1951,8 +2181,8 @@ function checkElementColorsDOM(el) {
|
|
|
1951
2181
|
// an oklch token near its own oklch background).
|
|
1952
2182
|
textColor: parseRgb(style.color) || parseAnyColor(style.color),
|
|
1953
2183
|
bgColor: ownBg,
|
|
1954
|
-
effectiveBg,
|
|
1955
|
-
effectiveBgStops: effectiveBg ? null : resolveGradientStops(el),
|
|
2184
|
+
effectiveBg: surfaceUnresolved ? null : effectiveBg,
|
|
2185
|
+
effectiveBgStops: surfaceUnresolved || effectiveBg ? null : resolveGradientStops(el),
|
|
1956
2186
|
fontSize: parseFloat(style.fontSize) || 16,
|
|
1957
2187
|
fontWeight: parseInt(style.fontWeight) || 400,
|
|
1958
2188
|
hasDirectText,
|
|
@@ -2102,283 +2332,6 @@ function resolveVarRefs(raw, customPropMap, depth = 0) {
|
|
|
2102
2332
|
});
|
|
2103
2333
|
}
|
|
2104
2334
|
|
|
2105
|
-
// OKLCH → sRGB conversion (Björn Ottosson's matrices). L in 0..1 (or %),
|
|
2106
|
-
// C in 0..~0.4 typical, H in degrees. Returns clamped {r,g,b,a:1} in 0..255.
|
|
2107
|
-
// Needed because jsdom doesn't compute oklch() values — getComputedStyle
|
|
2108
|
-
// returns the literal "oklch(...)" string. Without this, the entire
|
|
2109
|
-
// Tailwind v4 color palette (which is OKLCH-based) is invisible to the
|
|
2110
|
-
// detector's contrast / color checks.
|
|
2111
|
-
function oklchToRgb(L, C, H) {
|
|
2112
|
-
const hRad = (H * Math.PI) / 180;
|
|
2113
|
-
return oklabToRgb(L, C * Math.cos(hRad), C * Math.sin(hRad));
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
function oklabToRgb(L, a, b) {
|
|
2117
|
-
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
|
2118
|
-
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
|
2119
|
-
const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
|
|
2120
|
-
const lc = l_ * l_ * l_, mc = m_ * m_ * m_, sc = s_ * s_ * s_;
|
|
2121
|
-
const rLin = 4.0767416621 * lc - 3.3077115913 * mc + 0.2309699292 * sc;
|
|
2122
|
-
const gLin = -1.2684380046 * lc + 2.6097574011 * mc - 0.3413193965 * sc;
|
|
2123
|
-
const bLin = -0.0041960863 * lc - 0.7034186147 * mc + 1.7076147010 * sc;
|
|
2124
|
-
const enc = (x) => {
|
|
2125
|
-
const c = Math.max(0, Math.min(1, x));
|
|
2126
|
-
return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
2127
|
-
};
|
|
2128
|
-
return {
|
|
2129
|
-
r: Math.round(enc(rLin) * 255),
|
|
2130
|
-
g: Math.round(enc(gLin) * 255),
|
|
2131
|
-
b: Math.round(enc(bLin) * 255),
|
|
2132
|
-
a: 1,
|
|
2133
|
-
};
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
function hslToRgb(h, s, l) {
|
|
2137
|
-
h = ((h % 360) + 360) % 360;
|
|
2138
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
2139
|
-
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
2140
|
-
const m0 = l - c / 2;
|
|
2141
|
-
const [r, g, b] =
|
|
2142
|
-
h < 60 ? [c, x, 0] :
|
|
2143
|
-
h < 120 ? [x, c, 0] :
|
|
2144
|
-
h < 180 ? [0, c, x] :
|
|
2145
|
-
h < 240 ? [0, x, c] :
|
|
2146
|
-
h < 300 ? [x, 0, c] : [c, 0, x];
|
|
2147
|
-
return {
|
|
2148
|
-
r: Math.round((r + m0) * 255),
|
|
2149
|
-
g: Math.round((g + m0) * 255),
|
|
2150
|
-
b: Math.round((b + m0) * 255),
|
|
2151
|
-
a: 1,
|
|
2152
|
-
};
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
function hwbToRgb(h, w, bl) {
|
|
2156
|
-
if (w + bl >= 1) {
|
|
2157
|
-
const g = Math.round((w / (w + bl)) * 255);
|
|
2158
|
-
return { r: g, g, b: g, a: 1 };
|
|
2159
|
-
}
|
|
2160
|
-
const base = hslToRgb(h, 1, 0.5);
|
|
2161
|
-
const mix = (c) => Math.round(((c / 255) * (1 - w - bl) + w) * 255);
|
|
2162
|
-
return { r: mix(base.r), g: mix(base.g), b: mix(base.b), a: 1 };
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
// Common CSS named colors — the handful that actually show up in generated
|
|
2166
|
-
// UIs, not the full 148-name spec list. Includes the achromatic names so a
|
|
2167
|
-
// named gray parses (and correctly reads as no-chroma) instead of being
|
|
2168
|
-
// treated as an unknown color.
|
|
2169
|
-
const CSS_NAMED_COLORS = {
|
|
2170
|
-
black: { r: 0, g: 0, b: 0 },
|
|
2171
|
-
white: { r: 255, g: 255, b: 255 },
|
|
2172
|
-
gray: { r: 128, g: 128, b: 128 },
|
|
2173
|
-
grey: { r: 128, g: 128, b: 128 },
|
|
2174
|
-
silver: { r: 192, g: 192, b: 192 },
|
|
2175
|
-
dimgray: { r: 105, g: 105, b: 105 },
|
|
2176
|
-
darkgray: { r: 169, g: 169, b: 169 },
|
|
2177
|
-
lightgray: { r: 211, g: 211, b: 211 },
|
|
2178
|
-
gainsboro: { r: 220, g: 220, b: 220 },
|
|
2179
|
-
whitesmoke: { r: 245, g: 245, b: 245 },
|
|
2180
|
-
red: { r: 255, g: 0, b: 0 },
|
|
2181
|
-
crimson: { r: 220, g: 20, b: 60 },
|
|
2182
|
-
tomato: { r: 255, g: 99, b: 71 },
|
|
2183
|
-
coral: { r: 255, g: 127, b: 80 },
|
|
2184
|
-
salmon: { r: 250, g: 128, b: 114 },
|
|
2185
|
-
orange: { r: 255, g: 165, b: 0 },
|
|
2186
|
-
gold: { r: 255, g: 215, b: 0 },
|
|
2187
|
-
yellow: { r: 255, g: 255, b: 0 },
|
|
2188
|
-
olive: { r: 128, g: 128, b: 0 },
|
|
2189
|
-
lime: { r: 0, g: 255, b: 0 },
|
|
2190
|
-
green: { r: 0, g: 128, b: 0 },
|
|
2191
|
-
teal: { r: 0, g: 128, b: 128 },
|
|
2192
|
-
turquoise: { r: 64, g: 224, b: 208 },
|
|
2193
|
-
cyan: { r: 0, g: 255, b: 255 },
|
|
2194
|
-
aqua: { r: 0, g: 255, b: 255 },
|
|
2195
|
-
skyblue: { r: 135, g: 206, b: 235 },
|
|
2196
|
-
dodgerblue: { r: 30, g: 144, b: 255 },
|
|
2197
|
-
blue: { r: 0, g: 0, b: 255 },
|
|
2198
|
-
navy: { r: 0, g: 0, b: 128 },
|
|
2199
|
-
indigo: { r: 75, g: 0, b: 130 },
|
|
2200
|
-
rebeccapurple: { r: 102, g: 51, b: 153 },
|
|
2201
|
-
purple: { r: 128, g: 0, b: 128 },
|
|
2202
|
-
violet: { r: 238, g: 130, b: 238 },
|
|
2203
|
-
orchid: { r: 218, g: 112, b: 214 },
|
|
2204
|
-
magenta: { r: 255, g: 0, b: 255 },
|
|
2205
|
-
fuchsia: { r: 255, g: 0, b: 255 },
|
|
2206
|
-
hotpink: { r: 255, g: 105, b: 180 },
|
|
2207
|
-
pink: { r: 255, g: 192, b: 203 },
|
|
2208
|
-
maroon: { r: 128, g: 0, b: 0 },
|
|
2209
|
-
};
|
|
2210
|
-
|
|
2211
|
-
// Split a string on top-level commas (ignoring commas nested in parens).
|
|
2212
|
-
function splitTopLevelCommas(str) {
|
|
2213
|
-
const parts = [];
|
|
2214
|
-
let depth = 0, start = 0;
|
|
2215
|
-
for (let i = 0; i < str.length; i++) {
|
|
2216
|
-
const ch = str[i];
|
|
2217
|
-
if (ch === '(') depth++;
|
|
2218
|
-
else if (ch === ')') depth = Math.max(0, depth - 1);
|
|
2219
|
-
else if (ch === ',' && depth === 0) {
|
|
2220
|
-
parts.push(str.slice(start, i).trim());
|
|
2221
|
-
start = i + 1;
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
const tail = str.slice(start).trim();
|
|
2225
|
-
if (tail) parts.push(tail);
|
|
2226
|
-
return parts;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
// Evaluate a CSS color-mix() expression to {r,g,b,a}. Returns null when
|
|
2230
|
-
// the expression can't be resolved (unresolved var(), unknown colors).
|
|
2231
|
-
//
|
|
2232
|
-
// Mixing is done with premultiplied alpha in sRGB regardless of the
|
|
2233
|
-
// declared interpolation space. That is exact for the dominant generated-UI
|
|
2234
|
-
// pattern — `color-mix(in oklab, <color> N%, transparent)` — where the
|
|
2235
|
-
// result is simply <color> at alpha N% in ANY rectangular space, and a
|
|
2236
|
-
// close-enough approximation for opaque-opaque mixes (the detector only
|
|
2237
|
-
// consumes these values for contrast/chroma thresholds, not for display).
|
|
2238
|
-
function parseColorMix(str) {
|
|
2239
|
-
const m = String(str).trim().match(/^color-mix\(/i);
|
|
2240
|
-
if (!m) return null;
|
|
2241
|
-
// Balanced-paren capture of the arguments.
|
|
2242
|
-
let depth = 0, end = -1;
|
|
2243
|
-
const open = str.indexOf('(');
|
|
2244
|
-
for (let i = open; i < str.length; i++) {
|
|
2245
|
-
if (str[i] === '(') depth++;
|
|
2246
|
-
else if (str[i] === ')') { depth--; if (depth === 0) { end = i; break; } }
|
|
2247
|
-
}
|
|
2248
|
-
if (end < 0) return null;
|
|
2249
|
-
const args = splitTopLevelCommas(str.slice(open + 1, end));
|
|
2250
|
-
if (args.length !== 3 || !/^in\s/i.test(args[0])) return null;
|
|
2251
|
-
|
|
2252
|
-
const parseComponent = (component) => {
|
|
2253
|
-
// Percentage may lead or trail the color per spec.
|
|
2254
|
-
let pct = null;
|
|
2255
|
-
let colorStr = component;
|
|
2256
|
-
const trail = component.match(/\s+([\d.]+)%$/);
|
|
2257
|
-
const lead = component.match(/^([\d.]+)%\s+/);
|
|
2258
|
-
if (trail) { pct = parseFloat(trail[1]); colorStr = component.slice(0, trail.index).trim(); }
|
|
2259
|
-
else if (lead) { pct = parseFloat(lead[1]); colorStr = component.slice(lead[0].length).trim(); }
|
|
2260
|
-
let color;
|
|
2261
|
-
if (/^transparent$/i.test(colorStr)) color = { r: 0, g: 0, b: 0, a: 0 };
|
|
2262
|
-
else color = parseAnyColor(colorStr);
|
|
2263
|
-
if (!color) return null;
|
|
2264
|
-
return { color, pct };
|
|
2265
|
-
};
|
|
2266
|
-
|
|
2267
|
-
const c1 = parseComponent(args[1]);
|
|
2268
|
-
const c2 = parseComponent(args[2]);
|
|
2269
|
-
if (!c1 || !c2) return null;
|
|
2270
|
-
let p1 = c1.pct, p2 = c2.pct;
|
|
2271
|
-
if (p1 == null && p2 == null) { p1 = 50; p2 = 50; }
|
|
2272
|
-
else if (p1 == null) p1 = 100 - p2;
|
|
2273
|
-
else if (p2 == null) p2 = 100 - p1;
|
|
2274
|
-
const sum = p1 + p2;
|
|
2275
|
-
if (sum <= 0) return null;
|
|
2276
|
-
// Per spec: weights normalize to sum; when sum < 100 the result alpha is
|
|
2277
|
-
// additionally scaled by sum/100.
|
|
2278
|
-
const w1 = p1 / sum, w2 = p2 / sum;
|
|
2279
|
-
const alphaScale = sum < 100 ? sum / 100 : 1;
|
|
2280
|
-
const a1 = c1.color.a ?? 1, a2 = c2.color.a ?? 1;
|
|
2281
|
-
const a = (a1 * w1 + a2 * w2) * alphaScale;
|
|
2282
|
-
if (a <= 0) return { r: 0, g: 0, b: 0, a: 0 };
|
|
2283
|
-
const mix = (ch) => Math.round((c1.color[ch] * a1 * w1 + c2.color[ch] * a2 * w2) / (a1 * w1 + a2 * w2));
|
|
2284
|
-
return { r: mix('r'), g: mix('g'), b: mix('b'), a: Math.min(1, a) };
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
|
-
// Composite a translucent color over an opaque(ish) base (simple
|
|
2288
|
-
// source-over in sRGB). Returns an opaque {r,g,b,a:1}.
|
|
2289
|
-
function compositeColorOver(top, base) {
|
|
2290
|
-
const a = top.a ?? 1;
|
|
2291
|
-
return {
|
|
2292
|
-
r: Math.round(top.r * a + base.r * (1 - a)),
|
|
2293
|
-
g: Math.round(top.g * a + base.g * (1 - a)),
|
|
2294
|
-
b: Math.round(top.b * a + base.b * (1 - a)),
|
|
2295
|
-
a: 1,
|
|
2296
|
-
};
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
// Extended color parser: rgb/rgba/hex/oklch/oklab/hsl/hwb/color-mix/common
|
|
2300
|
-
// named colors. Returns null on no match. Use this when the input might be
|
|
2301
|
-
// any CSS color form; use plain parseRgb when you only expect computed rgb()
|
|
2302
|
-
// values from real browsers.
|
|
2303
|
-
function parseAnyColor(s) {
|
|
2304
|
-
if (!s || typeof s !== 'string') return null;
|
|
2305
|
-
const str = s.trim();
|
|
2306
|
-
if (str === 'transparent' || str === 'currentcolor' || str === 'inherit') return null;
|
|
2307
|
-
if (/^color-mix\(/i.test(str)) return parseColorMix(str);
|
|
2308
|
-
let m;
|
|
2309
|
-
m = str.match(/rgba?\(\s*(\d+(?:\.\d+)?)\s*,?\s*(\d+(?:\.\d+)?)\s*,?\s*(\d+(?:\.\d+)?)(?:\s*[,/]\s*([\d.]+))?\s*\)/);
|
|
2310
|
-
if (m) return { r: Math.round(+m[1]), g: Math.round(+m[2]), b: Math.round(+m[3]), a: m[4] !== undefined ? +m[4] : 1 };
|
|
2311
|
-
m = str.match(/^#([0-9a-f]{3,8})$/i);
|
|
2312
|
-
if (m) {
|
|
2313
|
-
const h = m[1];
|
|
2314
|
-
if (h.length === 3 || h.length === 4) {
|
|
2315
|
-
return {
|
|
2316
|
-
r: parseInt(h[0] + h[0], 16),
|
|
2317
|
-
g: parseInt(h[1] + h[1], 16),
|
|
2318
|
-
b: parseInt(h[2] + h[2], 16),
|
|
2319
|
-
a: h.length === 4 ? parseInt(h[3] + h[3], 16) / 255 : 1,
|
|
2320
|
-
};
|
|
2321
|
-
}
|
|
2322
|
-
if (h.length === 6 || h.length === 8) {
|
|
2323
|
-
return {
|
|
2324
|
-
r: parseInt(h.slice(0, 2), 16),
|
|
2325
|
-
g: parseInt(h.slice(2, 4), 16),
|
|
2326
|
-
b: parseInt(h.slice(4, 6), 16),
|
|
2327
|
-
a: h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1,
|
|
2328
|
-
};
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
// OKLCH parser. Tailwind v4's CSS minifier squishes the space after
|
|
2332
|
-
// `%` ("21.5%.02 50"), so the separator between L and C may be absent.
|
|
2333
|
-
// Match L (with optional %), then C and H separated permissively.
|
|
2334
|
-
m = str.match(/oklch\(\s*([\d.]+)(%?)\s*[\s,]*\s*([\d.]+)\s*[\s,]+\s*([-\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
|
|
2335
|
-
if (m) {
|
|
2336
|
-
const Lnum = parseFloat(m[1]);
|
|
2337
|
-
const L = m[2] === '%' ? Lnum / 100 : Lnum;
|
|
2338
|
-
const rgb = oklchToRgb(L, parseFloat(m[3]), parseFloat(m[4]));
|
|
2339
|
-
if (m[5] !== undefined) {
|
|
2340
|
-
const alpha = parseFloat(m[5]);
|
|
2341
|
-
rgb.a = m[6] === '%' ? alpha / 100 : alpha;
|
|
2342
|
-
}
|
|
2343
|
-
return rgb;
|
|
2344
|
-
}
|
|
2345
|
-
// OKLAB — a/b are signed axes; percentages map 100% → 0.4.
|
|
2346
|
-
m = str.match(/oklab\(\s*([\d.]+)(%?)\s+(-?[\d.]+)(%?)\s+(-?[\d.]+)(%?)(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
|
|
2347
|
-
if (m) {
|
|
2348
|
-
const L = m[2] === '%' ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
|
|
2349
|
-
const a = m[4] === '%' ? parseFloat(m[3]) * 0.004 : parseFloat(m[3]);
|
|
2350
|
-
const b = m[6] === '%' ? parseFloat(m[5]) * 0.004 : parseFloat(m[5]);
|
|
2351
|
-
const rgb = oklabToRgb(L, a, b);
|
|
2352
|
-
if (m[7] !== undefined) {
|
|
2353
|
-
const alpha = parseFloat(m[7]);
|
|
2354
|
-
rgb.a = m[8] === '%' ? alpha / 100 : alpha;
|
|
2355
|
-
}
|
|
2356
|
-
return rgb;
|
|
2357
|
-
}
|
|
2358
|
-
// HSL/HSLA — comma or space syntax, optional deg on hue.
|
|
2359
|
-
m = str.match(/hsla?\(\s*(-?[\d.]+)(?:deg)?\s*[,\s]\s*([\d.]+)%\s*[,\s]\s*([\d.]+)%(?:\s*[,/]\s*([\d.]+)(%)?)?\s*\)/i);
|
|
2360
|
-
if (m) {
|
|
2361
|
-
const rgb = hslToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
|
|
2362
|
-
if (m[4] !== undefined) {
|
|
2363
|
-
const alpha = parseFloat(m[4]);
|
|
2364
|
-
rgb.a = m[5] === '%' ? alpha / 100 : alpha;
|
|
2365
|
-
}
|
|
2366
|
-
return rgb;
|
|
2367
|
-
}
|
|
2368
|
-
// HWB — hue whiteness% blackness%.
|
|
2369
|
-
m = str.match(/hwb\(\s*(-?[\d.]+)(?:deg)?\s+([\d.]+)%\s+([\d.]+)%(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
|
|
2370
|
-
if (m) {
|
|
2371
|
-
const rgb = hwbToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
|
|
2372
|
-
if (m[4] !== undefined) {
|
|
2373
|
-
const alpha = parseFloat(m[4]);
|
|
2374
|
-
rgb.a = m[5] === '%' ? alpha / 100 : alpha;
|
|
2375
|
-
}
|
|
2376
|
-
return rgb;
|
|
2377
|
-
}
|
|
2378
|
-
const named = CSS_NAMED_COLORS[str.toLowerCase()];
|
|
2379
|
-
if (named) return { ...named, a: 1 };
|
|
2380
|
-
return null;
|
|
2381
|
-
}
|
|
2382
2335
|
|
|
2383
2336
|
// Resolve var() refs in a color string (via customPropMap), then parse.
|
|
2384
2337
|
// Returns null on any failure. Used in jsdom-mode paths where
|
|
@@ -2468,7 +2421,11 @@ function isKickerCandidate(opts) {
|
|
|
2468
2421
|
|| isSmallCaps;
|
|
2469
2422
|
if (!isUppercased) return false;
|
|
2470
2423
|
if (!(kickerFontSize > 0 && kickerFontSize <= 14)) return false;
|
|
2471
|
-
|
|
2424
|
+
// Proportional only, no absolute floor: the wild's most common recipe is
|
|
2425
|
+
// 0.08em at a sub-13px size, which computes to under 1px and sailed past
|
|
2426
|
+
// the old Math.max(1, ...) floor (observed live: a page whose kickers were
|
|
2427
|
+
// literally class="kicker" produced zero findings).
|
|
2428
|
+
const minTrackedSpacing = kickerFontSize * 0.06;
|
|
2472
2429
|
if (!(kickerLetterSpacing >= minTrackedSpacing)) return false;
|
|
2473
2430
|
return true;
|
|
2474
2431
|
}
|
|
@@ -2737,9 +2694,20 @@ function checkElementGlowDOM(el) {
|
|
|
2737
2694
|
if (!boxShadow && !textShadow) return [];
|
|
2738
2695
|
// Use parent's background — glow radiates outward, so the surrounding context matters
|
|
2739
2696
|
// If resolveBackground returns null (gradient), try to infer from the gradient colors
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2697
|
+
const parentBgInfo = resolveBackgroundInfo(el.parentElement || el);
|
|
2698
|
+
// Unknown surface (an unreadable layer on the way up): skip only the
|
|
2699
|
+
// gradient hunt below, which would walk PAST that layer and score the
|
|
2700
|
+
// glow against a background the visitor never sees. checkGlow still runs
|
|
2701
|
+
// with a null surface: the zero-offset chromatic halo tell holds on ANY
|
|
2702
|
+
// background, and the static loop already passes the unresolved walk's
|
|
2703
|
+
// null color straight through (detect-html.mjs uses resolveBackground).
|
|
2704
|
+
let parentBg = parentBgInfo.color;
|
|
2705
|
+
if (!parentBg && !parentBgInfo.unresolved) {
|
|
2706
|
+
// Gradient background — sample its colors to determine if it's dark.
|
|
2707
|
+
// Modern-syntax parsing matters here: body-level gradients now reach this
|
|
2708
|
+
// fallback in browser mode, and their stops usually serialize as oklch —
|
|
2709
|
+
// which the shared parseGradientColors reads via its color-function
|
|
2710
|
+
// token capture.
|
|
2743
2711
|
let cur = el.parentElement;
|
|
2744
2712
|
while (cur && cur.nodeType === 1) {
|
|
2745
2713
|
const bgImage = getComputedStyle(cur).backgroundImage || '';
|
|
@@ -2787,10 +2755,13 @@ function checkElementAIPaletteDOM(el) {
|
|
|
2787
2755
|
const hue = getHue(textColor);
|
|
2788
2756
|
const isAIPalette = (hue >= 160 && hue <= 200) || (hue >= 260 && hue <= 310);
|
|
2789
2757
|
if (isAIPalette) {
|
|
2790
|
-
const
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2758
|
+
const parentBgInfo = el.parentElement
|
|
2759
|
+
? resolveBackgroundInfo(el.parentElement)
|
|
2760
|
+
: { color: null, unresolved: false };
|
|
2761
|
+
// Unknown surface: leave effectiveBg null (no finding) rather than
|
|
2762
|
+
// hunting gradient ancestors past a layer we could not read.
|
|
2763
|
+
let effectiveBg = parentBgInfo.color;
|
|
2764
|
+
if (!effectiveBg && !parentBgInfo.unresolved) {
|
|
2794
2765
|
let cur = el.parentElement;
|
|
2795
2766
|
while (cur && cur.nodeType === 1) {
|
|
2796
2767
|
const gi = getComputedStyle(cur).backgroundImage || '';
|
|
@@ -3585,10 +3556,19 @@ function checkElementBorders(tag, style, overrides, resolvedRadius, el = null) {
|
|
|
3585
3556
|
}
|
|
3586
3557
|
|
|
3587
3558
|
function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInheritRule) {
|
|
3559
|
+
// Invisible at rest, static twin of the browser walk's skip: opacity does
|
|
3560
|
+
// not inherit, so walk ancestors multiplying declared opacity down.
|
|
3561
|
+
if (style.visibility === 'hidden') return [];
|
|
3562
|
+
let effOpacity = 1;
|
|
3563
|
+
for (let cur = el; cur && cur.nodeType === 1 && effOpacity > 0.02; cur = cur.parentElement) {
|
|
3564
|
+
effOpacity *= parseFloat(window.getComputedStyle(cur).opacity || '1');
|
|
3565
|
+
}
|
|
3566
|
+
if (effOpacity <= 0.02) return [];
|
|
3588
3567
|
const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
|
|
3589
3568
|
const hasDirectText = directText.trim().length > 0;
|
|
3590
3569
|
|
|
3591
|
-
const
|
|
3570
|
+
const bgInfo = resolveBackgroundInfo(el, window, customPropMap);
|
|
3571
|
+
const effectiveBg = bgInfo.color;
|
|
3592
3572
|
// jsdom returns literal "var(--X)" / "oklch(...)" for color, so plain
|
|
3593
3573
|
// parseRgb misses Tailwind-tokenized text colors. Resolve through the
|
|
3594
3574
|
// customPropMap first; fall back to parseRgb for vanilla rgb() pages.
|
|
@@ -3634,11 +3614,13 @@ function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInhe
|
|
|
3634
3614
|
// element itself has no usable own background, that pseudo is the real
|
|
3635
3615
|
// surface for contrast purposes.
|
|
3636
3616
|
let finalEffectiveBg = effectiveBg;
|
|
3617
|
+
let surfaceUnresolved = bgInfo.unresolved;
|
|
3637
3618
|
if ((!ownBg || (ownBg.a ?? 1) <= 0.5) && typeof window.getPseudoSurface === 'function') {
|
|
3638
3619
|
const pseudoSurface = window.getPseudoSurface(el);
|
|
3639
3620
|
if (pseudoSurface) {
|
|
3640
3621
|
ownBg = pseudoSurface;
|
|
3641
3622
|
finalEffectiveBg = pseudoSurface;
|
|
3623
|
+
surfaceUnresolved = false;
|
|
3642
3624
|
}
|
|
3643
3625
|
}
|
|
3644
3626
|
|
|
@@ -3646,8 +3628,9 @@ function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInhe
|
|
|
3646
3628
|
tag,
|
|
3647
3629
|
textColor,
|
|
3648
3630
|
bgColor: ownBg,
|
|
3649
|
-
|
|
3650
|
-
|
|
3631
|
+
// Unknown surface: hand the checks nothing rather than a guess.
|
|
3632
|
+
effectiveBg: surfaceUnresolved ? null : finalEffectiveBg,
|
|
3633
|
+
effectiveBgStops: surfaceUnresolved || finalEffectiveBg ? null : resolveGradientStops(el, window, customPropMap),
|
|
3651
3634
|
fontSize: parseFloat(style.fontSize) || 16,
|
|
3652
3635
|
fontWeight: parseInt(style.fontWeight) || 400,
|
|
3653
3636
|
hasDirectText,
|
|
@@ -3852,12 +3835,6 @@ function checkTypography() {
|
|
|
3852
3835
|
if (isBrandFontOnOwnDomain(font)) continue;
|
|
3853
3836
|
findings.push({ type: 'overused-font', detail: `Primary font: ${font} (${Math.round(share * 100)}% of text)` });
|
|
3854
3837
|
}
|
|
3855
|
-
|
|
3856
|
-
// Single-font check: only one distinct primary font across all text
|
|
3857
|
-
if (fontUsage.size === 1) {
|
|
3858
|
-
const only = [...fontUsage.keys()][0];
|
|
3859
|
-
findings.push({ type: 'single-font', detail: `only font used is ${only}` });
|
|
3860
|
-
}
|
|
3861
3838
|
}
|
|
3862
3839
|
|
|
3863
3840
|
const sizes = new Set();
|
|
@@ -4119,14 +4096,6 @@ function checkPageTypography(doc, win) {
|
|
|
4119
4096
|
findings.push({ id: 'overused-font', snippet: `Primary font: ${font}` });
|
|
4120
4097
|
}
|
|
4121
4098
|
|
|
4122
|
-
// Single font
|
|
4123
|
-
if (fonts.size === 1) {
|
|
4124
|
-
const els = doc.querySelectorAll('*');
|
|
4125
|
-
if (els.length >= 20) {
|
|
4126
|
-
findings.push({ id: 'single-font', snippet: `only font used is ${[...fonts][0]}` });
|
|
4127
|
-
}
|
|
4128
|
-
}
|
|
4129
|
-
|
|
4130
4099
|
// Flat type hierarchy
|
|
4131
4100
|
const sizes = new Set();
|
|
4132
4101
|
const textEls = doc.querySelectorAll('h1, h2, h3, h4, h5, h6, p, span, a, li, td, th, label, button, div');
|
|
@@ -4757,6 +4726,11 @@ function isRenderedForBrowserRule(el) {
|
|
|
4757
4726
|
function checkElementTextOverflowDOM(el) {
|
|
4758
4727
|
const tag = el.tagName.toLowerCase();
|
|
4759
4728
|
if (TEXT_OVERFLOW_SKIP_TAGS.has(tag)) return [];
|
|
4729
|
+
// scrollWidth/clientWidth are CSS box-model metrics; on SVG content Chrome
|
|
4730
|
+
// returns arbitrary non-zero values for both (a <text> reported 78/48 while
|
|
4731
|
+
// its rendered length sat comfortably inside its box), so the delta is
|
|
4732
|
+
// noise, not overflow. SVG clips to its own viewport anyway.
|
|
4733
|
+
if (el.namespaceURI === 'http://www.w3.org/2000/svg') return [];
|
|
4760
4734
|
if (!isRenderedForBrowserRule(el)) return [];
|
|
4761
4735
|
// Only the element that actually owns overflowing text — not its ancestors,
|
|
4762
4736
|
// which inherit a wider scrollWidth from the spilling descendant.
|
|
@@ -5141,6 +5115,22 @@ function isPaintedForOcclusion(el) {
|
|
|
5141
5115
|
// path is pure geometry and runs anywhere on the page.
|
|
5142
5116
|
const OCCLUSION_TEXT_SKIP_TAGS = new Set(['script', 'style', 'noscript', 'template', 'title']);
|
|
5143
5117
|
|
|
5118
|
+
// An element whose effective opacity multiplies out to ~0 paints nothing at
|
|
5119
|
+
// rest: it is not user-visible, so visual findings on it (contrast, occlusion)
|
|
5120
|
+
// measure a state nobody sees. Browser-only — the walk needs live computed
|
|
5121
|
+
// styles. Cycling scenes that fade such elements in later are the screenshot
|
|
5122
|
+
// subsystem's territory, not the analytic walk's.
|
|
5123
|
+
function effectiveOpacityDOM(el) {
|
|
5124
|
+
let o = 1;
|
|
5125
|
+
// Walk all the way through body and html: `body { opacity: 0 }` page-fade
|
|
5126
|
+
// wrappers hide every descendant just as thoroughly as a local wrapper.
|
|
5127
|
+
for (let cur = el; cur && cur.nodeType === 1; cur = cur.parentElement) {
|
|
5128
|
+
o *= parseFloat(getComputedStyle(cur).opacity || '1');
|
|
5129
|
+
if (o <= 0.02) return 0;
|
|
5130
|
+
}
|
|
5131
|
+
return o;
|
|
5132
|
+
}
|
|
5133
|
+
|
|
5144
5134
|
function checkTextOcclusionDOM() {
|
|
5145
5135
|
const findings = [];
|
|
5146
5136
|
const seenVictims = new Set();
|
|
@@ -5168,6 +5158,11 @@ function checkTextOcclusionDOM() {
|
|
|
5168
5158
|
}
|
|
5169
5159
|
return false;
|
|
5170
5160
|
};
|
|
5161
|
+
// The classic occluder shape this rules out is an opacity-0 interaction
|
|
5162
|
+
// layer — a range scrubber stretched over a before/after comparison — which
|
|
5163
|
+
// elementFromPoint still returns and whose UA background-color otherwise
|
|
5164
|
+
// reads as an opaque box.
|
|
5165
|
+
const effectiveOpacity = effectiveOpacityDOM;
|
|
5171
5166
|
|
|
5172
5167
|
// Collect renderable text owners in / near the first viewport for the
|
|
5173
5168
|
// elementFromPoint probe. SVG <text> counts too.
|
|
@@ -5180,6 +5175,7 @@ function checkTextOcclusionDOM() {
|
|
|
5180
5175
|
const text = inSvg ? (el.textContent || '').trim() : elementDirectText(el);
|
|
5181
5176
|
if (text.length < 2) continue;
|
|
5182
5177
|
if (!isPaintedForOcclusion(el)) continue;
|
|
5178
|
+
if (effectiveOpacity(el) <= 0.02) continue;
|
|
5183
5179
|
let rect; try { rect = el.getBoundingClientRect(); } catch { continue; }
|
|
5184
5180
|
if (rect.width < 6 || rect.height < 6) continue;
|
|
5185
5181
|
// Viewport-bound probe: keep text whose box overlaps the live viewport.
|
|
@@ -5213,6 +5209,7 @@ function checkTextOcclusionDOM() {
|
|
|
5213
5209
|
if (top === el || el.contains(top) || top.contains(el)) continue;
|
|
5214
5210
|
const topCs = getComputedStyle(top);
|
|
5215
5211
|
if (isFloated(topCs) || isMarqueeish(top, topCs) || isPinnedOverlay(top)) continue;
|
|
5212
|
+
if (effectiveOpacity(top) <= 0.02) continue;
|
|
5216
5213
|
const topTag = top.tagName.toLowerCase();
|
|
5217
5214
|
// Text sitting under a raw image/video is contrast territory (deduped
|
|
5218
5215
|
// against the pixel low-contrast rule); leave those alone here.
|
|
@@ -5423,6 +5420,7 @@ export {
|
|
|
5423
5420
|
CSS_NAMED_COLORS,
|
|
5424
5421
|
checkBorders,
|
|
5425
5422
|
isEmojiOnlyText,
|
|
5423
|
+
scopedIgnoreActive,
|
|
5426
5424
|
checkColors,
|
|
5427
5425
|
checkHoverContrast,
|
|
5428
5426
|
checkElementHoverContrast,
|
|
@@ -5448,9 +5446,11 @@ export {
|
|
|
5448
5446
|
cssLengthToPx,
|
|
5449
5447
|
scanCssTextForPulsingDot,
|
|
5450
5448
|
scanHtmlForShapeAssembledIllustration,
|
|
5449
|
+
buildHtmlPatternCorpora,
|
|
5451
5450
|
checkHtmlPatterns,
|
|
5452
5451
|
readOwnBackgroundColor,
|
|
5453
5452
|
resolveBackground,
|
|
5453
|
+
resolveBackgroundInfo,
|
|
5454
5454
|
resolveGradientStops,
|
|
5455
5455
|
parseRadiusToPx,
|
|
5456
5456
|
resolveBorderRadiusPx,
|