diff --git a/.cursor/.gitignore b/.cursor/.gitignore
new file mode 100644
index 000000000..8bf7cc27a
--- /dev/null
+++ b/.cursor/.gitignore
@@ -0,0 +1 @@
+plans/
diff --git a/.cursor/commands/accessibility-audit.md b/.cursor/commands/accessibility-audit.md
new file mode 100644
index 000000000..8d1f56ba4
--- /dev/null
+++ b/.cursor/commands/accessibility-audit.md
@@ -0,0 +1,41 @@
+# Accessibility Audit
+
+## Overview
+
+Perform comprehensive accessibility (a11y) audit of the current UI code to ensure compliance with WCAG guidelines and provide an inclusive user experience.
+
+## Steps
+
+1. **WCAG Compliance**
+ - Check conformance to WCAG 2.1 guidelines (A, AA, AAA levels)
+ - Verify proper semantic HTML structure
+ - Ensure keyboard navigation support
+ - Review color contrast and visual accessibility
+2. **Screen Reader Support**
+ - Validate ARIA labels and descriptions
+ - Check heading hierarchy and structure
+ - Ensure form labels and error messages are accessible
+ - Review dynamic content announcements
+3. **Interactive Elements**
+ - Verify focus management and visible focus indicators
+ - Check tab order and keyboard shortcuts
+ - Ensure interactive elements are properly sized
+ - Review modal and dialog accessibility
+4. **Testing & Tools**
+ - Suggest automated accessibility testing tools
+ - Provide manual testing procedures
+ - Create accessibility test cases
+ - Recommend browser extensions and validators
+5. **Remediation**
+ - Provide specific code fixes for each issue
+ - Include ARIA attributes and semantic improvements
+ - Suggest alternative approaches for complex interactions
+ - Create accessible component patterns
+
+## Accessibility Audit Checklist
+
+- [ ] WCAG compliance verified
+- [ ] Screen reader support validated
+- [ ] Interactive elements accessible
+- [ ] Testing tools recommended
+- [ ] Remediation code provided with examples
diff --git a/.cursor/commands/address-github-pr-comments.md b/.cursor/commands/address-github-pr-comments.md
new file mode 100644
index 000000000..9a522cd1a
--- /dev/null
+++ b/.cursor/commands/address-github-pr-comments.md
@@ -0,0 +1,32 @@
+# Address GitHub PR Comments
+
+## Overview
+
+Process outstanding reviewer feedback, apply required fixes, and draft clear responses for each GitHub pull-request comment.
+
+## Steps
+
+1. **Sync and audit comments**
+ - Pull the latest branch changes
+ - Open the PR conversation view and read every unresolved comment
+ - Group comments by affected files or themes
+2. **Plan resolutions**
+ - List the requested code edits for each thread
+ - Identify clarifications or additional context you must provide
+ - Note any dependencies or blockers before implementing changes
+3. **Implement fixes**
+ - Apply targeted updates addressing one comment thread at a time
+ - Run relevant tests or linters after impactful changes
+ - Stage changes with commits that reference the addressed feedback
+4. **Draft responses**
+ - Summarize the action taken or reasoning provided for each comment
+ - Link to commits or lines when clarification helps reviewers verify
+ - Highlight any remaining questions or follow-up needs
+
+## Response Checklist
+
+- [ ] All reviewer comments acknowledged
+- [ ] Required code changes implemented and tested
+- [ ] Clarifying explanations prepared for nuanced threads
+- [ ] Follow-up items documented or escalated
+- [ ] PR status updated for reviewers
diff --git a/.cursor/commands/code-review.md b/.cursor/commands/code-review.md
new file mode 100644
index 000000000..27f77deea
--- /dev/null
+++ b/.cursor/commands/code-review.md
@@ -0,0 +1,54 @@
+# Code Review
+
+## Overview
+
+Perform a thorough code review that verifies functionality, maintainability, and security before approving a change. Focus on architecture, readability, performance implications, and provide actionable suggestions for improvement.
+
+## Steps
+
+1. **Understand the change**
+ - Read the PR description and related issues for context
+ - Identify the scope of files and features impacted
+ - Note any assumptions or questions to clarify with the author
+2. **Validate functionality**
+ - Confirm the code delivers the intended behavior
+ - Exercise edge cases or guard conditions mentally or by running locally
+ - Check error handling paths and logging for clarity
+3. **Assess quality**
+ - Ensure functions are focused, names are descriptive, and code is readable
+ - Watch for duplication, dead code, or missing tests
+ - Verify documentation and comments reflect the latest changes
+4. **Review security and risk**
+ - Look for injection points, insecure defaults, or missing validation
+ - Confirm secrets or credentials are not exposed
+ - Evaluate performance or scalability impacts of the change
+
+## Review Checklist
+
+### Functionality
+
+- [ ] Intended behavior works and matches requirements
+- [ ] Edge cases handled gracefully
+- [ ] Error handling is appropriate and informative
+
+### Code Quality
+
+- [ ] Code structure is clear and maintainable
+- [ ] No unnecessary duplication or dead code
+- [ ] Tests/documentation updated as needed
+
+### Security & Safety
+
+- [ ] No obvious security vulnerabilities introduced
+- [ ] Inputs validated and outputs sanitized
+- [ ] Sensitive data handled correctly
+
+## Additional Review Notes
+
+- Architecture and design decisions considered
+- Performance bottlenecks or regressions assessed
+- Coding standards and best practices followed
+- Resource management, error handling, and logging reviewed
+- Suggested alternatives, additional test cases, or documentation updates captured
+
+Provide constructive feedback with concrete examples and actionable guidance for the author.
diff --git a/.cursor/commands/optimize-performance.md b/.cursor/commands/optimize-performance.md
new file mode 100644
index 000000000..bd4566ef7
--- /dev/null
+++ b/.cursor/commands/optimize-performance.md
@@ -0,0 +1,35 @@
+# Optimize Performance
+
+## Overview
+
+Analyze the current code for performance bottlenecks and provide optimization recommendations, focusing on measurable improvements while maintaining code quality and readability.
+
+## Steps
+
+1. **Performance Analysis**
+ - Identify slow algorithms and inefficient data structures
+ - Find memory leaks and excessive allocations
+ - Detect unnecessary computations and redundant operations
+ - Analyze database queries and API calls
+2. **Optimization Strategies**
+ - Suggest algorithm improvements and better data structures
+ - Recommend caching strategies where appropriate
+ - Propose lazy loading and pagination solutions
+ - Identify opportunities for parallel processing
+3. **Implementation**
+ - Provide optimized code with explanations
+ - Include performance impact estimates
+ - Suggest profiling and monitoring approaches
+ - Consider trade-offs between performance and maintainability
+
+## Optimize Performance Checklist
+
+- [ ] Identified slow algorithms and inefficient data structures
+- [ ] Found memory leaks and excessive allocations
+- [ ] Detected unnecessary computations and redundant operations
+- [ ] Analyzed database queries and API calls
+- [ ] Suggested algorithm improvements and better data structures
+- [ ] Recommended caching strategies where appropriate
+- [ ] Provided optimized code with explanations
+- [ ] Included performance impact estimates
+- [ ] Considered trade-offs between performance and maintainability
diff --git a/.cursor/commands/refactor-code.md b/.cursor/commands/refactor-code.md
new file mode 100644
index 000000000..b44d3eb4b
--- /dev/null
+++ b/.cursor/commands/refactor-code.md
@@ -0,0 +1,35 @@
+# Refactor Code
+
+## Overview
+
+Refactor the selected code to improve its quality while maintaining the same functionality, providing the refactored code with explanations of the improvements made.
+
+## Steps
+
+1. **Code Quality Improvements**
+ - Extract reusable functions or components
+ - Eliminate code duplication
+ - Improve variable and function naming
+ - Simplify complex logic and reduce nesting
+2. **Performance Optimizations**
+ - Identify and fix performance bottlenecks
+ - Optimize algorithms and data structures
+ - Reduce unnecessary computations
+ - Improve memory usage
+3. **Maintainability**
+ - Make the code more readable and self-documenting
+ - Add appropriate comments where needed
+ - Follow SOLID principles and design patterns
+ - Improve error handling and edge case coverage
+
+## Refactor Code Checklist
+
+- [ ] Extracted reusable functions or components
+- [ ] Eliminated code duplication
+- [ ] Improved variable and function naming
+- [ ] Simplified complex logic and reduced nesting
+- [ ] Identified and fixed performance bottlenecks
+- [ ] Optimized algorithms and data structures
+- [ ] Made code more readable and self-documenting
+- [ ] Followed SOLID principles and design patterns
+- [ ] Improved error handling and edge case coverage
diff --git a/.cursor/commands/security-audit.md b/.cursor/commands/security-audit.md
new file mode 100644
index 000000000..96f1e27cd
--- /dev/null
+++ b/.cursor/commands/security-audit.md
@@ -0,0 +1,28 @@
+# Security Audit
+
+## Overview
+
+Comprehensive security review to identify and fix vulnerabilities in the codebase.
+
+## Steps
+
+1. **Dependency audit**
+ - Check for known vulnerabilities
+ - Update outdated packages
+ - Review third-party dependencies
+2. **Code security review**
+ - Check for common vulnerabilities
+ - Review authentication/authorization
+ - Audit data handling practices
+3. **Infrastructure security**
+ - Review environment variables
+ - Check access controls
+ - Audit network security
+
+## Security Checklist
+
+- [ ] Dependencies updated and secure
+- [ ] No hardcoded secrets
+- [ ] Input validation implemented
+- [ ] Authentication secure
+- [ ] Authorization properly configured
diff --git a/.cursor/commands/write-unit-tests.md b/.cursor/commands/write-unit-tests.md
new file mode 100644
index 000000000..7fc8dd6d8
--- /dev/null
+++ b/.cursor/commands/write-unit-tests.md
@@ -0,0 +1,42 @@
+# Write Unit Tests
+
+## Overview
+
+Create comprehensive unit tests for the current code and generate the test file with proper imports and setup according to the project's testing conventions.
+
+## Steps
+
+1. **Test Coverage**
+ - Test all public methods and functions
+ - Cover edge cases and error conditions
+ - Test both positive and negative scenarios
+ - Aim for high code coverage
+2. **Test Structure**
+ - Use the project's testing framework conventions
+ - Write clear, descriptive test names
+ - Follow the Arrange-Act-Assert pattern
+ - Group related tests logically
+3. **Test Cases to Include**
+ - Happy path scenarios
+ - Edge cases and boundary conditions
+ - Error handling and exception cases
+ - Mock external dependencies appropriately
+4. **Test Quality**
+ - Make tests independent and isolated
+ - Ensure tests are deterministic and repeatable
+ - Keep tests simple and focused on one thing
+ - Add helpful assertion messages
+
+## Write Unit Tests Checklist
+
+- [ ] Tested all public methods and functions
+- [ ] Covered edge cases and error conditions
+- [ ] Tested both positive and negative scenarios
+- [ ] Used the project's testing framework conventions
+- [ ] Written clear, descriptive test names
+- [ ] Followed the Arrange-Act-Assert pattern
+- [ ] Included happy path scenarios
+- [ ] Included edge cases and boundary conditions
+- [ ] Mocked external dependencies appropriately
+- [ ] Made tests independent and isolated
+- [ ] Ensured tests are deterministic and repeatable
diff --git a/.cursor/rules/core-development.mdc b/.cursor/rules/core-development.mdc
new file mode 100644
index 000000000..a7cf0cb49
--- /dev/null
+++ b/.cursor/rules/core-development.mdc
@@ -0,0 +1,37 @@
+---
+description: Core development workflow and critical thinking rules
+alwaysApply: true
+---
+
+# Core Development
+
+Read `AGENTS.md` at the repository root for full project standards, coding conventions, and architecture.
+
+## Critical Partner Mindset
+
+Do not affirm my statements or assume my conclusions are correct. Question assumptions, offer counterpoints, test reasoning. Prioritize truth over agreement.
+
+## Execution Sequence (always reply with "Applying rules X,Y,Z")
+
+1. SEARCH FIRST - Search the codebase until finding similar functionality or confirming none exists. Investigate deeply, be 100% sure before implementing.
+2. REUSE FIRST - Check existing functions/patterns/structure. Extend before creating new. Strive to smallest possible code changes.
+3. NO ASSUMPTIONS - Only use: files read, user messages, tool results. Missing info? Search then ask user.
+4. CHALLENGE IDEAS - If you see flaws/risks/better approaches, say so directly.
+5. BE HONEST - State what's needed/problematic, don't sugarcoat to please.
+6. SELF-CHECK - Periodically self-check rule compliance during long conversations.
+7. RULE REFRESH - Re-check this rules file every few messages to stay compliant.
+
+## Coding Standards
+
+- Plan before coding, explain reasoning for complex suggestions
+- Follow the code comment guidance in `AGENTS.md`
+- Keep imports alphabetically sorted
+- Keep code SOLID but simple - separation of concerns without over-engineering
+- Treat file length as a signal; split only for reuse, meaningful separation, or clearer testing
+- Follow the testing requirements in `AGENTS.md`. Use the AAA pattern.
+- Run `npm run lint` for lint check
+
+## Prohibited Actions
+
+- DO NOT WRITE DOCS UNLESS EXPLICITLY ASKED TO
+- NEVER run `npm run watch` command - assume dev servers always running
diff --git a/.cursor/rules/declarative-react.mdc b/.cursor/rules/declarative-react.mdc
new file mode 100644
index 000000000..609e2f28f
--- /dev/null
+++ b/.cursor/rules/declarative-react.mdc
@@ -0,0 +1,158 @@
+---
+description: Enforces declarative React coding patterns, avoiding imperative DOM manipulation and useEffect chains in favor of derived state and direct updates
+globs: ["**/*.tsx", "**/*.jsx", "**/*.ts", "**/*.js"]
+alwaysApply: false
+---
+
+# Declarative React
+
+Always write React code in a declarative style.
+
+## Core Principles
+
+1. Prefer **functional components** with JSX and hooks over class components.
+2. **Never** manipulate the DOM directly (e.g., `document.querySelector`, `innerHTML`). Instead, let React's render cycle handle UI updates via **state** and **props**.
+3. Use hooks (`useState`, `useReducer`, `useEffect`, `useMemo`, `useCallback`) responsibly:
+ - Avoid running imperative logic in `useEffect` that can be expressed through declarative data-flow.
+ - Derive data instead of duplicating state.
+4. Separate components when doing so clarifies responsibility or enables reuse. Prefer conditional rendering (`{condition && }`) and array mapping to create lists. Reserve `condition ? : null` for numeric or string conditions that would otherwise render.
+5. Keep side-effects isolated; only place effectful code inside `useEffect`, `useLayoutEffect`, or custom hooks.
+6. Use **refs** sparingly and only for non-DOM data persistence, not for styling or visibility toggling.
+7. Prefer **controlled components** for forms. Keep input values in React state.
+8. Pass data downward via props; avoid event emitters or global variables for child-parent communication, unless using a proper state management solution.
+9. Use unique `key` props for list items to maintain element identity.
+10. Favor pure functions and immutable data patterns to make render output predictable.
+11. **Avoid cascading state updates**: Never create chains where updating one state triggers a `useEffect` that updates another state. Instead, derive values using `useMemo` or calculate them during render.
+12. **Prefer direct state updates over useEffect chains**: When you need to update multiple related state values, do it directly in the event handler rather than creating cascading useEffect dependencies.
+13. **Keep hooks non-visual**: Custom hooks return state and actions, not rendered elements.
+14. **Avoid trivial memoization**: Calculate inexpensive derived values during render instead of wrapping them in `useMemo` or `useCallback`.
+
+## Anti-Patterns to Avoid
+
+- Direct DOM manipulation (`document.getElementById`, `jQuery`, etc.)
+- Calling imperative animations or style changes outside React's lifecycle
+- Mutating state directly (e.g., `state.value = 1`). Always use the state setter returned by `useState` or a reducer action
+- Fetching or performing expensive computations during render
+- Using refs to bypass React for UI updates instead of relying on state
+
+## Derived State vs Cascading Updates
+
+**Avoid:**
+
+```jsx
+const [users, setUsers] = useState([]);
+const [filteredUsers, setFilteredUsers] = useState([]);
+const [searchTerm, setSearchTerm] = useState('');
+
+useEffect(() => {
+ const filtered = users.filter(user =>
+ user.name.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+ setFilteredUsers(filtered);
+}, [users, searchTerm]);
+```
+
+**Prefer:**
+
+```jsx
+const [users, setUsers] = useState([]);
+const [searchTerm, setSearchTerm] = useState('');
+
+const filteredUsers = useMemo(() =>
+ users.filter(user =>
+ user.name.toLowerCase().includes(searchTerm.toLowerCase())
+ ), [users, searchTerm]
+);
+```
+
+## Complex State Derivation
+
+For complex state transformations, declare what you want step by step:
+
+```jsx
+function UserDashboard() {
+ const [users, setUsers] = useState([]);
+ const [selectedCategory, setSelectedCategory] = useState('all');
+ const [sortOrder, setSortOrder] = useState('asc');
+
+ const categoryFiltered = useMemo(() =>
+ selectedCategory === 'all'
+ ? users
+ : users.filter(user => user.category === selectedCategory)
+ , [users, selectedCategory]);
+
+ const sortedAndFiltered = useMemo(() =>
+ [...categoryFiltered].sort((a, b) =>
+ sortOrder === 'asc'
+ ? a.name.localeCompare(b.name)
+ : b.name.localeCompare(a.name)
+ )
+ , [categoryFiltered, sortOrder]);
+
+ const groupedUsers = useMemo(() =>
+ sortedAndFiltered.reduce((groups, user) => {
+ const key = user.department;
+ groups[key] = groups[key] || [];
+ groups[key].push(user);
+ return groups;
+ }, {})
+ , [sortedAndFiltered]);
+
+ return (
+
+ {/* Render UI based on derived state */}
+
+ );
+}
+```
+
+## Imperative vs Declarative Examples
+
+**Avoid:**
+
+```js
+componentDidMount() {
+ document.getElementById('sidebar').style.display = 'none';
+}
+```
+
+**Prefer:**
+
+```jsx
+function Sidebar({ isOpen }) {
+ if (!isOpen) return null;
+ return ;
+}
+```
+
+## Direct State Updates vs useEffect Chains
+
+**Avoid:**
+
+```jsx
+function handleSubmit() {
+ setLoading(true);
+}
+
+useEffect(() => {
+ if (loading) {
+ setError(null);
+ }
+}, [loading]);
+
+useEffect(() => {
+ if (loading && !error) {
+ setSubmitted(false);
+ }
+}, [loading, error]);
+```
+
+**Prefer:**
+
+```jsx
+function handleSubmit() {
+ setLoading(true);
+ setError(null);
+ setSubmitted(false);
+}
+```
diff --git a/.cursor/rules/development-guidelines.mdc b/.cursor/rules/development-guidelines.mdc
new file mode 100644
index 000000000..955cc8549
--- /dev/null
+++ b/.cursor/rules/development-guidelines.mdc
@@ -0,0 +1,53 @@
+---
+description: Planning, file organization, testing, typing, clean code, and code quality guidelines
+alwaysApply: true
+---
+
+# Development Guidelines
+
+## Planning
+
+- Always plan the code before writing it
+- Think about how the new code will:
+ - Fit into the existing codebase
+ - Interact with other parts of the codebase
+ - Handle errors and edge cases
+ - Be used by the frontend
+ - Be used by the users or by the developers
+
+## File Organization
+
+- Keep files focused on a single responsibility or closely related functionality
+- Treat file length as a signal; split only for reuse, meaningful separation, or clearer testing
+- Avoid single-use helper files and pass-through abstractions
+- Use clear directory structure to organize related files
+
+## Testing Requirements
+
+- Follow the testing requirements in `AGENTS.md`
+- Follow existing testing patterns and conventions in the codebase
+- Ensure tests are deterministic and properly isolated
+
+## Typing
+
+- Always use proper TypeScript types for all variables, parameters, and return values
+- Avoid using `any` type unless absolutely necessary
+- Try finding existing types / defined package types, and re-use them or build on top of them instead of creating new ones
+- For functions with 3+ parameters, use types for better maintainability
+
+## Clean Code
+
+- Write clean, readable, and maintainable code
+- Keep functions small and focused
+- Use descriptive and clear variable, function, and class names that explain their purpose
+- Prefer self-documenting code with meaningful names over excessive comments
+- Add comments only when needed to explain complex business logic or non-obvious decisions
+- Avoid redundant or obvious comments that just restate what the code does
+
+## Code Quality
+
+- Don't Repeat Yourself (DRY): Identify and refactor duplicated code
+- Single Responsibility Principle (SRP): Ensure each module/function has one responsibility
+- Separation of Concerns: Ensure different concerns are handled in separate modules/components
+- Meaningful Names: Verify that names are descriptive and adhere to conventions
+- Parameter Handling: Avoid redundant parameter extraction; keep parameters close to the logic where they are used
diff --git a/.cursor/rules/development-workflow.mdc b/.cursor/rules/development-workflow.mdc
new file mode 100644
index 000000000..f2aa448fa
--- /dev/null
+++ b/.cursor/rules/development-workflow.mdc
@@ -0,0 +1,54 @@
+---
+description: Development workflow guidelines for before, during, and after implementation
+alwaysApply: true
+---
+
+# Development Workflow
+
+## Before Starting Work
+
+1. **Search First**: Use codebase_search to find existing functionality
+2. **Understand Architecture**: Know if it's main repo or turbo-repo work
+3. **Check Dependencies**: Understand package relationships
+4. **Plan API Design**: Design interfaces before implementation
+5. **Sync Target Branch**: Fetch the target branch and verify it hasn't changed the files you plan to modify since you branched. If it has, read the new version before proceeding
+6. **Scope by Layer**: Plan separate PRs for independent layers (PHP vs JS/TS, backend vs frontend, features vs fixes). Smaller, layer-scoped PRs merge cleaner, review faster, and survive upstream changes
+
+## During Development
+
+1. **Follow Patterns**: Use existing patterns and conventions
+2. **Write Tests**: Follow the testing requirements in `AGENTS.md`
+3. **Type Safety**: Use TypeScript strictly
+4. **Document APIs**: Document all public interfaces
+
+## After Implementation
+
+1. **Run Tests**: Run and verify the merge gate required by `AGENTS.md` before release integration
+2. **Lint Code**: Fix any linting issues
+3. **Check Dependencies**: Verify no circular dependencies
+4. **Test Integration**: Verify with other packages
+
+## Performance Considerations
+
+### Bundle size
+
+- Monitor bundle size changes
+- Use dynamic imports for large components
+- Optimize images and assets
+- Remove unused dependencies
+
+### Runtime performance
+
+- Profile React components
+- Optimize re-renders
+- Use proper memoization
+- Monitor memory usage
+
+## Security Guidelines
+
+- Sanitize all user inputs
+- Validate data types and ranges
+- Use proper escaping
+- Check user permissions
+- Keep dependencies updated
+- Audit for vulnerabilities
diff --git a/.cursor/rules/general-code-style.mdc b/.cursor/rules/general-code-style.mdc
new file mode 100644
index 000000000..8cbc1fdbe
--- /dev/null
+++ b/.cursor/rules/general-code-style.mdc
@@ -0,0 +1,38 @@
+---
+description: Code style rules for magic numbers, error codes, and self-documenting code
+alwaysApply: true
+---
+
+# Code Style
+
+## Avoid Magic Numbers
+
+- Do not use unexplained hardcoded values ("magic numbers") in code or tests
+- Define such values as named constants or use existing constants to clarify their meaning
+
+## Consistent Error Codes and Status
+
+- When returning error codes and HTTP status, always be very specific to use the correct code, not only 200 and 500
+
+## Prioritize Style and Developer Experience
+
+- Always pay attention for clarity, maintainability, and ease of understanding, even if the underlying logic does not change
+- Code style and developer experience are important for long-term project health
+
+## Self-Documented Code
+
+- Avoid adding comments that can be a constant or a well-named function
+- Prefer existing or local functions for small, stateless, single-use behavior; extract helpers when they provide reuse
+ or meaningful separation
+- Only add comments to explain "why" when it is truly not understandable from the code itself
+- Do NOT add comments that explain "what" the code does (the code should be self-explanatory)
+- Examples of unnecessary comments to avoid:
+ - `// Loop through items` (obvious from for loop)
+ - `// Check if user exists` (obvious from if statement)
+ - `// Return the result` (obvious from return statement)
+ - JSDoc comments that just repeat the function name or parameter names
+- Examples of valuable comments to keep:
+ - Business logic explanations that aren't obvious from code
+ - Workarounds for bugs in third-party libraries
+ - Performance optimizations that might look odd without context
+ - Complex algorithms where the "why" matters more than the "what"
diff --git a/.cursor/rules/typescript-method-typing.mdc b/.cursor/rules/typescript-method-typing.mdc
new file mode 100644
index 000000000..0451e9595
--- /dev/null
+++ b/.cursor/rules/typescript-method-typing.mdc
@@ -0,0 +1,38 @@
+---
+description: Use typed parameter objects for functions with 3 or more parameters
+globs: ["**/*.ts", "**/*.tsx"]
+alwaysApply: false
+---
+
+# Method Parameter Typing
+
+## Functions with 3+ Parameters
+
+When a function or method has 3 or more parameters, use a type for better maintainability and type safety.
+
+**Avoid:**
+
+```typescript
+function processData(param1: string, param2: number, param3: boolean, param4: string) {
+ // implementation
+}
+```
+
+**Prefer:**
+
+```typescript
+type ProcessDataArgs = {
+ param1: string;
+ param2: number;
+ param3: boolean;
+ param4: string;
+};
+
+function processData(args: ProcessDataArgs) {
+ // implementation
+}
+```
+
+## Auto-Suggestion Rule
+
+When you see a function with 3 or more parameters, suggest refactoring to use a type for the parameters.
diff --git a/.cursor/rules/wordpress.mdc b/.cursor/rules/wordpress.mdc
new file mode 100644
index 000000000..bc4708413
--- /dev/null
+++ b/.cursor/rules/wordpress.mdc
@@ -0,0 +1,73 @@
+---
+description: WordPress and PHP best practices, coding standards, and key conventions
+globs: ["**/*.php"]
+alwaysApply: false
+---
+
+# WordPress
+
+You are an expert in WordPress, PHP, and related web development technologies.
+
+## Core Principles
+
+- Provide precise, technical PHP and WordPress examples
+- Adhere to PHP and WordPress best practices for consistency and readability
+- Use OOP for state, lifecycle, contracts, or reusable behavior; prefer existing or local functions for small, stateless,
+ single-use behavior
+- Focus on code reusability through iteration and modularization, avoiding duplication
+- Use descriptive and meaningful function, variable, and file names
+- Directory naming conventions: lowercase with hyphens (e.g., wp-content/themes/my-theme)
+- Use WordPress hooks (actions and filters) for extending functionality
+
+## PHP/WordPress Coding Practices
+
+- Utilize features of PHP 7.4+ (e.g., typed properties, arrow functions) where applicable
+- Follow WordPress PHP coding standards throughout the codebase
+- Prefer `declare(strict_types=1);` in appropriate new or already-strict code; do not add it as unrelated cleanup in
+ existing integration files
+- Leverage core WordPress functions and APIs wherever possible
+- Maintain WordPress theme and plugin directory structure and naming conventions
+- Implement robust error handling:
+ - Use WordPress's built-in debug logging (WP_DEBUG_LOG)
+ - Implement custom error handlers if necessary
+ - Apply try-catch blocks for controlled exception handling
+- Always use WordPress's built-in functions for data validation and sanitization
+- Ensure secure form handling by verifying nonces in submissions
+- For database interactions:
+ - Use WordPress's `$wpdb` abstraction layer
+ - Apply `prepare()` statements for all dynamic queries to prevent SQL injection
+ - Use the `dbDelta()` function for schema changes only after custom storage is justified under `AGENTS.md`
+
+## Dependencies
+
+- Ensure compatibility with the repository's minimum supported WordPress and PHP versions
+- Use Composer for dependency management in advanced plugins or themes
+
+## WordPress Best Practices
+
+- Use child themes for customizations to preserve update compatibility
+- Never modify core WordPress files — extend using hooks (actions and filters)
+- Organize theme-specific functions within `functions.php`
+- Use WordPress's user roles and capabilities for managing permissions
+- Apply the transients API for caching data and optimizing performance
+- Implement background processing tasks using `wp_cron()` for long-running operations
+- Write unit tests using WordPress's built-in `WP_UnitTestCase` framework
+- Follow best practices for internationalization (i18n) by using WordPress localization functions
+- Apply proper security practices such as nonce verification, input sanitization, and data escaping
+- Manage scripts and styles by using `wp_enqueue_script()` and `wp_enqueue_style()`
+- Use custom post types and taxonomies when necessary to extend WordPress functionality
+- Store configuration data securely using WordPress's options API
+- Implement pagination effectively with functions like `paginate_links()`
+
+## Key Conventions
+
+1. Follow WordPress's plugin API to extend functionality in a modular and scalable manner
+2. Use WordPress's template hierarchy when developing themes to ensure flexibility
+3. Apply WordPress's built-in functions for data sanitization and validation to secure user inputs
+4. Implement WordPress's template tags and conditional tags in themes for dynamic content handling
+5. For custom queries, use `$wpdb` or `WP_Query` for database interactions
+6. Use WordPress's authentication and authorization mechanisms for secure access control
+7. For AJAX requests, use `admin-ajax.php` or the WordPress REST API for handling backend requests
+8. Always apply WordPress's hook system (actions and filters) for extensible and modular code
+9. Implement database operations using transactional functions where needed
+10. Schedule tasks using WordPress's WP_Cron API for automated workflows
diff --git a/.githooks/pre-commit b/.githooks/pre-commit
new file mode 100755
index 000000000..7e617c2c7
--- /dev/null
+++ b/.githooks/pre-commit
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+npx --no-install lint-staged
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 000000000..ff61c8fe1
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,71 @@
+# Code Snippets — Copilot Instructions
+
+> **Project standards** live in `AGENTS.md` at the repository root. Read that file in full before performing
+> any task. This file contains only GitHub Copilot-specific configuration.
+
+---
+
+## MCP Tools — Context7 and Chrome DevTools
+
+When working on tasks that involve library documentation, API references, or browser debugging, use the available
+MCP tools for better, up-to-date results.
+
+### Context7 (`mcp_context7_*`)
+
+Use Context7 to look up accurate, version-specific documentation for any library used in this project
+(WordPress, React, TypeScript, CodeMirror, Playwright, etc.) rather than relying on training-data knowledge.
+
+**When to use:**
+
+- Looking up WordPress hook signatures, REST API schemas, or WP component props.
+- Confirming TypeScript / React API details.
+- Checking Playwright test API or assertion methods.
+- Any time you are about to write code that depends on a third-party API you are not 100% certain about.
+
+**How to use:**
+
+1. Call `mcp_context7_resolve-library-id` with the library name to get its Context7 ID.
+2. Call `mcp_context7_query-docs` with the resolved ID and a specific question.
+
+**If Context7 is not installed:**
+
+Ask the user: *"The Context7 MCP server is not available. Would you like to set it up? It gives me access to
+up-to-date library docs. Install via: `npx -y @upstash/context7-mcp@latest` and add it to your MCP config."*
+
+### Chrome DevTools (`mcp_chrome-devtoo_*`)
+
+Use Chrome DevTools MCP for any task involving the browser UI: inspecting rendered admin pages, debugging
+JavaScript errors, validating accessibility (contrast, ARIA), checking network requests, or running
+performance traces.
+
+**When to use:**
+
+- Debugging a UI regression or layout issue in the WP admin.
+- Verifying that a REST API call returns the expected payload.
+- Checking console errors after a JS change.
+- Validating accessibility of admin UI changes (colour contrast, keyboard focus, ARIA).
+- Running a Lighthouse / performance trace on a frontend page.
+
+**How to use:**
+
+Use the `mcp_chrome-devtoo_*` family of tools — take a snapshot, navigate a page, inspect network requests,
+evaluate scripts, or start a performance trace.
+
+**If Chrome DevTools MCP is not installed:**
+
+Ask the user: *"The Chrome DevTools MCP server is not available. Would you like to set it up? It lets me
+inspect the live browser state. Install via the VS Code MCP extension or add `@modelcontextprotocol/server-chrome`
+to your MCP config."*
+
+---
+
+## Path-Specific Instructions
+
+More targeted rules live in `.github/instructions/` as `*.instructions.md` files:
+
+| File | Scope |
+|---|---|
+| `code-review.instructions.md` | Copilot Code Review (CCR) — review standards (excludes coding agent) |
+
+Add new `*.instructions.md` files here for language- or area-specific rules (e.g., `php.instructions.md`,
+`react.instructions.md`).
diff --git a/.github/instructions/code-review.instructions.md b/.github/instructions/code-review.instructions.md
new file mode 100644
index 000000000..b18bf225e
--- /dev/null
+++ b/.github/instructions/code-review.instructions.md
@@ -0,0 +1,166 @@
+---
+applyTo: "**/*"
+excludeAgent: "coding-agent"
+description: "Repository code-review standards for Copilot Code Review (CCR). Follow the severity model, security checks, WP conventions, i18n, accessibility, tests, and suggested remediation steps."
+---
+
+# Code Review Standards for Code Snippets
+
+## Purpose
+
+These instructions guide Copilot code review across all files in the Code Snippets WordPress plugin repository.
+Use these rules when reviewing pull requests to produce specific, actionable feedback.
+
+Severity labels used in this file:
+- **MUST** — Flag as a blocking issue; must be resolved before merge.
+- **SHOULD** — Flag as a recommendation; resolve before merge unless a risk-aware rationale is provided.
+
+## Practical Reviewer Checks
+
+- Don't silently widen types or drop generics when refactoring. If typing gets weaker, require a clear reason. [MUST]
+- In namespaced PHP, call WordPress globals with `\function_name()` and do not assume pluggable/core functions exist on very early execution paths. If a callback can run during early bootstrap, guard availability appropriately. [MUST]
+- Ensure hook callbacks that respond to options/actions are tightly gated to the intended option/action. Be suspicious of inverted or overly broad conditionals that can be triggered by other plugins. [MUST]
+- Avoid no-op abstractions (pass-through helpers, one-liner wrappers) unless they materially improve readability, reuse, or testability. [SHOULD]
+- Inline single-use extractions that do not clarify intent (local functions/variables used once). [SHOULD]
+- Prefer `undefined` for absent optional values in TypeScript/React unless `null` has explicit semantics in that API. [SHOULD]
+- For conditional class names, prefer the repository `classnames.classnames` helper over manual array filtering and joining. [SHOULD]
+- Prefer JSX for React markup. If a hook/util needs to render elements, suggest moving the markup into a `.tsx` component instead of using `createElement` in a `.ts` file. [SHOULD]
+- For simple key-to-value parsing/transforms, prefer a literal object/record map over a loop + `switch` when it improves clarity. [SHOULD]
+- Do not stack redundant `catch` blocks (e.g., `ParseError` plus `Throwable`) unless the handlers differ. [SHOULD]
+- When a screen is React-driven, question heavy PHP view logic. If PHP is used due to WordPress admin primitives (e.g., Screen Options, non-REST file streaming), require a short rationale. [SHOULD]
+
+## Scope and Diff Hygiene
+
+- Flag PRs that mix behavior changes with refactors, renames, or formatting-only edits. Each change should be single-purpose. [MUST]
+- Flag renamed identifiers, files, UI labels, or data keys that lack a concrete justification in the commit message or comments. [MUST]
+- Look for indentation or formatting regressions in PHP, JS/TS, CSS, or Markdown. [MUST]
+- Identify redundant changes that do not alter behavior (e.g., pointless `printf`/wrapper edits). [MUST]
+- Check whether large changes could be split into smaller reviewable units (UI refactor vs logic vs data model). [SHOULD]
+- Flag "drive-by" cleanup outside the area being changed. [SHOULD]
+
+## Correctness, Resilience, and Types
+
+- Verify all external/variable inputs (request parameters, shortcode content, option values, API responses) are validated before use. [MUST]
+- Check that code does not assume array structure; verify the expected key/shape exists before parsing or indexing. [MUST]
+- Flag type inconsistencies (e.g., storing an "int-like" state as a `string` when it is treated as an `int`). [MUST]
+- Identify redundant checks and tautologies (e.g., checking a condition already guaranteed by casting or a documented union type). [MUST]
+- Look for inverted `if` chains that reduce readability; prefer early returns and simpler control flow. [SHOULD]
+- Check that conditionals use braces; flag unbraced single-line `if` statements. Prefer `switch` for multi-branch dispatch logic. [SHOULD]
+- Flag `if/else` chains that could be simplified as "default then override" to reduce nesting (e.g., initialize `$primary_button` then adjust fields). [SHOULD]
+- Flag mutation of list arrays via numeric offsets like `$buttons[0]`; prefer named local variables or associative keys. [SHOULD]
+- Identify repeated passes over the same data that could be consolidated (e.g., multiple `array_filter` iterations). [SHOULD]
+
+## WordPress Conventions and Internal APIs
+
+- Verify user-facing behavior and labels follow WordPress conventions and terminology (e.g., "Trash" and "Undo" patterns for reversible deletion). [MUST]
+- Flag hand-built admin page URLs; use platform/internal URL builders and constants instead. [MUST]
+- Flag use of fragile constants for environment/variant checks; use established internal APIs when a canonical method exists. [MUST]
+- Check for vendored assets that duplicate WordPress core functionality (e.g., dashicons, CodeMirror/linting scripts). Prefer core assets. [SHOULD]
+- Verify changes do not silently degrade when other plugins load conflicting assets. [SHOULD]
+
+## Extensibility: Hooks, Filters, and Configuration
+
+- Flag changes to default behavior that lack a non-UI escape hatch (filter/hook) or explicit configuration surface, when the behavior may be preference-driven. [MUST]
+- Flag hooks/actions added without a clear, long-term extension need. [MUST]
+- Check whether filters are preferred over new UI options for niche workflows (unless discoverability is critical or support burden demands UI). [SHOULD]
+- Flag "too-early" checks outside the runtime context they depend on; prefer evaluating conditions inside the actual hook callback. [SHOULD]
+
+## Security and Trust Boundaries
+
+- Flag dynamic execution patterns (e.g., `eval`, `create_function`) that increase security risk or trigger security-scanner false positives. Require a documented trust model if used. [MUST]
+- Flag remote code or remotely sourced content executed without an explicit, robust trust model. [MUST]
+- Verify escaping/sanitization is applied at the correct output boundary (attribute context, HTML context, JS context). Flag "random escaping" that breaks dependent scripts or UI. [MUST]
+- Check that shared UI renderers (base classes/helpers/templates) escape/sanitize their own inputs rather than relying on callers returning pre-escaped strings. [MUST]
+- Check every `target="_blank"` link for `rel="noopener noreferrer"`. Flag any missing instance unless a documented exception exists. [MUST]
+- Verify all state-changing requests (AJAX endpoints, form submissions, REST/action handlers) require and verify a WordPress nonce server-side. [MUST]
+- Verify capability checks are present alongside nonce checks for state-changing endpoints. [MUST]
+- Check that all external and remote inputs are validated server-side; flag reliance on client-side validation alone. [MUST]
+- Flag features that fetch or render remote content without a documented threat assessment (attack surface, trust model, mitigations). [MUST]
+- Check whether output helpers could be refactored to own correct escaping, rather than scattering escaping at call sites. [SHOULD]
+- Flag large opaque blobs (e.g., encoded payloads) shipped into contexts where security tools may flag them. [SHOULD]
+
+## Internationalization (i18n)
+
+- Verify correct translation functions are used for each string type and context (e.g., context-aware functions when the string is ambiguous or partial). [MUST]
+- Flag HTML markup placed inside translated strings unless there is a strong, documented reason. [MUST]
+- Flag access to translated labels before translation files are loaded. [MUST]
+- Check for concatenation of translated fragments; prefer translating full sentences/phrases. [SHOULD]
+
+## UX and Accessibility
+
+- Flag removal of accessibility-relevant context that lacks an equivalent or better replacement affordance. [MUST]
+- When `title` attributes are replaced with ARIA attributes, verify the change does not regress non-assistive UX (e.g., hover help) or reduce meaning for screen readers. [MUST]
+- Verify user settings that disable or hide UI elements are respected (e.g., do not render upsell/promotions when the "hide" setting is enabled). [MUST]
+- Check that dismissible notices actually persist dismissal for the users who can see them (capability checks, AJAX handlers, and nonces must align). [MUST]
+- Check that the change follows WordPress admin interaction patterns (undo/trash flows, notices behavior, iconography). [SHOULD]
+- Verify built-in icon sets and established admin styling conventions are used where applicable. [SHOULD]
+- For UI changes: verify all interactive elements are keyboard-reachable and operable. [MUST]
+- For UI changes: check semantic markup and ARIA usage for screen reader support. [MUST]
+- For UI changes: verify text and UI elements meet WCAG AA color contrast ratios. [MUST]
+- For UI changes: check for visible and logical focus order on interactive elements. [MUST]
+
+## Architecture and Code Organization
+
+- Flag new classes, namespaces, or files that lack a clear need, or redundant wrapper classes. [MUST]
+- Flag complex commands or subsystems stuffed into unrelated classes; keep concerns separated. [MUST]
+- Flag manual loading of class files that are already covered by Composer autoloading. [MUST]
+- In `src/php/class-plugin.php`, verify Composer autoloading is used; flag `require_once` unless the file cannot be loaded via Composer. [MUST]
+- Flag direct-access guards or runtime checks in individual class files; keep bootstrapping and gating at entry points. [SHOULD]
+- Check that class names, file names, and directory naming are consistent. [SHOULD]
+- Check whether complex logic could be extracted into small, named methods with single responsibilities. [SHOULD]
+- Flag hook registration split into extra methods without clear value; prefer co-locating registration with construction/bootstrap. [SHOULD]
+
+## Dependencies, Assets, and Tooling
+
+- Flag dependencies that are not needed for the actual implementation. [MUST]
+- Flag bundled assets that duplicate platform-provided equivalents. If bundling is unavoidable, note the maintenance obligation (updates, conflicts, compatibility). [MUST]
+- Verify the repository's linting/formatting conventions are followed (e.g., logical CSS properties, consistent formatting). [SHOULD]
+- Check for manual workarounds that could be replaced by framework-native patterns in React/TS. [SHOULD]
+
+## Persistence, Cleanup, and Uninstall
+
+- Verify any new persistent option/setting is accounted for in uninstall/cleanup paths. [MUST]
+- Flag storage of empty/default data that could be safely removed to reduce configuration drift. [MUST]
+- Verify installation/uninstallation behavior is coherent and repeatable (fresh installs vs reinstalls vs data-preserving removals). [MUST]
+- Check whether WordPress API conveniences (e.g., `get_option` default values) could reduce conditional noise and edge cases. [SHOULD]
+- For new DB schema changes or options: verify migration and rollback paths exist. [MUST]
+- For new persistent options: verify an uninstall path removes them. [MUST]
+
+## JavaScript/React State and Async Behavior
+
+- Verify types referenced by exported functions and public APIs are also exported. Flag unexported internal shapes leaked through public APIs. [MUST]
+- Check that the correct state primitive (`useState` vs `useRef`) is used based on rendering and lifecycle needs. Flag refs used as a state substitute. [MUST]
+- Look for concurrent async calls and race conditions; flag state updates that can interleave unpredictably. [MUST]
+- Check whether patterns degrade gracefully on partial failure (e.g., one failed async request should not fail the entire operation when partial results are acceptable). [SHOULD]
+- Flag repeated state updates in loops; prefer a single update at the end. [SHOULD]
+
+## Tests, Compatibility, and Release Hygiene
+
+- Verify changes are compatible with the minimum supported WordPress/PHP versions (especially type-related behavior). [MUST]
+- Flag automation/scripts that rely on brittle timing/waiting; they must be deterministic. [MUST]
+- Verify changelog/readme formatting remains valid (Markdown and `readme.txt` heading/list spacing must not break rendering). [MUST]
+- When the repo ships generated artifacts (Composer autoload/classmaps, built `dist` assets), verify they are regenerated and include newly added files/classes. [MUST]
+- Check whether tests cover the minimum supported platform versions, not just "latest". [SHOULD]
+- Flag unverified bug fixes; require additional diagnostics (logs, error output, minimal reproduction) rather than guessing. [SHOULD]
+- Verify unit tests are included for logic changes, covering edge cases and error paths. [MUST]
+- Verify integration tests are included when cross-cutting concerns (DB, REST, hooks) or subsystem interactions change. [MUST]
+- Verify E2E tests are included for changes affecting critical user flows (snippet creation, execution, editor workflows); check for updated Playwright specs. [MUST]
+- Check for tests asserting compatibility with minimum-supported WordPress/PHP versions when behavior differs by version. [SHOULD]
+
+## Operational Safety and Recovery
+
+- For changes affecting snippet execution: verify recovery paths (safe mode flows) are preserved. Flag changes that turn recoverable failures into unrecoverable lockouts. [MUST]
+- Verify documented safe-mode activation mechanisms are not broken. [MUST]
+- Flag reliance on "deactivate the plugin" as a recovery mechanism; the product must support safe recovery without forcing users to lose state. [MUST]
+- For changes affecting snippet execution: check for a recovery and verification plan. [MUST]
+- Check that proactive validation (syntax checks, duplicate identifier checks) does not produce false positives for legitimate WordPress patterns (e.g., pluggable functions). [SHOULD]
+- Verify example snippet code (docs, help text, UI templates) uses anonymous functions and collision-resistant patterns. [SHOULD]
+
+## Backwards Compatibility and Deprecation
+
+- For any public API change (hooks/filters, public methods, REST endpoints, option names): verify the PR description documents compatibility impact and deprecation plan. [MUST]
+- Verify deprecation shims with clear warnings and compatibility tests are provided when removing or changing public APIs. [MUST]
+- Check for migration guidance and a compatibility matrix listing affected versions and suggested mitigations. [MUST]
+- Verify automated tests exercise deprecated paths to ensure compatibility remains intact until removal. [MUST]
+- Check for a code example showing how to migrate away from the deprecated API. [SHOULD]
+- Check that user-facing deprecation messages are included where appropriate. [SHOULD]
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 000000000..70029ba91
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,88 @@
+name: "(Lint): CSS, JS, PHP"
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, labeled]
+ push:
+ branches:
+ - 'core'
+ - 'pro'
+ paths-ignore:
+ - '**.md'
+ - '**.txt'
+ - '.gitignore'
+ - 'docs/**'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: lint-${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ name: stylelint, eslint, phpcs
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: .node-version
+ cache: 'npm'
+
+ - name: Install npm dependencies
+ run: npm ci
+
+ - name: Set up PHP
+ uses: codesnippetspro/setup-php@v2
+ with:
+ php-version: '8.2'
+
+ - name: Compute dependency hash
+ id: deps-hash
+ run: |
+ set -euo pipefail
+ tmpfile=$(mktemp)
+ if [ -f "src/composer.lock" ]; then
+ cat "src/composer.lock" >> "$tmpfile"
+ fi
+ if [ -s "$tmpfile" ]; then
+ deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8)
+ else
+ deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8)
+ fi
+ echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT"
+
+ - name: Get Composer cache
+ id: composer-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: src/vendor
+ key: ${{ runner.os }}-php-8.2-composer-${{ steps.deps-hash.outputs.deps_hash }}
+ restore-keys: |
+ ${{ runner.os }}-php-8.2-composer-
+
+ - name: Install Composer dependencies
+ if: steps.composer-cache.outputs.cache-hit != 'true'
+ run: composer install -d src --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Save Composer cache
+ if: steps.composer-cache.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v4
+ with:
+ path: src/vendor
+ key: ${{ runner.os }}-php-8.2-composer-${{ steps.deps-hash.outputs.deps_hash }}
+
+ - name: Run Stylelint
+ run: npm run lint:styles
+
+ - name: Run ESLint
+ run: npm run lint:js
+
+ - name: Run PHP CS
+ run: npm run lint:php
diff --git a/.github/workflows/phpunit-test.yml b/.github/workflows/phpunit-test.yml
new file mode 100644
index 000000000..d2789e82c
--- /dev/null
+++ b/.github/workflows/phpunit-test.yml
@@ -0,0 +1,124 @@
+name: PHPUnit Test Runner
+
+on:
+ workflow_call:
+ inputs:
+ php-version:
+ required: false
+ type: string
+ default: '8.2'
+ description: 'PHP version to test against'
+
+jobs:
+ phpunit-test:
+ name: PHPUnit tests (PHP ${{ inputs.php-version }})
+ runs-on: ubuntu-22.04
+ env:
+ WP_CORE_DIR: /tmp/wordpress
+ WP_TESTS_DIR: /tmp/wordpress-tests-lib
+
+ services:
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: wordpress_test
+ ports:
+ - 3306:3306
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+
+ steps:
+ - name: Checkout source code
+ uses: actions/checkout@v4
+
+ - name: Set up PHP
+ uses: codesnippetspro/setup-php@v2
+ with:
+ php-version: ${{ inputs.php-version }}
+
+ - name: Compute dependency hash
+ id: deps-hash
+ run: |
+ set -euo pipefail
+ tmpfile=$(mktemp)
+ if [ -f "src/composer.lock" ]; then
+ cat "src/composer.lock" >> "$tmpfile"
+ fi
+ if [ -f "src/composer.json" ]; then
+ cat "src/composer.json" >> "$tmpfile"
+ fi
+ if [ -s "$tmpfile" ]; then
+ deps_hash=$(shasum -a 1 "$tmpfile" | awk '{print $1}' | cut -c1-8)
+ else
+ deps_hash=$(echo "${GITHUB_SHA:-unknown}" | cut -c1-8)
+ fi
+ echo "deps_hash=$deps_hash" >> "$GITHUB_OUTPUT"
+
+ - name: Get Composer cache
+ id: composer-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: src/vendor
+ key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ steps.deps-hash.outputs.deps_hash }}
+ restore-keys: |
+ ${{ runner.os }}-php-${{ inputs.php-version }}-composer-
+
+ - name: Install Composer dependencies
+ if: steps.composer-cache.outputs.cache-hit != 'true'
+ run: composer install -d src --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Save Composer cache
+ if: steps.composer-cache.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v4
+ with:
+ path: src/vendor
+ key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ steps.deps-hash.outputs.deps_hash }}
+
+ - name: Resolve WordPress version metadata
+ id: wp-meta
+ run: |
+ set -euo pipefail
+ wp_version=$(curl -s http://api.wordpress.org/core/version-check/1.7/ | grep -o '"version":"[^"]*' | head -1 | sed 's/"version":"//')
+ if [ -z "$wp_version" ]; then
+ wp_version="latest-unknown"
+ fi
+ echo "wp_version=$wp_version" >> "$GITHUB_OUTPUT"
+
+ - name: Get WordPress test suite cache
+ id: wp-tests-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ${{ env.WP_CORE_DIR }}
+ ${{ env.WP_TESTS_DIR }}
+ key: ${{ runner.os }}-php-${{ inputs.php-version }}-wp-tests-${{ hashFiles('tests/install-wp-tests.sh') }}-wp-${{ steps.wp-meta.outputs.wp_version }}
+
+ - name: Install WordPress test suite
+ run: |
+ bash scripts/install-wp-tests.sh wordpress_test root root 127.0.0.1:3306 latest true
+
+ - name: Save WordPress test suite cache
+ if: steps.wp-tests-cache.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ ${{ env.WP_CORE_DIR }}
+ ${{ env.WP_TESTS_DIR }}
+ key: ${{ steps.wp-tests-cache.outputs.cache-primary-key }}
+
+ - name: Run PHPUnit tests
+ run: |
+ set -euo pipefail
+ mkdir -p test-results/phpunit
+ cd src
+ vendor/bin/phpunit -c ../phpunit.xml --testdox --log-junit ../test-results/phpunit/phpunit-${{ inputs.php-version }}.xml 2>&1 | tee ../test-results/phpunit/phpunit-${{ inputs.php-version }}.log
+
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: phpunit-test-results-php-${{ inputs.php-version }}
+ path: |
+ src/.phpunit.result.cache
+ test-results/phpunit/
+ if-no-files-found: ignore
+ retention-days: 2
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
new file mode 100644
index 000000000..e6d550d27
--- /dev/null
+++ b/.github/workflows/phpunit.yml
@@ -0,0 +1,275 @@
+name: "(Test): PHPUnit"
+
+on:
+ pull_request:
+ types: [labeled, synchronize, opened, reopened]
+ push:
+ branches:
+ - 'core'
+ - 'pro'
+ - 'core-beta'
+ - 'pro-beta'
+ paths-ignore:
+ - '**.md'
+ - '**.txt'
+ - '.gitignore'
+ - 'docs/**'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pull-requests: read
+ actions: read
+
+concurrency:
+ group: phpunit-${{ github.event_name }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+ phpunit:
+ if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-tests')
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ uses: ./.github/workflows/phpunit-test.yml
+ with:
+ php-version: ${{ matrix.php-version }}
+
+ test-result:
+ needs: [phpunit]
+ if: always() && needs.phpunit.result != 'skipped'
+ runs-on: ubuntu-22.04
+ name: PHPUnit - Test Results Summary
+ permissions:
+ pull-requests: write
+ issues: write
+ steps:
+ - name: Test status summary
+ run: |
+ echo "PHPUnit matrix result: ${{ needs.phpunit.result }}"
+
+ - name: Delete previous PR failure comment on success
+ if: github.event_name == 'pull_request' && needs.phpunit.result == 'success'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const marker = '';
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ per_page: 100,
+ });
+
+ const existing = comments.filter(c => typeof c.body === 'string' && c.body.startsWith(marker));
+
+ for (const comment of existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: comment.id,
+ });
+ }
+
+ - name: Download PHPUnit artifacts
+ if: github.event_name == 'pull_request' && needs.phpunit.result == 'failure'
+ uses: actions/download-artifact@v5
+ with:
+ pattern: phpunit-test-results-php-*
+ path: phpunit-artifacts
+ merge-multiple: true
+
+ - name: Build distinct error summary
+ if: github.event_name == 'pull_request' && needs.phpunit.result == 'failure'
+ run: |
+ set -euo pipefail
+ python3 - <<'PY'
+ import glob
+ import os
+ import re
+ import xml.etree.ElementTree as ET
+ from collections import defaultdict
+
+ artifacts_dir = 'phpunit-artifacts'
+
+ def version_from_path(path: str) -> str:
+ base = os.path.basename(path)
+ m = re.search(r'phpunit-([0-9]+\.[0-9]+)\.xml$', base)
+ if m:
+ return m.group(1)
+ m = re.search(r'phpunit-([0-9]+\.[0-9]+)\.log$', base)
+ if m:
+ return m.group(1)
+ return 'unknown'
+
+ def add_entry(grouped, key, version, message):
+ if not message.strip():
+ return
+ grouped[key]['versions'].add(version)
+ # Keep the first representative message we see for this key.
+ if not grouped[key]['message']:
+ grouped[key]['message'] = message.strip()
+
+ grouped = defaultdict(lambda: {'versions': set(), 'message': ''})
+ versions_seen = set()
+
+ # Prefer JUnit XML when present.
+ for xml_path in sorted(glob.glob(os.path.join(artifacts_dir, '**', '*.xml'), recursive=True)):
+ version = version_from_path(xml_path)
+ versions_seen.add(version)
+ try:
+ root = ET.parse(xml_path).getroot()
+ except Exception:
+ continue
+
+ for testcase in root.iter('testcase'):
+ for tag in ('error', 'failure'):
+ for node in testcase.findall(tag):
+ etype = (node.attrib.get('type') or tag).strip()
+ msg = (node.attrib.get('message') or '').strip()
+ details = (node.text or '').strip()
+ combined = f"{etype}: {msg}".strip(': ')
+ if details:
+ combined = combined + "\n" + details
+
+ testcase_id = (
+ (testcase.attrib.get('classname') or '').strip() +
+ '::' +
+ (testcase.attrib.get('name') or '').strip()
+ ).strip(':')
+
+ extracted = ''
+ if not msg and details:
+ for line in details.splitlines():
+ line = line.strip()
+ if line.startswith('CI demo:'):
+ extracted = line
+ break
+ if not extracted:
+ extracted = details.splitlines()[0].strip()
+
+ # Dedupe key: prefer explicit message; otherwise use extracted details + testcase id.
+ key_parts = [etype]
+ if msg:
+ key_parts.append(msg)
+ elif extracted:
+ key_parts.append(extracted)
+ if testcase_id:
+ key_parts.append(testcase_id)
+ key = "\n".join([p for p in key_parts if p]).strip() or (combined.splitlines()[0] if combined else 'unknown')
+ add_entry(grouped, key, version, combined)
+
+ # Fallback: scan logs for fatals if XML missing.
+ fatal_re = re.compile(r'^(PHP\s+Fatal\s+error:.*|Fatal\s+error:.*)$', re.MULTILINE)
+ for log_path in sorted(glob.glob(os.path.join(artifacts_dir, '**', '*.log'), recursive=True)):
+ version = version_from_path(log_path)
+ versions_seen.add(version)
+ try:
+ log = open(log_path, 'r', encoding='utf-8', errors='replace').read()
+ except Exception:
+ continue
+ m = fatal_re.search(log)
+ if m:
+ msg = m.group(1).strip()
+ key = 'Fatal error\n' + msg
+ add_entry(grouped, key, version, msg)
+
+ versions = sorted(v for v in versions_seen if v != 'unknown')
+
+ def versions_label(affected):
+ affected = sorted(v for v in affected if v != 'unknown')
+ if versions and affected == versions:
+ return 'all'
+ return ', '.join(affected) if affected else 'unknown'
+
+ items = sorted(grouped.items(), key=lambda kv: (-len(kv[1]['versions']), kv[0]))
+ blocks = []
+ for idx, (key, info) in enumerate(items):
+ affected = versions_label(info['versions'])
+ message = info['message']
+ block = (
+ "-----\n"
+ f"Affected PHP version: `{affected}`\n"
+ "```php\n"
+ f"{message}\n"
+ "```"
+ )
+ if idx == len(items) - 1:
+ block += "\n-----"
+ blocks.append(block)
+
+ if blocks:
+ details = "\n\n".join(blocks)
+ else:
+ details = "No PHPUnit error details could be parsed from artifacts."
+
+ md = "\n".join([
+ "",
+ "See all PHPUnit errors (click to expand) ",
+ "",
+ details,
+ "",
+ " ",
+ "",
+ ])
+
+ with open('phpunit-errors.md', 'w', encoding='utf-8') as f:
+ f.write(md)
+ PY
+
+ - name: Post PR comment on failure
+ if: github.event_name == 'pull_request' && needs.phpunit.result == 'failure'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const marker = '';
+
+ const fs = require('fs');
+ let details = '';
+ try {
+ details = fs.readFileSync('phpunit-errors.md', 'utf8').trim();
+ } catch (e) {
+ details = '';
+ }
+
+ const body = [
+ marker,
+ '## PHPUnit Test Failure',
+ '',
+ `One or more PHP version targets failed in [this workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`,
+ '',
+ details || '_No parsed error details found._',
+ '',
+ 'Please review the failing jobs and fix the issues before merging.',
+ ].join('\n');
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ per_page: 100,
+ });
+
+ const existing = comments.filter(c => typeof c.body === 'string' && c.body.startsWith(marker));
+
+ for (const comment of existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: comment.id,
+ });
+ }
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body,
+ });
+
+ - name: Check overall status
+ if: needs.phpunit.result != 'success'
+ run: exit 1
+
diff --git a/.github/workflows/playwright-test.yml b/.github/workflows/playwright-test.yml
index 59f5b4a91..a59a3d8f8 100644
--- a/.github/workflows/playwright-test.yml
+++ b/.github/workflows/playwright-test.yml
@@ -45,13 +45,20 @@ jobs:
WORDPRESS_DEBUG: 1
WORDPRESS_CONFIG_EXTRA: |
define( 'FS_METHOD', 'direct' );
+ define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
- define( 'WP_DEBUG_DISPLAY', false );
+ define( 'WP_DEBUG_DISPLAY', true );
+ define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true );
define( 'SCRIPT_DEBUG', true );
define( 'WP_ENVIRONMENT_TYPE', 'local' );
+ @ini_set( 'display_errors', '1' );
+ @ini_set( 'display_startup_errors', '1' );
+ @ini_set( 'log_errors', '1' );
+ @ini_set( 'error_reporting', (string) E_ALL );
ports:
- 8888:80
steps:
+
- name: Checkout source code
uses: actions/checkout@v4
@@ -88,25 +95,29 @@ jobs:
uses: actions/cache/restore@v4
with:
path: |
- node_modules
src/vendor
- key: ${{ runner.os }}-${{ inputs.test-mode }}-deps-${{ steps.deps-hash.outputs.deps_hash }}-${{ github.run_id }}-${{ github.job }}
+ node_modules
+ key: ${{ runner.os }}-${{ inputs.test-mode }}-deps-${{ steps.deps-hash.outputs.deps_hash }}
restore-keys: |
${{ runner.os }}-${{ inputs.test-mode }}-deps-${{ steps.deps-hash.outputs.deps_hash }}-
+ ${{ runner.os }}-${{ inputs.test-mode }}-deps-
- name: Install workflow dependencies
- if: steps.deps-cache.outputs.cache-matched-key == ''
- run: npm run prepare-environment:ci && npm run bundle
+ if: steps.deps-cache.outputs.cache-hit != 'true'
+ run: npm run prepare-environment:ci
+
+ - name: Build plugin assets
+ run: npm run bundle
- name: Save vendor and node_modules cache
- if: steps.deps-cache.outputs.cache-matched-key == ''
+ if: steps.deps-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
src/vendor
node_modules
- key: ${{ steps.deps-cache.outputs.cache-primary-key }}
-
+ key: ${{ steps.deps-cache.outputs.cache-primary-key }}-${{ github.run_id }}-${{ github.job }}
+
- name: Wait for WordPress to be reachable
run: |
for i in $(seq 1 60); do
@@ -114,7 +125,7 @@ jobs:
echo "WordPress is reachable."
exit 0
fi
- echo "Waiting for WordPress... ($i/60)"
+ echo "Waiting for WordPress… ($i/60)"
sleep 2
done
@@ -229,8 +240,32 @@ jobs:
WP_E2E_WPCLI_URL: http://localhost:8888
WP_E2E_WP_CONTAINER: ${{ job.services.wordpress.id }}
WP_E2E_MULTISITE_MODE: ${{ inputs.multisite }}
- run: npm run test:playwright -- --project=${{ inputs.project-name }}
-
+ run: |
+ set -euo pipefail
+ mkdir -p test-results/ci
+ suffix="${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }}"
+ : > "test-results/ci/playwright-${suffix}.log"
+ npm run test:playwright -- --project=${{ inputs.project-name }} 2>&1 | tee -a "test-results/ci/playwright-${suffix}.log"
+
+ - name: Normalize Playwright report filenames
+ if: always()
+ run: |
+ set -euo pipefail
+ mkdir -p test-results/ci
+ suffix="${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }}"
+
+ if [ -f test-results/results.xml ]; then
+ mv test-results/results.xml "test-results/ci/results-${suffix}.xml"
+ elif [ -f tests/playwright/test-results/results.xml ]; then
+ mv tests/playwright/test-results/results.xml "test-results/ci/results-${suffix}.xml"
+ fi
+
+ if [ -f test-results/results.json ]; then
+ mv test-results/results.json "test-results/ci/results-${suffix}.json"
+ elif [ -f tests/playwright/test-results/results.json ]; then
+ mv tests/playwright/test-results/results.json "test-results/ci/results-${suffix}.json"
+ fi
+
- name: Print WordPress logs on failure
if: failure()
run: |
@@ -241,7 +276,7 @@ jobs:
- uses: actions/upload-artifact@v4
if: always()
with:
- name: playwright-test-results-${{ inputs.test-mode }}
+ name: playwright-test-results-${{ inputs.test-mode }}-${{ inputs.project-name }}${{ inputs.multisite && '-multisite' || '' }}
path: test-results/
if-no-files-found: ignore
retention-days: 2
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 51f39bd4d..cf36bdff0 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -7,6 +7,8 @@ on:
branches:
- 'core'
- 'pro'
+ - 'core-beta'
+ - 'pro-beta'
paths-ignore:
- '**.md'
- '**.txt'
@@ -15,8 +17,8 @@ on:
workflow_dispatch:
permissions:
- contents: write
- pull-requests: write
+ contents: read
+ pull-requests: read
actions: read
concurrency:
@@ -45,12 +47,289 @@ jobs:
if: always() && (needs.playwright-default.result != 'skipped' || needs.playwright-file-based-execution.result != 'skipped')
runs-on: ubuntu-22.04
name: Playwright - Test Results Summary
+ permissions:
+ pull-requests: write
+ issues: write
steps:
- name: Test status summary
run: |
echo "Default Mode: ${{ needs.playwright-default.result }}"
echo "File-based Execution: ${{ needs.playwright-file-based-execution.result }}"
+ - name: Delete previous PR failure comment on success
+ if: github.event_name == 'pull_request' && needs.playwright-default.result == 'success' && needs.playwright-file-based-execution.result == 'success'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const marker = '';
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ per_page: 100,
+ });
+
+ const existing = comments.filter(c => typeof c.body === 'string' && c.body.startsWith(marker));
+
+ for (const comment of existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: comment.id,
+ });
+ }
+
+ - name: Download Playwright artifacts
+ if: github.event_name == 'pull_request' && (needs.playwright-default.result == 'failure' || needs.playwright-file-based-execution.result == 'failure')
+ uses: actions/download-artifact@v5
+ with:
+ pattern: playwright-test-results-*
+ path: playwright-artifacts
+ merge-multiple: true
+
+ - name: Build distinct error summary
+ if: github.event_name == 'pull_request' && (needs.playwright-default.result == 'failure' || needs.playwright-file-based-execution.result == 'failure')
+ run: |
+ set -euo pipefail
+ python3 - <<'PY'
+ import glob
+ import json
+ import os
+ import re
+ import xml.etree.ElementTree as ET
+ from collections import defaultdict
+
+ artifacts_dir = 'playwright-artifacts'
+
+ def target_from_path(path: str) -> str:
+ base = os.path.basename(path)
+ m = re.search(r'(?:results|playwright)-(.+)\.(?:xml|json|log)$', base)
+ if m:
+ return m.group(1)
+ return 'unknown'
+
+ def add_entry(grouped, key, target, message):
+ if not message.strip():
+ return
+ grouped[key]['targets'].add(target)
+ if not grouped[key]['message']:
+ grouped[key]['message'] = message.strip()
+
+ grouped = defaultdict(lambda: {'targets': set(), 'message': ''})
+ targets_seen = set()
+
+ def safe_str(val) -> str:
+ if val is None:
+ return ''
+ if isinstance(val, str):
+ return val
+ try:
+ return str(val)
+ except Exception:
+ return ''
+
+ def walk_suite(suite, title_path):
+ # Playwright JSON reporter structure: suites -> suites/specs.
+ for child in suite.get('suites', []) or []:
+ walk_suite(child, title_path + [safe_str(child.get('title'))])
+
+ for spec in suite.get('specs', []) or []:
+ spec_title = safe_str(spec.get('title'))
+ for test in spec.get('tests', []) or []:
+ test_title = safe_str(test.get('title'))
+ for result in test.get('results', []) or []:
+ status = safe_str(result.get('status'))
+ err = result.get('error') or {}
+ err_msg = safe_str(err.get('message'))
+ err_stack = safe_str(err.get('stack'))
+
+ if status != 'failed' and not err_msg and not err_stack:
+ continue
+
+ name = ' > '.join([p for p in (title_path + [spec_title, test_title]) if p])
+ combined = name
+ if err_msg:
+ combined += "\n" + err_msg
+ if err_stack:
+ combined += "\n" + err_stack
+ yield combined.strip()
+
+ # 1) Parse JSON reporter output (most reliable for Playwright failures).
+ for json_path in sorted(glob.glob(os.path.join(artifacts_dir, '**', '*.json'), recursive=True)):
+ target = target_from_path(json_path)
+ targets_seen.add(target)
+ try:
+ data = json.loads(open(json_path, 'r', encoding='utf-8', errors='replace').read())
+ except Exception:
+ continue
+
+ suites = data.get('suites', []) if isinstance(data, dict) else []
+ if not suites:
+ continue
+
+ for suite in suites:
+ for combined in walk_suite(suite, [safe_str(suite.get('title'))]):
+ # Dedupe key: first line of error message + test name line.
+ first = combined.splitlines()[0].strip() if combined else 'unknown'
+ key = first
+ add_entry(grouped, key, target, combined)
+
+ # 2) Parse JUnit XML (secondary; may be missing on some runner failures).
+ for xml_path in sorted(glob.glob(os.path.join(artifacts_dir, '**', '*.xml'), recursive=True)):
+ target = target_from_path(xml_path)
+ targets_seen.add(target)
+ try:
+ root = ET.parse(xml_path).getroot()
+ except Exception:
+ continue
+
+ for testcase in root.iter('testcase'):
+ for tag in ('error', 'failure'):
+ for node in testcase.findall(tag):
+ etype = (node.attrib.get('type') or tag).strip()
+ msg = (node.attrib.get('message') or '').strip()
+ details = (node.text or '').strip()
+
+ combined = f"{etype}: {msg}".strip(': ')
+ if details:
+ combined = combined + "\n" + details
+
+ testcase_id = (
+ (testcase.attrib.get('classname') or '').strip() +
+ '::' +
+ (testcase.attrib.get('name') or '').strip()
+ ).strip(':')
+
+ extracted = ''
+ if not msg and details:
+ extracted = details.splitlines()[0].strip()
+
+ key_parts = [etype]
+ if msg:
+ key_parts.append(msg)
+ elif extracted:
+ key_parts.append(extracted)
+ if testcase_id:
+ key_parts.append(testcase_id)
+ key = "\n".join([p for p in key_parts if p]).strip() or (combined.splitlines()[0] if combined else 'unknown')
+
+ add_entry(grouped, key, target, combined)
+
+ # 3) Fallback: scan logs if JSON/XML were not usable.
+ if not grouped:
+ # Try to extract something meaningful from Playwright logs.
+ error_line_re = re.compile(r'^(Error:.*|\s+at\s+.*|expect\(.*\).*)$', re.MULTILINE)
+ for log_path in sorted(glob.glob(os.path.join(artifacts_dir, '**', '*.log'), recursive=True)):
+ target = target_from_path(log_path)
+ targets_seen.add(target)
+ try:
+ text = open(log_path, 'r', encoding='utf-8', errors='replace').read()
+ except Exception:
+ continue
+
+ # Prefer a compact tail if we can't find explicit error lines.
+ lines = [ln.rstrip() for ln in text.splitlines()]
+ tail = "\n".join(lines[-80:])
+
+ matches = error_line_re.findall(text)
+ extracted = "\n".join(matches[:80]).strip() if matches else tail.strip()
+ if extracted:
+ key = extracted.splitlines()[0][:200]
+ add_entry(grouped, key, target, extracted)
+
+ targets = sorted(t for t in targets_seen if t != 'unknown')
+
+ def targets_label(affected):
+ affected = sorted(t for t in affected if t != 'unknown')
+ if targets and affected == targets:
+ return 'all'
+ return ', '.join(affected) if affected else 'unknown'
+
+ items = sorted(grouped.items(), key=lambda kv: (-len(kv[1]['targets']), kv[0]))
+ blocks = []
+ for idx, (key, info) in enumerate(items):
+ affected = targets_label(info['targets'])
+ message = info['message']
+ block = (
+ "-----\n"
+ f"Affected Playwright test: `{affected}`\n"
+ "```text\n"
+ f"{message}\n"
+ "```"
+ )
+ if idx == len(items) - 1:
+ block += "\n-----"
+ blocks.append(block)
+
+ if blocks:
+ details_md = "\n\n".join(blocks)
+ else:
+ details_md = "No Playwright error details could be parsed from artifacts."
+
+ md = "\n".join([
+ "",
+ "See all Playwright errors (click to expand) ",
+ "",
+ details_md,
+ "",
+ " ",
+ "",
+ ])
+
+ with open('playwright-errors.md', 'w', encoding='utf-8') as f:
+ f.write(md)
+ PY
+
+ - name: Post PR comment on failure
+ if: github.event_name == 'pull_request' && (needs.playwright-default.result == 'failure' || needs.playwright-file-based-execution.result == 'failure')
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const marker = '';
+
+ const fs = require('fs');
+ let details = '';
+ try {
+ details = fs.readFileSync('playwright-errors.md', 'utf8').trim();
+ } catch (e) {
+ details = '';
+ }
+
+ const body = [
+ marker,
+ '## Playwright Test Failure',
+ '',
+ `One or more Playwright targets failed in [this workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`,
+ '',
+ details || '_No parsed error details found._',
+ '',
+ 'Please review the failing jobs and fix the issues before merging.',
+ ].join('\n');
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ per_page: 100,
+ });
+
+ const existing = comments.filter(c => typeof c.body === 'string' && c.body.startsWith(marker));
+ for (const comment of existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: comment.id,
+ });
+ }
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body,
+ });
+
- name: Check overall status
if: ${{ (needs.playwright-default.result != 'success' && needs.playwright-default.result != 'skipped') || (needs.playwright-file-based-execution.result != 'success' && needs.playwright-file-based-execution.result != 'skipped') }}
run: exit 1
diff --git a/.gitignore b/.gitignore
index 1c3b03cb0..453a38177 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,12 @@ node_modules/
npm-debug.log
.sass-cache/
+# PHPUnit
+.phpunit.result.cache
+.wp-core
+.wp-tests-lib
+/coverage/
+
# Playwright
playwright-report/
test-results/
@@ -19,9 +25,18 @@ tests/e2e/.auth/
auth.json
# Local files (ideally, should be in a global .gitignore)
+/.env
.idea/
Thumbs.db
.DS_Store
# storyman
.story
+.tmp
+tmp
+.continue
+.codex
+# Local environment overrides (license keys, secrets)
+/.env
+.superpowers/
+docs/superpowers/
diff --git a/.storyman.json b/.storyman.json
deleted file mode 100644
index b246f1786..000000000
--- a/.storyman.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "jiraUrl": "https://codesnippets.atlassian.net/",
- "defaultProject": "PD"
-}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e2bcb26b1..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-# Declare project language.
-# @link http://about.travis-ci.org/docs/user/languages/php/
-language: php
-
-# Declare versions of PHP to use. Use one decimal max.
-php:
- - '7.2'
- - '7.3'
- - '7.4'
- - '8.0'
- - nightly
-
-services:
- - mysql
-
-env:
- global:
- - WP_DEVELOP_DIR=/tmp/wordpress/
- matrix:
- # Trunk
- - WP_VERSION=master WP_MULTISITE=0
- - WP_VERSION=master WP_MULTISITE=1
-
- # Latest stable version
- - WP_VERSION=stable WP_MULTISITE=0
- - WP_VERSION=stable WP_MULTISITE=1
-
-
-# Prepare the system by installing prerequisites and dependencies.
-# Failures in this section will result in build status 'errored'.
-before_install:
- - nvm install node
- - composer self-update
-
-# Prepare your build for testing.
-# Failures in this section will result in build status 'errored'.
-before_script:
- - pwd
-
- # Build plugin
- - composer install
- - npm install
- - npm run build
- - npm run package
-
- # set up WP install
- - bash tests/install.sh $WP_VERSION wordpress_test root
-
-# Run test script commands
-# Default is specific to project language.
-# All commands must exit with code 0 on success. Anything else is considered failure.
-script:
-
- # Search for PHP syntax errors.
- - find . -maxdepth 1 \( -name '*.php' \) -exec php -lf {} \;
- - find src/php/ \( -name '*.php' \) -exec php -lf {} \;
-
- # Run linters
- - npm run lint
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4dc3ada52..c9db77a9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,47 +1,71 @@
# Changelog
-## [3.9.6] (2026-04-28)
+## [3.10.0] (UPCOMING)
-### Changed
-* tweak: improve snippets rest api
+### Added
+* New admin interface for managing snippets, with a cleaner layout, faster interactions, and a more consistent experience across plugin screens.
+* Card view for browsing snippets, with a view switcher on the snippets table and Community Cloud.
+* Snippet preview modal for viewing snippet code from the snippets table without opening the editor.
+* Automatic hiding of unrelated admin notices from other plugins on Code Snippets screens.
+* Admin bar snippet drawer, based on the Deckerweb Snippets workflow, for quick access to snippets and Safe Mode from the WordPress admin bar.
+* Snippet locking to help prevent accidental edits or deletion of important snippets. Props to https://github.com/mgiannopoulos24.
+* Improved screen options on the main snippets table, including controls for visible columns and truncating long snippet names or descriptions.
+* Bulk actions and bulk code download support in the redesigned snippets table.
+* Featured snippets and improved browsing in Community Cloud.
+* WordPress modern theme admin styling compatibility.
+* Clearer accessibility labels, headings, tab markup, table checkboxes, sort buttons, copy buttons, and drag-and-drop upload controls.
+
+### Changed
+* Redesigned the main snippets table with improved search, filtering, sorting, pagination, row actions, and bulk selection.
+* Improved the snippet import and migration experience, including clearer file upload handling and third-party plugin migration flows.
+* Improved snippet error handling so activation failures, validation errors, and stack traces are easier to understand.
+* Improved Community Cloud search and filtering, including server-side filters, better result loading, and clearer empty states.
+* Updated the welcome screen, toolbar, import screen, and cloud screens to match the new admin experience.
+* Updated internal plugin architecture to a cleaner PSR-4 structure for better long-term maintainability.
+* Improved accessibility across the snippets table, import screen, migration flow, Community Cloud, welcome screen, toolbar, dialogs, tooltips, and code editor.
+* Improved colour contrast and reduced-motion support across admin screens.
+
+### Fixed
+* Fixed REST API server error responses on missing snippets.
+* Fixed redundant frontend logic, improving overall performance.
+* Fixed Community Cloud search results and pagination to respect WordPress screen options.
+* Fixed snippet saving and activation feedback to improve validation and runtime error display.
+* Fixed downloaded Community Cloud snippets appearing as not downloaded after a page reload.
+* Fixed network snippet lookups using the wrong database table on multisite.
+* Fixed the inactive snippets count including trashed snippets.
+* Fixed featured Community Cloud snippets failing to load with some cloud API responses.
-### Removed
-* remove redundant comments
+## [3.9.6] (2026-04-28)
### Fixed
-* site admin cannot toggle shared network snippets status
+* Improved permissions handling with snippets REST API.
+* Site admin cannot toggle shared network snippets status.
## [3.9.5] (2026-02-05)
-### Added
-* Confirmed WordPress 6.9 compatability
-
### Fixed
-* Improved nonce handling for cloud snippet download and update actions to for enhanced security
+* Improved security when handling actions for downloading and updating cloud snippets.
## [3.9.4] (2026-01-14)
### Added
-* New import functionality to migrate snippets from file uploads with drag-and-drop interface
-* Support for importing snippets from other popular plugins (Header Footer Code Manager, Insert Headers and Footers, Insert PHP Code Snippet)
-* Enhanced file based execution support with improved multisite mode compatibility
-
-### Changed
-* Updated links to more recent documentation pages
+* New import functionality to migrate snippets from file uploads with drag-and-drop interface.
+* Support for importing snippets from other popular plugins (Header Footer Code Manager, Insert Headers and Footers, Insert PHP Code Snippet).
+* Enhanced file based execution support with improved multisite mode compatibility.
### Fixed
-* Fixed multisite capability checks in Plugin class
-* Fixed snippet execution logic for multisite support by centralizing trashed snippet handling
-* Fixed multisite snippet handling to ensure local snippets use correct table and filter out trashed snippets
+* Fixed multisite capability checks in Plugin class.
+* Fixed snippet execution logic for multisite support by centralizing trashed snippet handling.
+* Fixed multisite snippet handling to ensure local snippets use correct table and filter out trashed snippets.
## [3.9.3] (2025-12-03)
### Added
-* end-to-end tests to verify the toggle visual state in the snippets list page, improving UI verification and test reliability
+* End-to-end tests to verify the toggle visual state in the snippets list page, improving UI verification and test reliability.
### Fixed
-* Fix missing import of common/direction in src/css/manage.scss to restore correct styling and direction-aware layout
-* Fix toggle activation check to ensure the correct transformation value is used when detecting active/inactive state
+* Restored missing styles styling and direction-aware layout from Manage menu.
+* Ensure correct transformation value is used when detecting state of activation toggle.
## [3.9.2] (2025-11-17)
@@ -226,10 +250,10 @@
### Changed
* Updated CSS to use latest Sass features.
-* Moved theme selector to just above editor preview on settings page (thanks to [brandonjp]). ([#206](https://github.com/codesnippetspro/code-snippets/issues/206))
+* Moved theme selector to just above editor preview on settings page (thanks to [brandonjp]). ([#206](https://github.com/codesnippetspro/code-snippets/issues/206))
* `[code_snippet]` shortcodes can now be nested within each other. ([#198](https://github.com/codesnippetspro/code-snippets/issues/198))
-### Fixed
+### Fixed
* Save buttons above editor did not follow usual validation process in Pro. (PRO) ([#197](https://github.com/codesnippetspro/code-snippets/issues/197))
* Minor inconsistencies in consistent UI elements between Core and Pro.
* Tags input not allowing input. ([#211](https://github.com/codesnippetspro/code-snippets/issues/211))
@@ -253,13 +277,13 @@
* Action hook `code_snippets/admin/manage` now includes the currently viewed type.
### Fixed
-* Memory issue from checking aggregate posts while loading front-end syntax highlighter.
+* Memory issue from checking aggregate posts while loading front-end syntax highlighter.
* Translation functions being called too early on upgrade, resulting in localisation loading errors.
* Bug preventing the 'share on network' status of network snippets from correctly updating.
* Incorrect logic controlling when to display 'Save Changes' or 'Save Changes and Activate' buttons.
* Old notices persisting when switching between editing and creating snippets.
-## 3.6.5.1 (2024-05-24)
+## [3.6.5.1] (2024-05-24)
* Redeployment of [v3.6.5](#365-2024-05-24) to overcome issue with initial build.
@@ -387,11 +411,11 @@
### Changed
* Better compatibility with modern versions of PHP (7.0+).
-* Converted Edit/Add New Snippet page to use React.
- * Converted action buttons to asynchronously use REST API endpoints through AJAX.
- * Load page components dynamically through React.
- * Added action notice queue system.
- * Replaced native alert dialog with proper React modal.
+* Converted Edit/Add New Snippet page to use React:
+ - Converted action buttons to asynchronously use REST API endpoints through AJAX.
+ - Load page components dynamically through React.
+ - Added action notice queue system
+ - Replaced native alert dialog with proper React modal.
* Catch snippet execution errors to prevent site from crashing.
* Display recent snippet errors in admin dashboard instead.
* Updated editor block to use new REST API endpoints. (PRO)
@@ -575,9 +599,9 @@
### Added
* Added translations:
- * Spanish by [Ibidem Group](https://www.ibidemgroup.com)
- * Urdu by [Samuel Badree](https://mobilemall.pk/)
- * Greek by [Toni Bishop from Jrop](https://www.jrop.com/)
+ - Spanish by [Ibidem Group](https://www.ibidemgroup.com)
+ - Urdu by [Samuel Badree](https://mobilemall.pk/)
+ - Greek by [Toni Bishop from Jrop](https://www.jrop.com/)
* Support for `:class` syntax to the code validator.
* PHP8 support to the code linter.
* Color picker feature to the code editor.
@@ -1318,12 +1342,12 @@
### Changed
* Updated CodeMirror to version 2.33.
-* Updated the 'Manage Snippets' page to use the WP_List_Table class.
- * Added 'Screen Options' tab to 'Manage Snippets' page.
- * Added search capability to 'Manage Snippets' page.
- * Added views to easily filter activated, deactivated and recently activated snippets.
- * Added ID column to 'Manage Snippets' page.
- * Added sortable name and ID column on 'Manage Snippets' page ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-sort-by-snippet-name))
+* Updated the 'Manage Snippets' page to use the WP_List_Table class:
+ - Added 'Screen Options' tab to 'Manage Snippets' page.
+ - Added search capability to 'Manage Snippets' page.
+ - Added views to easily filter activated, deactivated and recently activated snippets.
+ - Added ID column to 'Manage Snippets' page.
+ - Added sortable name and ID column on 'Manage Snippets' page ([#](https://wordpress.org/support/topic/plugin-code-snippets-suggestion-sort-by-snippet-name))
* Improved API.
* Lengthened snippet name field to 64 characters. ([#](https://wordpress.org/support/topic/plugin-code-snippets-snippet-title-limited-to-36-characters))
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0b543a3b3..4949dde96 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -69,6 +69,44 @@ command:
npm run watch
```
+## Pre-commit hooks (automatic linting & autofix) 🔧
+
+We use a native Git hook and lint-staged to run linters only on the files being committed. The hook will:
+
+- Run the appropriate autofix for the changed files (PHP, JS/TS, CSS/SCSS).
+- Automatically stage any files that were fixed so the fixes are included in the same commit.
+- Block the commit only if non-fixable linter errors remain.
+
+Files → actions (configured in this repository):
+
+- `*.php` → `npm run lint:php:fix` (phpcbf)
+- `*.{js,ts,jsx,tsx}` → `npm run lint:js:fix` (ESLint --fix)
+- `*.{css,scss}` → `npm run lint:styles:fix` (Stylelint --fix)
+
+Setup
+
+1. Install node deps. The `prepare` script configures Git to use this repository's hooks:
+
+```shell
+npm install
+```
+
+2. If you already have the repo checked out, activate the Git hooks (run once):
+
+```shell
+npm run prepare
+```
+
+Usage notes
+
+- To bypass hooks in an emergency: `git commit --no-verify` (not recommended).
+- To run the same checks locally on staged files: `npx lint-staged`.
+- If fixes were applied by the hook they will be included automatically in the commit; the commit is only blocked when
+ a non-autofixable problem remains.
+
+If you need to change which linters run for a filetype, see `package.json` -> `lint-staged`.
+
+
## Managing Composer dependencies
Code Snippets uses the [Imposter plugin](https://github.com/TypistTech/imposter) to namespace-prefix all vendor
diff --git a/claude.md b/claude.md
new file mode 100644
index 000000000..fa4318081
--- /dev/null
+++ b/claude.md
@@ -0,0 +1,7 @@
+# Code Snippets — Claude Instructions
+
+Project standards live in `AGENTS.md` at the repository root. Read that file in full before performing any task.
+
+This file exists to establish the Claude-specific configuration location. No additional Claude-specific
+directives are defined at this time — follow `AGENTS.md` for all coding standards, architecture patterns,
+security requirements, and workflow conventions.
diff --git a/config/modules/postcss-color-hsl.d.ts b/config/modules/postcss-color-hsl.d.ts
deleted file mode 100644
index fc76708c8..000000000
--- a/config/modules/postcss-color-hsl.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-declare module 'postcss-color-hsl' {
- import type { Plugin } from 'postcss'
- export default function (): Plugin
-}
diff --git a/tests/playwright/playwright.config.ts b/config/playwright/playwright.config.ts
similarity index 52%
rename from tests/playwright/playwright.config.ts
rename to config/playwright/playwright.config.ts
index 10c2e67f5..68b6ac87d 100644
--- a/tests/playwright/playwright.config.ts
+++ b/config/playwright/playwright.config.ts
@@ -2,27 +2,44 @@
import { join } from 'path'
import { defineConfig, devices } from '@playwright/test'
-const RETRIES = 2
const WORKERS = 1
+const CI_RETRIES = 2
+const LOCAL_RETRIES = 1
+
+const TEST_TIMEOUT_SECONDS = 60
+const ASSERT_TIMEOUT_SECONDS = 30
+
+const MILLISECONDS_IN_SECOND = 1000
+
+const baseTestsDir = join(__dirname, '..', '..', 'tests')
+const storageState = join(baseTestsDir, 'e2e/.auth/user.json')
+
/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
- testDir: '../e2e',
+ testDir: join(baseTestsDir, 'e2e'),
snapshotPathTemplate: '{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-{platform}{ext}',
fullyParallel: true,
forbidOnly: !!process.env.CI,
- retries: process.env.CI ? RETRIES : 0,
- workers: process.env.CI ? WORKERS : undefined,
- reporter: [
- ['html'],
- ['json', { outputFile: 'test-results/results.json' }],
- ['junit', { outputFile: 'test-results/results.xml' }]
- ],
+ retries: process.env.CI ? CI_RETRIES : LOCAL_RETRIES,
+ workers: WORKERS,
+ reporter: process.env.CI
+ ? [
+ ['line'],
+ ['html'],
+ ['json', { outputFile: join(process.cwd(), 'test-results', 'results.json') }],
+ ['junit', { outputFile: join(process.cwd(), 'test-results', 'results.xml') }]
+ ]
+ : [
+ ['html'],
+ ['json', { outputFile: join(process.cwd(), 'test-results', 'results.json') }],
+ ['junit', { outputFile: join(process.cwd(), 'test-results', 'results.xml') }]
+ ],
use: {
baseURL: 'http://localhost:8888',
- trace: 'on-first-retry',
+ trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure'
},
@@ -32,13 +49,12 @@ export default defineConfig({
name: 'setup',
testMatch: /auth\.setup\.ts/
},
-
{
name: 'flat-files-setup',
testMatch: /flat-files\.setup\.ts/,
use: {
...devices['Desktop Chrome'],
- storageState: join(__dirname, '../e2e/.auth/user.json')
+ storageState
},
dependencies: ['setup']
},
@@ -47,7 +63,7 @@ export default defineConfig({
name: 'chromium-db-snippets',
use: {
...devices['Desktop Chrome'],
- storageState: join(__dirname, '../e2e/.auth/user.json')
+ storageState
},
dependencies: ['setup'],
testIgnore: /.*\.setup\.ts/
@@ -57,17 +73,17 @@ export default defineConfig({
name: 'chromium-file-based-snippets',
use: {
...devices['Desktop Chrome'],
- storageState: join(__dirname, '../e2e/.auth/user.json')
+ storageState
},
dependencies: ['setup', 'flat-files-setup'],
testIgnore: /.*\.setup\.ts/
}
],
- timeout: 30000,
+ timeout: TEST_TIMEOUT_SECONDS * MILLISECONDS_IN_SECOND,
expect: {
- timeout: 10000,
+ timeout: ASSERT_TIMEOUT_SECONDS * MILLISECONDS_IN_SECOND,
toHaveScreenshot: { maxDiffPixels: 100 }
}
})
diff --git a/config/webpack/modules/codemirror-keymaps.d.ts b/config/webpack/modules/codemirror-keymaps.d.ts
new file mode 100644
index 000000000..9f5e587c1
--- /dev/null
+++ b/config/webpack/modules/codemirror-keymaps.d.ts
@@ -0,0 +1,5 @@
+declare module 'codemirror/src/input/keymap' {
+ import type { KeyMap } from 'codemirror'
+
+ export const getKeyMap: (keyMap: string) => KeyMap
+}
diff --git a/config/modules/postcss-hexrgba.d.ts b/config/webpack/modules/postcss-hexrgba.d.ts
similarity index 100%
rename from config/modules/postcss-hexrgba.d.ts
rename to config/webpack/modules/postcss-hexrgba.d.ts
diff --git a/config/webpack/postcss-hsl-legacy.ts b/config/webpack/postcss-hsl-legacy.ts
new file mode 100644
index 000000000..07dc87d17
--- /dev/null
+++ b/config/webpack/postcss-hsl-legacy.ts
@@ -0,0 +1,208 @@
+interface HslMatch {
+ fn: 'hsl' | 'hsla'
+ h: string
+ s: string
+ l: string
+ alpha?: string
+}
+
+interface DeclarationLike {
+ value?: string
+}
+
+const GRAD_TO_DEG = 0.9
+const DEG_PER_TURN = 360
+const DEG_PER_PI = 180
+const PERCENT_DIVISOR = 100
+const ROUNDING_MULTIPLIER = 1000
+
+const hslArgsRegex = new RegExp(
+ [
+ String.raw`(?hsl)a?\s*\(\s*`,
+ String.raw`(?\d*\.?\d+(?:deg|grad|rad|turn)?)`,
+ String.raw`(?:\s+|(?:\s*,\s*))`,
+ String.raw`(?\d*\.?\d+%)`,
+ String.raw`(?:\s+|(?:\s*,\s*))`,
+ String.raw`(?\d*\.?\d+%)`,
+ String.raw`(?:\s*(?:\/|,)\s*(?\d*\.?\d+%?))?`,
+ String.raw`\s*\)`
+ ].join(''),
+ 'g'
+)
+
+const hueWithUnitRegex = /^(?\d*\.?\d+)(?deg|grad|rad|turn)$/
+
+const convertHueToDeg = (hue: string): string => {
+ const match = hueWithUnitRegex.exec(hue)
+ if (!match?.groups) {
+ return hue
+ }
+
+ const value = Number(match.groups.value)
+ const unit = match.groups.unit
+
+ const degrees =
+ 'deg' === unit
+ ? value
+ : 'grad' === unit
+ ? value * GRAD_TO_DEG
+ : 'rad' === unit
+ ? value * DEG_PER_PI / Math.PI
+ : value * DEG_PER_TURN
+
+ return String(Math.round(degrees * ROUNDING_MULTIPLIER) / ROUNDING_MULTIPLIER)
+}
+
+const normalizeAlpha = (alpha: string): string => {
+ if (alpha.includes('%')) {
+ const value = Number(alpha.slice(0, -1)) / PERCENT_DIVISOR
+ alpha = String(value)
+ }
+
+ return alpha.replace(/^0\./, '.')
+}
+
+const toLegacyHsl = (colorFn: string): HslMatch | null => {
+ hslArgsRegex.lastIndex = 0
+ const match = hslArgsRegex.exec(colorFn)
+ if (!match?.groups) {
+ return null
+ }
+
+ const alpha = match.groups.alpha
+
+ return {
+ fn: alpha ? 'hsla' : 'hsl',
+ h: convertHueToDeg(match.groups.hue),
+ s: match.groups.s,
+ l: match.groups.l,
+ alpha: alpha ? normalizeAlpha(alpha) : undefined
+ }
+}
+
+const isIdentChar = (char: string | undefined): boolean => Boolean(char && /[a-zA-Z0-9_-]/.test(char))
+
+const isUnescapedQuote = (value: string, index: number, quote: '"' | "'"): boolean =>
+ quote === value[index] && '\\' !== value[index - 1]
+
+const findFunctionEnd = (value: string, openParenIndex: number): number | null => {
+ let depth = 0
+ let index = openParenIndex
+
+ while (index < value.length) {
+ const char = value[index]
+
+ if ('(' === char) {
+ depth += 1
+ } else if (')' === char) {
+ depth -= 1
+ if (0 === depth) {
+ return index
+ }
+ }
+
+ index += 1
+ }
+
+ return null
+}
+
+const legacyHslString = (fnText: string): string | null => {
+ const legacy = toLegacyHsl(fnText)
+ if (!legacy) {
+ return null
+ }
+
+ if ('hsl' === legacy.fn) {
+ return `hsl(${legacy.h}, ${legacy.s}, ${legacy.l})`
+ }
+
+ return `hsla(${legacy.h}, ${legacy.s}, ${legacy.l}, ${legacy.alpha})`
+}
+
+const replaceHslAtIndex = (
+ value: string,
+ index: number
+): { nextIndex: number; text: string } | null => {
+ const isStart = value.startsWith('hsl', index) || value.startsWith('hsla', index)
+ if (!isStart || isIdentChar(value[index - 1])) {
+ return null
+ }
+
+ const name = value.startsWith('hsla', index) ? 'hsla' : 'hsl'
+ let afterNameIndex = index + name.length
+
+ while (afterNameIndex < value.length && /\s/.test(value[afterNameIndex])) {
+ afterNameIndex += 1
+ }
+
+ if ('(' !== value[afterNameIndex]) {
+ return null
+ }
+
+ const endIndex = findFunctionEnd(value, afterNameIndex)
+ if (null === endIndex) {
+ return { nextIndex: value.length, text: value.slice(index) }
+ }
+
+ const fnText = value.slice(index, endIndex + 1)
+ return { nextIndex: endIndex + 1, text: legacyHslString(fnText) ?? fnText }
+}
+
+const transformHslFunctions = (value: string): string => {
+ let result = ''
+ let index = 0
+
+ let inSingle = false
+ let inDouble = false
+
+ while (index < value.length) {
+ const char = value[index]
+
+ if (!inDouble && isUnescapedQuote(value, index, "'")) {
+ inSingle = !inSingle
+ result += char
+ index += 1
+ continue
+ }
+
+ if (!inSingle && isUnescapedQuote(value, index, '"')) {
+ inDouble = !inDouble
+ result += char
+ index += 1
+ continue
+ }
+
+ if (inSingle || inDouble) {
+ result += char
+ index += 1
+ continue
+ }
+
+ const replacement = replaceHslAtIndex(value, index)
+ if (!replacement) {
+ result += char
+ index += 1
+ continue
+ }
+
+ result += replacement.text
+ index = replacement.nextIndex
+ }
+
+ return result
+}
+
+const postcssHslLegacy = () => ({
+ postcssPlugin: 'postcss-hsl-legacy',
+ Declaration(decl: DeclarationLike) {
+ if (!decl.value || !decl.value.includes('hsl(') && !decl.value.includes('hsla(')) {
+ return
+ }
+ decl.value = transformHslFunctions(decl.value)
+ }
+})
+
+postcssHslLegacy.postcss = true
+
+export default postcssHslLegacy
diff --git a/config/webpack-css.ts b/config/webpack/webpack-css.ts
similarity index 98%
rename from config/webpack-css.ts
rename to config/webpack/webpack-css.ts
index 828d7a023..1e4f94e54 100644
--- a/config/webpack-css.ts
+++ b/config/webpack/webpack-css.ts
@@ -3,10 +3,10 @@ import libsass from 'sass'
import cssnano from 'cssnano'
import autoprefixer from 'autoprefixer'
import rgbaCompat from 'postcss-hexrgba'
-import hslCompat from 'postcss-color-hsl'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import RemoveEmptyScriptsPlugin from 'webpack-remove-empty-scripts'
import { glob } from 'glob'
+import hslCompat from './postcss-hsl-legacy'
import type { Configuration, EntryObject } from 'webpack'
import type { Config as PostCssConfig } from 'postcss-load-config'
diff --git a/config/webpack-js.ts b/config/webpack/webpack-js.ts
similarity index 64%
rename from config/webpack-js.ts
rename to config/webpack/webpack-js.ts
index 3e2a19ce8..5c88861e8 100644
--- a/config/webpack-js.ts
+++ b/config/webpack/webpack-js.ts
@@ -2,11 +2,11 @@ import { join, resolve } from 'path'
import { DefinePlugin } from 'webpack'
import ESLintPlugin from 'eslint-webpack-plugin'
import RemoveEmptyScriptsPlugin from 'webpack-remove-empty-scripts'
-import { toCamelCase } from '../src/js/utils/text'
-import { dependencies } from '../package.json'
+import { toCamelCase } from '../../src/js/utils/text'
+import { dependencies } from '../../package.json'
import type { Configuration } from 'webpack'
-const SOURCE_DIR = './src/js'
+const SOURCE_DIR = './src/js/entries'
const DEST_DIR = './src/dist'
const babelConfig = {
@@ -24,16 +24,18 @@ const babelConfig = {
export const jsWebpackConfig: Configuration = {
entry: {
- edit: { import: `${SOURCE_DIR}/edit.tsx`, dependOn: 'editor' },
- editor: `${SOURCE_DIR}/editor.ts`,
- import: `${SOURCE_DIR}/import.tsx`,
- manage: `${SOURCE_DIR}/manage.ts`,
- mce: `${SOURCE_DIR}/mce.ts`,
- prism: `${SOURCE_DIR}/prism.ts`,
- settings: { import: `${SOURCE_DIR}/settings.ts`, dependOn: 'editor' }
+ 'admin-bar': `${SOURCE_DIR}/admin-bar.ts`,
+ 'edit': { import: `${SOURCE_DIR}/edit.ts`, dependOn: 'editor' },
+ 'editor': `${SOURCE_DIR}/editor.ts`,
+ 'import': `${SOURCE_DIR}/import.ts`,
+ 'manage': `${SOURCE_DIR}/manage.ts`,
+ 'mce': `${SOURCE_DIR}/mce.ts`,
+ 'prism': `${SOURCE_DIR}/prism.ts`,
+ 'settings': { import: `${SOURCE_DIR}/settings.ts`, dependOn: 'editor' },
+ 'welcome': `${SOURCE_DIR}/welcome.ts`
},
output: {
- path: join(resolve(__dirname), '..', DEST_DIR),
+ path: join(resolve(__dirname), '..', '..', DEST_DIR),
filename: '[name].js',
clean: true
},
@@ -41,6 +43,8 @@ export const jsWebpackConfig: Configuration = {
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
+ 'react-dom/client': 'ReactDOM',
+ 'react/jsx-runtime': 'ReactJSXRuntime',
'jquery': 'jQuery',
'tinymce': 'tinymce',
'codemirror': ['wp', 'CodeMirror'],
@@ -54,7 +58,7 @@ export const jsWebpackConfig: Configuration = {
)
},
resolve: {
- modules: [resolve(__dirname, '..', 'node_modules')],
+ modules: [resolve(__dirname, '..', '..', 'node_modules')],
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
},
module: {
diff --git a/eslint.config.mjs b/eslint.config.mjs
index c66739dc1..387380142 100755
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -6,7 +6,9 @@ import eslintTs from 'typescript-eslint'
import stylistic from '@stylistic/eslint-plugin'
import reactHooks from 'eslint-plugin-react-hooks'
import importPlugin from 'eslint-plugin-import'
+import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
import reactPlugin from 'eslint-plugin-react'
+import svgPlugin from 'eslint-plugin-svg-jsx'
import { FlatCompat } from '@eslint/eslintrc'
const compat = new FlatCompat({
@@ -21,12 +23,17 @@ export default eslintTs.config(
...compat.extends('plugin:react-hooks/recommended'),
reactPlugin.configs.flat.recommended,
importPlugin.flatConfigs.recommended,
+ jsxA11yPlugin.flatConfigs.recommended,
{
plugins: { 'react-hooks': reactHooks },
rules: reactHooks.configs.recommended.rules,
},
{
- ignores: ['bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*', '*.config.mjs', '*.config.js']
+ ignores: [
+ 'bundle/*', 'src/dist/*', 'src/vendor/*', 'svn/*',
+ '*.config.mjs', '*.config.js',
+ '.*/*', 'tmp/*', 'playwright-report/*'
+ ]
},
{
languageOptions: {
@@ -41,18 +48,8 @@ export default eslintTs.config(
},
plugins: {
'@stylistic': stylistic,
- 'react': reactPlugin
- },
- settings: {
- 'react': {
- version: 'detect'
- },
- 'import/resolver': {
- typescript: {
- alwaysTryTypes: true,
- project: './tsconfig.json',
- }
- }
+ 'react': reactPlugin,
+ 'svg-jsx': svgPlugin
},
rules: {
'@stylistic/array-bracket-newline': ['error', 'consistent'],
@@ -63,9 +60,9 @@ export default eslintTs.config(
'@stylistic/indent': ['error', 'tab', { SwitchCase: 1 }],
'@stylistic/jsx-quotes': ['error', 'prefer-double'],
'@stylistic/linebreak-style': ['error', 'unix'],
- '@stylistic/max-len': ['warn', 140, { ignorePattern: 'd="(.*?)"|_[_xn]\\(|import .+ from .+' }],
+ '@stylistic/max-len': ['error', 140, { ignorePattern: 'd="(.*?)"|_[_xn]\\(|import .+ from .+' }],
'@stylistic/multiline-ternary': 'off',
- '@stylistic/no-extra-parens': ['error', 'all'],
+ '@stylistic/no-extra-parens': ['error', 'all', { ignoreJSX: 'all', returnAssign: true }],
'@stylistic/no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
'@stylistic/no-tabs': ['error', { allowIndentationTabs: true }],
'@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }],
@@ -81,11 +78,11 @@ export default eslintTs.config(
objectLiteralTypeAssertions: 'never'
}],
'@typescript-eslint/consistent-type-imports': 'error',
- '@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/no-confusing-void-expression': ['error', { ignoreArrowShorthand: true }],
'@typescript-eslint/no-for-in-array': 'error',
'@typescript-eslint/no-import-type-side-effects': 'error',
'@typescript-eslint/no-inferrable-types': ['error', { ignoreProperties: true, ignoreParameters: false }],
+ '@typescript-eslint/no-magic-numbers': ['error', { ignore: [-1, 0, 1], ignoreEnums: true }],
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
@@ -117,14 +114,50 @@ export default eslintTs.config(
}],
'max-lines-per-function': ['warn', { skipBlankLines: true, skipComments: true }],
'no-invalid-this': 'error',
- 'no-magic-numbers': ['error', { ignore: [-1, 0, 1] }],
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'no-ternary': 'off',
'one-var': ['error', 'never'],
'prefer-named-capture-group': 'error',
'prefer-template': 'error',
'sort-imports': ['error', { ignoreDeclarationSort: true }],
- 'yoda': ['error', 'always']
+ 'yoda': ['error', 'always'],
+ // Accessibility rules.
+ 'jsx-a11y/alt-text': 'error',
+ 'jsx-a11y/anchor-has-content': 'error',
+ 'jsx-a11y/anchor-is-valid': 'error',
+ 'jsx-a11y/aria-props': 'error',
+ 'jsx-a11y/aria-proptypes': 'error',
+ 'jsx-a11y/aria-role': 'error',
+ 'jsx-a11y/aria-unsupported-elements': 'error',
+ 'jsx-a11y/click-events-have-key-events': 'error',
+ 'jsx-a11y/control-has-associated-label': ['warn', { ignoreElements: ['th', 'td'] }],
+ 'jsx-a11y/heading-has-content': 'error',
+ 'jsx-a11y/iframe-has-title': 'error',
+ 'jsx-a11y/img-redundant-alt': 'error',
+ 'jsx-a11y/interactive-supports-focus': 'error',
+ 'jsx-a11y/label-has-associated-control': 'error',
+ 'jsx-a11y/no-autofocus': 'error',
+ 'jsx-a11y/no-noninteractive-element-interactions': 'error',
+ 'jsx-a11y/no-noninteractive-tabindex': 'error',
+ 'jsx-a11y/no-redundant-roles': 'error',
+ 'jsx-a11y/no-static-element-interactions': 'error',
+ 'jsx-a11y/role-has-required-aria-props': 'error',
+ 'jsx-a11y/role-supports-aria-props': 'error',
+ 'jsx-a11y/tabindex-no-positive': 'error',
+ 'svg-jsx/camel-case-dash': 'error',
+ 'svg-jsx/camel-case-colon': 'error',
+ 'svg-jsx/no-style-string': 'error',
+ },
+ settings: {
+ 'react': {
+ version: 'detect'
+ },
+ 'import/resolver': {
+ typescript: {
+ alwaysTryTypes: true,
+ project: './tsconfig.json',
+ }
+ }
}
},
{
@@ -137,8 +170,9 @@ export default eslintTs.config(
}
},
{
- files: ['test/**', '**/*.test.*', '**/*.spec.*'],
+ files: ['tests/**', '**/*.test.*', '**/*.spec.*'],
rules: {
+ '@typescript-eslint/no-magic-numbers': 'off',
'max-lines-per-function': 'off'
}
}
diff --git a/package-lock.json b/package-lock.json
index b21cce5be..63be2d3ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,21 +1,22 @@
{
"name": "code-snippets",
- "version": "3.9.6",
+ "version": "3.10.0-beta.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "code-snippets",
- "version": "3.9.6",
+ "version": "3.10.0-beta.1",
"license": "GPL-2.0-or-later",
"dependencies": {
"@codemirror/fold": "^0.19.4",
"@wordpress/components": "^29.3.0",
+ "@wordpress/date": "^5.43.0",
"@wordpress/dom-ready": "^4.17.0",
"@wordpress/element": "^6.28.0",
"@wordpress/i18n": "^5.17.0",
"@wordpress/url": "^4.20.0",
- "axios": "^1.7.9",
+ "axios": "^1.13.5",
"classnames": "^2.5.1",
"codemirror": "^5.29",
"php-parser": "^3.2.2",
@@ -25,7 +26,8 @@
"react-select": "^5.10.0"
},
"devDependencies": {
- "@eslint/eslintrc": "^3.2.0",
+ "@axe-core/playwright": "^4.11.2",
+ "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.20.0",
"@playwright/test": "^1.48.0",
"@stylistic/eslint-plugin": "^3.1.0",
@@ -53,14 +55,16 @@
"eslint": "^9.20.1",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-svg-jsx": "^1.3.0",
"eslint-webpack-plugin": "^4.2.0",
- "glob": "^11.0.1",
+ "glob": "^11.1.0",
"globals": "^15.14.0",
+ "lint-staged": "^15.5.2",
"mini-css-extract-plugin": "^2.9.2",
"postcss": "^8.5.2",
- "postcss-color-hsl": "^2.0.0",
"postcss-hexrgba": "^2.1.0",
"postcss-load-config": "^6.0.1",
"postcss-loader": "^8.1.1",
@@ -127,6 +131,19 @@
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/@axe-core/playwright": {
+ "version": "4.11.3",
+ "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.3.tgz",
+ "integrity": "sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "axe-core": "~4.11.4"
+ },
+ "peerDependencies": {
+ "playwright-core": ">= 1.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"license": "MIT",
@@ -430,8 +447,6 @@
},
"node_modules/@babel/helpers": {
"version": "7.26.10",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
- "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -444,8 +459,6 @@
},
"node_modules/@babel/parser": {
"version": "7.26.10",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
- "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.10"
@@ -1702,19 +1715,16 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.25.7",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
"license": "MIT",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.26.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
- "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
@@ -1750,8 +1760,6 @@
},
"node_modules/@babel/types": {
"version": "7.26.10",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
- "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
@@ -1843,8 +1851,6 @@
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
- "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
"dev": true,
"funding": [
{
@@ -1866,8 +1872,6 @@
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
- "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
"dev": true,
"funding": [
{
@@ -1886,8 +1890,6 @@
},
"node_modules/@csstools/media-query-list-parser": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz",
- "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==",
"dev": true,
"funding": [
{
@@ -1918,8 +1920,6 @@
},
"node_modules/@dual-bundle/import-meta-resolve": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
- "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1927,10 +1927,43 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@emotion/babel-plugin": {
"version": "11.13.5",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
- "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
@@ -1951,8 +1984,7 @@
},
"node_modules/@emotion/cache": {
"version": "11.14.0",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
- "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
"dependencies": {
"@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0",
@@ -1974,26 +2006,22 @@
},
"node_modules/@emotion/hash": {
"version": "0.9.2",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
- "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ "license": "MIT"
},
"node_modules/@emotion/is-prop-valid": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
- "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
+ "license": "MIT",
"dependencies": {
"@emotion/memoize": "^0.9.0"
}
},
"node_modules/@emotion/memoize": {
"version": "0.9.0",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
- "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ "license": "MIT"
},
"node_modules/@emotion/react": {
"version": "11.14.0",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
- "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -2015,8 +2043,7 @@
},
"node_modules/@emotion/serialize": {
"version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
- "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
"dependencies": {
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
@@ -2027,13 +2054,11 @@
},
"node_modules/@emotion/sheet": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
- "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ "license": "MIT"
},
"node_modules/@emotion/styled": {
"version": "11.14.0",
- "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
- "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -2054,43 +2079,46 @@
},
"node_modules/@emotion/unitless": {
"version": "0.10.0",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
- "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ "license": "MIT"
},
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
- "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@emotion/utils": {
"version": "1.4.2",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
- "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+ "license": "MIT"
},
"node_modules/@emotion/weak-memoize": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
- "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ "license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.0",
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "eslint-visitor-keys": "^3.3.0"
+ "eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.11.0",
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2098,11 +2126,13 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.19.2",
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
+ "@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
@@ -2110,8 +2140,23 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/@eslint/core": {
- "version": "0.11.0",
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2122,7 +2167,9 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.2.0",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2132,7 +2179,7 @@
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
+ "js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
@@ -2145,6 +2192,8 @@
},
"node_modules/@eslint/eslintrc/node_modules/ajv": {
"version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2171,19 +2220,28 @@
},
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/@eslint/js": {
- "version": "9.20.0",
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.6",
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -2191,28 +2249,19 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.5",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.10.0",
+ "@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
- "version": "0.10.0",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
"node_modules/@floating-ui/core": {
"version": "1.5.0",
"license": "MIT",
@@ -2288,7 +2337,9 @@
}
},
"node_modules/@humanwhocodes/retry": {
- "version": "0.4.1",
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -2437,7 +2488,9 @@
}
},
"node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2459,8 +2512,6 @@
},
"node_modules/@keyv/serialize": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz",
- "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2469,18 +2520,16 @@
},
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
- "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.1"
}
},
"node_modules/@kwsites/promise-deferred": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
- "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@lezer/common": {
"version": "0.15.12",
@@ -2493,6 +2542,19 @@
"@lezer/common": "^0.15.0"
}
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@@ -2568,8 +2630,10 @@
"@parcel/watcher-win32-x64": "2.5.0"
}
},
- "node_modules/@parcel/watcher-darwin-arm64": {
+ "node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz",
+ "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==",
"cpu": [
"arm64"
],
@@ -2577,7 +2641,7 @@
"license": "MIT",
"optional": true,
"os": [
- "darwin"
+ "android"
],
"engines": {
"node": ">= 10.0.0"
@@ -2587,154 +2651,386 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.0",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
"optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=14"
- }
- },
- "node_modules/@playwright/test": {
- "version": "1.55.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz",
- "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==",
- "dev": true,
- "dependencies": {
- "playwright": "1.55.0"
- },
- "bin": {
- "playwright": "cli.js"
+ "node": ">= 10.0.0"
},
- "engines": {
- "node": ">=18"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@rtsao/scc": {
- "version": "1.1.0",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@sinclair/typebox": {
- "version": "0.27.8",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@sindresorhus/is": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
- "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz",
+ "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=10"
+ "node": ">= 10.0.0"
},
"funding": {
- "url": "https://github.com/sindresorhus/is?sponsor=1"
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@stylistic/eslint-plugin": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz",
- "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==",
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz",
+ "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@typescript-eslint/utils": "^8.13.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
- "estraverse": "^5.3.0",
- "picomatch": "^4.0.2"
- },
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">= 10.0.0"
},
- "peerDependencies": {
- "eslint": ">=8.40.0"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz",
+ "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": ">= 10.0.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
- "version": "4.0.2",
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz",
+ "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=12"
+ "node": ">= 10.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@stylistic/stylelint-plugin": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.2.tgz",
- "integrity": "sha512-tylFJGMQo62alGazK74MNxFjMagYOHmBZiePZFOJK2n13JZta0uVkB3Bh5qodUmOLtRH+uxH297EibK14UKm8g==",
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz",
+ "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.1",
- "@csstools/css-tokenizer": "^3.0.1",
- "@csstools/media-query-list-parser": "^3.0.1",
- "is-plain-object": "^5.0.0",
- "postcss-selector-parser": "^6.1.2",
- "postcss-value-parser": "^4.2.0",
- "style-search": "^0.1.0",
- "stylelint": "^16.8.2"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": "^18.12 || >=20.9"
+ "node": ">= 10.0.0"
},
- "peerDependencies": {
- "stylelint": "^16.8.0"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@stylistic/stylelint-plugin/node_modules/@csstools/media-query-list-parser": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz",
- "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz",
+ "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==",
+ "cpu": [
+ "arm64"
],
+ "dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=18"
+ "node": ">= 10.0.0"
},
- "peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.1",
- "@csstools/css-tokenizer": "^3.0.1"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@szmarczak/http-timer": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
- "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz",
+ "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "dependencies": {
- "defer-to-connect": "^2.0.0"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=10"
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/@tannin/compile": {
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz",
+ "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz",
+ "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz",
+ "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz",
+ "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.58.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
+ "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.58.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.13.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@stylistic/stylelint-plugin": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
+ "@csstools/media-query-list-parser": "^3.0.1",
+ "is-plain-object": "^5.0.0",
+ "postcss": "^8.4.41",
+ "postcss-selector-parser": "^6.1.2",
+ "postcss-value-parser": "^4.2.0",
+ "style-search": "^0.1.0"
+ },
+ "engines": {
+ "node": "^18.12 || >=20.9"
+ },
+ "peerDependencies": {
+ "stylelint": "^16.8.0"
+ }
+ },
+ "node_modules/@stylistic/stylelint-plugin/node_modules/@csstools/media-query-list-parser": {
+ "version": "3.0.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tannin/compile": {
"version": "1.1.0",
"license": "MIT",
"dependencies": {
@@ -2757,6 +3053,12 @@
"version": "1.1.0",
"license": "MIT"
},
+ "node_modules/@tannin/sprintf": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz",
+ "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==",
+ "license": "MIT"
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"dev": true,
@@ -2790,6 +3092,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/archiver": {
"version": "6.0.3",
"dev": true,
@@ -2800,9 +3113,8 @@
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
- "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
- "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/http-cache-semantics": "*",
"@types/keyv": "^3.1.4",
@@ -2837,7 +3149,9 @@
}
},
"node_modules/@types/estree": {
- "version": "1.0.6",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -2851,9 +3165,8 @@
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
- "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
@@ -2896,15 +3209,16 @@
},
"node_modules/@types/keyv": {
"version": "3.1.4",
- "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
- "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/mousetrap": {
- "version": "1.6.14",
+ "version": "1.6.15",
+ "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz",
+ "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==",
"license": "MIT"
},
"node_modules/@types/node": {
@@ -2929,11 +3243,13 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "18.3.12",
+ "version": "18.3.28",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
+ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
@@ -2960,9 +3276,8 @@
},
"node_modules/@types/responselike": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
- "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
@@ -2990,8 +3305,6 @@
},
"node_modules/@types/web": {
"version": "0.0.202",
- "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.202.tgz",
- "integrity": "sha512-2iO+wBir5OBnMlB9Z7aD/0SUZjR2mhiCLtPvGPboTqwBC4O3Yv6Vjwn5eMxGMXtRAm01OV9yUBi9C8pJa02TIA==",
"dev": true,
"license": "Apache-2.0"
},
@@ -3008,20 +3321,144 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.24.0",
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz",
+ "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.56.0",
+ "@typescript-eslint/type-utils": "8.56.0",
+ "@typescript-eslint/utils": "8.56.0",
+ "@typescript-eslint/visitor-keys": "8.56.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.56.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz",
+ "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz",
+ "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.55.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz",
+ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.56.0",
+ "@typescript-eslint/types": "8.56.0",
+ "@typescript-eslint/typescript-estree": "8.56.0",
+ "@typescript-eslint/visitor-keys": "8.56.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz",
+ "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.55.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz",
+ "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz",
+ "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.24.0",
- "@typescript-eslint/type-utils": "8.24.0",
- "@typescript-eslint/utils": "8.24.0",
- "@typescript-eslint/visitor-keys": "8.24.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.3.1",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.0.1"
+ "@typescript-eslint/tsconfig-utils": "^8.56.0",
+ "@typescript-eslint/types": "^8.56.0",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3031,21 +3468,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/parser": {
- "version": "8.24.0",
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz",
+ "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.24.0",
- "@typescript-eslint/types": "8.24.0",
- "@typescript-eslint/typescript-estree": "8.24.0",
- "@typescript-eslint/visitor-keys": "8.24.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/types": "8.56.0",
+ "@typescript-eslint/visitor-keys": "8.56.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3053,37 +3487,69 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
}
},
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.24.0",
+ "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz",
+ "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.24.0",
- "@typescript-eslint/visitor-keys": "8.24.0"
+ "@typescript-eslint/types": "8.55.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz",
+ "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz",
+ "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==",
+ "dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.24.0",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz",
+ "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.24.0",
- "@typescript-eslint/utils": "8.24.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.0.1"
+ "@typescript-eslint/types": "8.56.0",
+ "@typescript-eslint/typescript-estree": "8.56.0",
+ "@typescript-eslint/utils": "8.56.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3093,12 +3559,14 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.24.0",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz",
+ "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3110,18 +3578,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.24.0",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz",
+ "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.24.0",
- "@typescript-eslint/visitor-keys": "8.24.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.0.1"
+ "@typescript-eslint/project-service": "8.56.0",
+ "@typescript-eslint/tsconfig-utils": "8.56.0",
+ "@typescript-eslint/types": "8.56.0",
+ "@typescript-eslint/visitor-keys": "8.56.0",
+ "debug": "^4.4.3",
+ "minimatch": "^9.0.5",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3131,19 +3602,45 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.1",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz",
+ "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "@typescript-eslint/types": "8.55.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz",
+ "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3157,7 +3654,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.7.1",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3168,14 +3667,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.24.0",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz",
+ "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.24.0",
- "@typescript-eslint/types": "8.24.0",
- "@typescript-eslint/typescript-estree": "8.24.0"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.56.0",
+ "@typescript-eslint/types": "8.56.0",
+ "@typescript-eslint/typescript-estree": "8.56.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3185,36 +3686,278 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.24.0",
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "@typescript-eslint/types": "8.24.0",
- "eslint-visitor-keys": "^4.2.0"
+ "@napi-rs/wasm-runtime": "^0.2.11"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "node": ">=14.0.0"
}
},
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
},
"node_modules/@use-gesture/core": {
"version": "10.3.1",
@@ -3403,14 +4146,33 @@
}
},
"node_modules/@wordpress/a11y": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.20.0.tgz",
- "integrity": "sha512-hyFKC3D1o0Cvy1HeFgujsuW9gTrwVL4DVIfnQytG2+gMFaDyux4Qmzyg2e3k71BKlHn7J28Q3i0xNqC2k7ZoFw==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.40.0.tgz",
+ "integrity": "sha512-WhBuBgJTvanbBMNeflgCvwQLOU9ToITdYSzOvWg0kzz1i/e138NlCxrVpcXGUc6MQulduKhOWOtjizSdotaQRA==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/dom-ready": "^4.20.0",
- "@wordpress/i18n": "^5.20.0"
+ "@wordpress/dom-ready": "^4.40.0",
+ "@wordpress/i18n": "^6.13.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/a11y/node_modules/@wordpress/i18n": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz",
+ "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.40.0",
+ "gettext-parser": "^1.3.1",
+ "memize": "^2.1.0",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
},
"engines": {
"node": ">=18.12.0",
@@ -3418,20 +4180,20 @@
}
},
"node_modules/@wordpress/babel-preset-default": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.20.0.tgz",
- "integrity": "sha512-UGfPuNFjN8RG1BsFc04jOHoJFi3ZINYo4nsmrrUx1PFSFD2qpttmV03dWFWfqSvLvrMlYPQPMkYyK5KS6THxVQ==",
+ "version": "8.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.40.0.tgz",
+ "integrity": "sha512-UzSwDaxsMarnlfFUmEWW2qvkJy4JupW49uH0JztFobCamQ5QCL71M75zIspIXffiZVjQMBWruR7/+5QTJklewA==",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/core": "7.25.7",
+ "@babel/plugin-syntax-import-attributes": "7.26.0",
"@babel/plugin-transform-react-jsx": "7.25.7",
"@babel/plugin-transform-runtime": "7.25.7",
"@babel/preset-env": "7.25.7",
"@babel/preset-typescript": "7.25.7",
- "@babel/runtime": "7.25.7",
- "@wordpress/browserslist-config": "^6.20.0",
- "@wordpress/warning": "^3.20.0",
+ "@wordpress/browserslist-config": "^6.40.0",
+ "@wordpress/warning": "^3.40.0",
"browserslist": "^4.21.10",
"core-js": "^3.31.0",
"react": "^18.3.0"
@@ -3442,9 +4204,9 @@
}
},
"node_modules/@wordpress/browserslist-config": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.20.0.tgz",
- "integrity": "sha512-n9Q1UN3QL4DuZLySZpbJoZbQvBTjMjRV5yaxnmQaEpOyqablX4GnYq39fwTY72hBN/c1b0oyOFcsbhsrx0wqzg==",
+ "version": "6.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.40.0.tgz",
+ "integrity": "sha512-aX44MD4Kcr4LZT1YWa3VkMUUJjNfAgt7UECs/qrNGM8tC3l4/2Z4zRkJfpK3AoaXusb1J8+r5ZXWJWhxYK1JMQ==",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
@@ -3453,9 +4215,9 @@
}
},
"node_modules/@wordpress/components": {
- "version": "29.6.0",
- "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-29.6.0.tgz",
- "integrity": "sha512-kk9GxGnoGBqHz0S4gT2UJHQBwudE1AgTPOc3v3k72kZkDaT88ZayBd/4/gHsa659zImgrwXZ6SjQ6Nczt80Bgg==",
+ "version": "29.12.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-29.12.0.tgz",
+ "integrity": "sha512-jE96pUj84OZya54VusRdEIdTiLjbe2Qst3GbHZcQpA5GiSkPBmGjKWpO6FxR7kRDT4GMnZoVxgtV6xJk4IaNQw==",
"license": "GPL-2.0-or-later",
"dependencies": {
"@ariakit/react": "^0.4.15",
@@ -3470,23 +4232,23 @@
"@types/gradient-parser": "0.1.3",
"@types/highlight-words-core": "1.2.1",
"@use-gesture/react": "^10.3.1",
- "@wordpress/a11y": "^4.20.0",
- "@wordpress/compose": "^7.20.0",
- "@wordpress/date": "^5.20.0",
- "@wordpress/deprecated": "^4.20.0",
- "@wordpress/dom": "^4.20.0",
- "@wordpress/element": "^6.20.0",
- "@wordpress/escape-html": "^3.20.0",
- "@wordpress/hooks": "^4.20.0",
- "@wordpress/html-entities": "^4.20.0",
- "@wordpress/i18n": "^5.20.0",
- "@wordpress/icons": "^10.20.0",
- "@wordpress/is-shallow-equal": "^5.20.0",
- "@wordpress/keycodes": "^4.20.0",
- "@wordpress/primitives": "^4.20.0",
- "@wordpress/private-apis": "^1.20.0",
- "@wordpress/rich-text": "^7.20.0",
- "@wordpress/warning": "^3.20.0",
+ "@wordpress/a11y": "^4.26.0",
+ "@wordpress/compose": "^7.26.0",
+ "@wordpress/date": "^5.26.0",
+ "@wordpress/deprecated": "^4.26.0",
+ "@wordpress/dom": "^4.26.0",
+ "@wordpress/element": "^6.26.0",
+ "@wordpress/escape-html": "^3.26.0",
+ "@wordpress/hooks": "^4.26.0",
+ "@wordpress/html-entities": "^4.26.0",
+ "@wordpress/i18n": "^5.26.0",
+ "@wordpress/icons": "^10.26.0",
+ "@wordpress/is-shallow-equal": "^5.26.0",
+ "@wordpress/keycodes": "^4.26.0",
+ "@wordpress/primitives": "^4.26.0",
+ "@wordpress/private-apis": "^1.26.0",
+ "@wordpress/rich-text": "^7.26.0",
+ "@wordpress/warning": "^3.26.0",
"change-case": "^4.1.2",
"clsx": "^2.1.1",
"colord": "^2.7.0",
@@ -3494,7 +4256,7 @@
"deepmerge": "^4.3.0",
"fast-deep-equal": "^3.1.3",
"framer-motion": "^11.1.9",
- "gradient-parser": "^0.1.5",
+ "gradient-parser": "1.0.2",
"highlight-words-core": "^1.2.2",
"is-plain-object": "^5.0.0",
"memize": "^2.1.0",
@@ -3514,20 +4276,19 @@
}
},
"node_modules/@wordpress/compose": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.20.0.tgz",
- "integrity": "sha512-L84QUGXbXPdCAgNDNmmH+4tJuAl1MwH5an6CaQ+NaSXk4kM4xAc42znHo0n5LfsRmWxOPrtlGikxMXCaejvoyw==",
+ "version": "7.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.40.0.tgz",
+ "integrity": "sha512-u8LR5dxJd8KsiEv8eKG+aIgyRrp0lH0oOJy7cK9Jh721zc24TBu8vpxCADL7LbgmpPjQrjHh3LmPoCBtWL+FMg==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
"@types/mousetrap": "^1.6.8",
- "@wordpress/deprecated": "^4.20.0",
- "@wordpress/dom": "^4.20.0",
- "@wordpress/element": "^6.20.0",
- "@wordpress/is-shallow-equal": "^5.20.0",
- "@wordpress/keycodes": "^4.20.0",
- "@wordpress/priority-queue": "^3.20.0",
- "@wordpress/undo-manager": "^1.20.0",
+ "@wordpress/deprecated": "^4.40.0",
+ "@wordpress/dom": "^4.40.0",
+ "@wordpress/element": "^6.40.0",
+ "@wordpress/is-shallow-equal": "^5.40.0",
+ "@wordpress/keycodes": "^4.40.0",
+ "@wordpress/priority-queue": "^3.40.0",
+ "@wordpress/undo-manager": "^1.40.0",
"change-case": "^4.1.2",
"clipboard": "^2.0.11",
"mousetrap": "^1.6.5",
@@ -3542,19 +4303,18 @@
}
},
"node_modules/@wordpress/data": {
- "version": "10.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.20.0.tgz",
- "integrity": "sha512-oj1Ci7mPZ2kbmI2cdqk7apfvd4nlWziPstlIZIKCb02rCEMqP8dC0lc/CDt8GVOXJ23iMhZgkfkvnFNaMXmBNQ==",
+ "version": "10.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.40.0.tgz",
+ "integrity": "sha512-wwqkMc9iLteRO1zNxL/R3COWnijsdC5TIjenmd2JivReUmdA4ulAN3Tq7QiHkhwOV4jzZkuWW7DgR2ynxf55lw==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/compose": "^7.20.0",
- "@wordpress/deprecated": "^4.20.0",
- "@wordpress/element": "^6.20.0",
- "@wordpress/is-shallow-equal": "^5.20.0",
- "@wordpress/priority-queue": "^3.20.0",
- "@wordpress/private-apis": "^1.20.0",
- "@wordpress/redux-routine": "^5.20.0",
+ "@wordpress/compose": "^7.40.0",
+ "@wordpress/deprecated": "^4.40.0",
+ "@wordpress/element": "^6.40.0",
+ "@wordpress/is-shallow-equal": "^5.40.0",
+ "@wordpress/priority-queue": "^3.40.0",
+ "@wordpress/private-apis": "^1.40.0",
+ "@wordpress/redux-routine": "^5.40.0",
"deepmerge": "^4.3.0",
"equivalent-key-map": "^0.2.2",
"is-plain-object": "^5.0.0",
@@ -3572,13 +4332,12 @@
}
},
"node_modules/@wordpress/date": {
- "version": "5.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.20.0.tgz",
- "integrity": "sha512-V34zSLveuXTe8wvnIpUXroP7dP9FK1HzMmGNB5JtoPhrqJeNvP4fzju8RJwBGpU1sFaqO3w+EZoNdTV9k0hqxA==",
+ "version": "5.43.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.43.0.tgz",
+ "integrity": "sha512-8DiFlE7YzP7F/P59Hr6h5fWJxJlvt6eZgU1C7huM9XhANh8Y3dZfepsySL6K7h1yE66SQDSq07cEefFQgJW31g==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/deprecated": "^4.20.0",
+ "@wordpress/deprecated": "^4.43.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.40"
},
@@ -3588,13 +4347,12 @@
}
},
"node_modules/@wordpress/deprecated": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.20.0.tgz",
- "integrity": "sha512-36JbtGUSQ49SM33fvfSAvN8ZGDqCxCPAj2PByAney4WhoVbznxGWnao8qKwWrNNG5xec1reQvXFxOsD7qab4rg==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.43.0.tgz",
+ "integrity": "sha512-Pxn+nUmCVAaKBiZun2tEVweVdevMvWFWyCRqIqsAKdWCLsD8Uk6o27EwXc1u8BlO65VmK8D2zF9uWKGKfdZbCw==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/hooks": "^4.20.0"
+ "@wordpress/hooks": "^4.43.0"
},
"engines": {
"node": ">=18.12.0",
@@ -3602,13 +4360,12 @@
}
},
"node_modules/@wordpress/dom": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.20.0.tgz",
- "integrity": "sha512-uLYH7hKfJDUHkooAy0uoFJXMCkraTP3gdybblAJT9a/dqAOVcsMODH9gTGI99IoFhsvJwWo5Vk94/kgqeOdarA==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.40.0.tgz",
+ "integrity": "sha512-JBF1sRjJMFgLn0pet0tmPzO1kNaa35/DwAAtG81zzjikctR1PzE3EK8o6ZGPtUY1sTa9l7aB1Lxfcum/eroyRg==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/deprecated": "^4.20.0"
+ "@wordpress/deprecated": "^4.40.0"
},
"engines": {
"node": ">=18.12.0",
@@ -3616,28 +4373,24 @@
}
},
"node_modules/@wordpress/dom-ready": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.20.0.tgz",
- "integrity": "sha512-FkdfoITfj1yBSUMn+IKIqpm7zwA4AbHPkYdCXNgP9w5BRBpoTqXMGgDbe8rt4aSWkSEiRChZ9rGmtG84LByRTA==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.40.0.tgz",
+ "integrity": "sha512-mHVy4P6yc0XLmGgnccxptMKg83TwcbYKfYrQH8pTcIu43P24zONTd44eZFjkfz7c/b+RLJg1Kj+d5mKh1xqH1A==",
"license": "GPL-2.0-or-later",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
"node_modules/@wordpress/element": {
- "version": "6.28.0",
- "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.28.0.tgz",
- "integrity": "sha512-FSojQxfsaDXwc11nMgc/OlIgq1BgjpNf9m2Smw1Z3GmVq8J4E6wAFpJuoUPwyjON4i1apiWl/bqQA84yT9C84g==",
+ "version": "6.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.40.0.tgz",
+ "integrity": "sha512-OhU8B2xEGg7c41rh/VRiJLOz6TnM/r5r8sraAg5ISc2bF7s2oAFqLwvlR0/U6ervyYwbK644osWZGQxFyL3huA==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@types/react": "^18.2.79",
- "@types/react-dom": "^18.2.25",
- "@wordpress/escape-html": "^3.28.0",
+ "@types/react": "^18.3.27",
+ "@types/react-dom": "^18.3.1",
+ "@wordpress/escape-html": "^3.40.0",
"change-case": "^4.1.2",
"is-plain-object": "^5.0.0",
"react": "^18.3.0",
@@ -3650,9 +4403,8 @@
},
"node_modules/@wordpress/env": {
"version": "9.10.0",
- "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-9.10.0.tgz",
- "integrity": "sha512-GqUg1XdrUXI3l5NhHhEZisrccW+VPqJSU5xO1IXybI6KOvmSecidxWEqlMj26vzu2P5aLCWZcx28QkrrY3jvdg==",
"dev": true,
+ "license": "GPL-2.0-or-later",
"dependencies": {
"chalk": "^4.0.0",
"copy-dir": "^1.3.0",
@@ -3667,85 +4419,110 @@
"terminal-link": "^2.0.0",
"yargs": "^17.3.0"
},
- "bin": {
- "wp-env": "bin/wp-env"
+ "bin": {
+ "wp-env": "bin/wp-env"
+ }
+ },
+ "node_modules/@wordpress/env/node_modules/cliui": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "node_modules/@wordpress/env/node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "node_modules/@wordpress/env/node_modules/wrap-ansi": {
+ "version": "7.0.0",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "sprintf-js": "~1.0.2"
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@wordpress/env/node_modules/y18n": {
+ "version": "5.0.8",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
}
},
- "node_modules/@wordpress/env/node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "node_modules/@wordpress/env/node_modules/yargs": {
+ "version": "17.7.2",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
},
- "bin": {
- "js-yaml": "bin/js-yaml.js"
+ "engines": {
+ "node": ">=12"
}
},
- "node_modules/@wordpress/env/node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true
+ "node_modules/@wordpress/env/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
},
"node_modules/@wordpress/escape-html": {
- "version": "3.28.0",
- "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.28.0.tgz",
- "integrity": "sha512-LDcr26vX7OkcvHMjAFxg0vNmI7cP5lzLs+HbnwM1H9h0dsj3svIWXXFF/7lQl7sbI9+rjF0GkR1Fgd+DvF7zxw==",
+ "version": "3.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.40.0.tgz",
+ "integrity": "sha512-DD6xWVbnw4fGGgO6DFDTJiLj52om0OG4cYHLz7ZhuipmOlEUGljPYOcrj8uxtlh5EFrqHCIPkOya+qQXUHUSBw==",
"license": "GPL-2.0-or-later",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
"node_modules/@wordpress/hooks": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.20.0.tgz",
- "integrity": "sha512-nn6RbAER5EitMJVr+jpOg5HDIUEEOEv6jC/P1s5C0HvsOaldBeJ80A73Gsd/NFGlUqCc7o51uoZO36wGoPjIpg==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.43.0.tgz",
+ "integrity": "sha512-BY7GPjEwhOlgkavVak40E3RtA8Z9ehydqTZckRoesMRjXYfxKSzr1C1FT4wAPS5uXM1pNlWivfofMaJjVNQu5w==",
"license": "GPL-2.0-or-later",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
"node_modules/@wordpress/html-entities": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.20.0.tgz",
- "integrity": "sha512-ZOQ9zsfs5p32K+uAEy2vbY7rnAG5KjMdXwOn4v2FPeXF6A6jWQudK/smV7nRB3ZMaSZnzQ54tiUXbuSpCmmGYA==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.40.0.tgz",
+ "integrity": "sha512-bsJrwZk22On8gNhUd84yyWKt/nrNZtACNZpXmkpyue/oTlFqNenLfhqRkvTKJzjbLxrrcUPsXlskbPcS7mxwTQ==",
"license": "GPL-2.0-or-later",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
"node_modules/@wordpress/i18n": {
- "version": "5.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-5.20.0.tgz",
- "integrity": "sha512-JrgVe5QT+nDHFbujeD0lJifDpdgmOt1SSnEK631jIISjfGjriYwphoOEAzBGRh9S9ThqOOfW4mLOOeXPYmJR7w==",
+ "version": "5.26.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-5.26.0.tgz",
+ "integrity": "sha512-YHzaUWlCuN2ynl47qbsdMkTGtP52+E1giDOdWBgUaSexUYjbeFxKFUzRMB0Wuh1psL80+VzvJOH/mU440KAJnA==",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "7.25.7",
- "@wordpress/hooks": "^4.20.0",
+ "@wordpress/hooks": "^4.26.0",
"gettext-parser": "^1.3.1",
"memize": "^2.1.0",
"sprintf-js": "^1.1.1",
@@ -3760,14 +4537,14 @@
}
},
"node_modules/@wordpress/icons": {
- "version": "10.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.20.0.tgz",
- "integrity": "sha512-wGmmGDQoDKjmuGdC2I8C3JA9GlqVM9DK5FJZuUukHTh+Nz72W8CA30PzGKavxWOYd7cZ0B97VioE85aVwOAe3g==",
+ "version": "10.32.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.32.0.tgz",
+ "integrity": "sha512-1WvJdT361X1LnetYBpBWUjAVXZzl+pBdIwHbYRAp8ej47EI/igPmNxmq81nFd40s8fer/9qtipielcqSI6H2rA==",
"license": "GPL-2.0-or-later",
"dependencies": {
"@babel/runtime": "7.25.7",
- "@wordpress/element": "^6.20.0",
- "@wordpress/primitives": "^4.20.0"
+ "@wordpress/element": "^6.32.0",
+ "@wordpress/primitives": "^4.32.0"
},
"engines": {
"node": ">=18.12.0",
@@ -3775,26 +4552,42 @@
}
},
"node_modules/@wordpress/is-shallow-equal": {
- "version": "5.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.20.0.tgz",
- "integrity": "sha512-/m8P/6AQgZchMbeDhne5z8Wzde07mv8+l7qsYK6VhChEWonrYN7Sfig9uGPtWijkWwOkxYjWE6ggcJ5xn8KVlg==",
+ "version": "5.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.40.0.tgz",
+ "integrity": "sha512-IU11xOcHIGqDLxx9X+8RIk4WFo0qqba0bpeLqrVKsQXNGjP7tXSo2ufylxE9K9CEYXFMF0C65k83XpRZtEkA8g==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/keycodes": {
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.40.0.tgz",
+ "integrity": "sha512-laLkfjwkhMdreCl/KQdHucBIQAYwSjkyk3BToq/PCrcxFJBwWK2NgEtSl/t1CEw2HJwe0H2ne3FEWtipY4iDrA==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7"
+ "@wordpress/i18n": "^6.13.0"
},
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
- "node_modules/@wordpress/keycodes": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.20.0.tgz",
- "integrity": "sha512-GLzp9uTSNOPvX378FInwvLj4riqq1N/By1kd40iAr1hXfRAjy0H//vktJ70r+AkwK0R07txtCPiLnDcW53hLmg==",
+ "node_modules/@wordpress/keycodes/node_modules/@wordpress/i18n": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz",
+ "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/i18n": "^5.20.0"
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.40.0",
+ "gettext-parser": "^1.3.1",
+ "memize": "^2.1.0",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
},
"engines": {
"node": ">=18.12.0",
@@ -3802,13 +4595,12 @@
}
},
"node_modules/@wordpress/primitives": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.20.0.tgz",
- "integrity": "sha512-fVs9EnuI2UV1xfAYY//OOfO+O3n4VvPVGcI/zHMAfIdJGWEbCQVDatAnteX/2hkjBe85jqErkU+0bAKsddhpcA==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.40.0.tgz",
+ "integrity": "sha512-0gOw3n3kSUsAPo91xNDS9J4GGTrNXU90XmuWn7mNfXAl5uRAMRnxgkfL+pwd0ng0rmdPtjPqrJpljnP2oy3K2w==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/element": "^6.20.0",
+ "@wordpress/element": "^6.40.0",
"clsx": "^2.1.1"
},
"engines": {
@@ -3820,12 +4612,11 @@
}
},
"node_modules/@wordpress/priority-queue": {
- "version": "3.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.20.0.tgz",
- "integrity": "sha512-2gOa8LQaTLPgk1GDkkXWALA9yH47yhDZKHKBHy8YH61c+m8ai8RctWegzXA6pSInPW77nbBUNHSOzxWTsDN1Sw==",
+ "version": "3.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.40.0.tgz",
+ "integrity": "sha512-85km9+I7RWi7P73BU/yom41gpdu0watdQ1GscQhQBel6BjHOXO5qWG6P9i3sEH47bz7EyO248l4LC/h8oHqpfQ==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
"requestidlecallback": "^0.3.0"
},
"engines": {
@@ -3834,25 +4625,21 @@
}
},
"node_modules/@wordpress/private-apis": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.20.0.tgz",
- "integrity": "sha512-DngnywYj6zDt9D0HgnX7k0il5SsdDYUxEg82GqNu3Jd879LlG9MtIxcoV+ErCsH7ryTydXw4sC17W09m2LEMBQ==",
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.40.0.tgz",
+ "integrity": "sha512-68cwZKVq8Xy8GBzKoDRuV4b3pQ4nJFItY689HXp+poc0XXrnAeC4ZhjeSgS1qGRpFo6RVvLjjcaZsN2OrSSMvQ==",
"license": "GPL-2.0-or-later",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
}
},
"node_modules/@wordpress/redux-routine": {
- "version": "5.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.20.0.tgz",
- "integrity": "sha512-6JZI75oMAWGBgo+x2rmfIGzqVuxiZ3wQBqNCdVDDOGYH9qcRzYgBWRSPVfh4rvGLTtpVnFHnnBQ+jr5iPGHOxQ==",
+ "version": "5.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.40.0.tgz",
+ "integrity": "sha512-V+c1yCBl4i7qvRsWtQpGevbFCGtrRlzDe++4bwnrYJUiu79wbSXWRrmiSFr/EQie2KNM680t2MeFcfO7nsDVoA==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
"is-plain-object": "^5.0.0",
"is-promise": "^4.0.0",
"rungen": "^0.3.2"
@@ -3866,20 +4653,22 @@
}
},
"node_modules/@wordpress/rich-text": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.20.0.tgz",
- "integrity": "sha512-irx6cvmoxSSajzGGt5iVxek3vNfG5LslORQ1g7HXcNawfFBxhptU3vzPF2+ywvs6o3BCbTZVfa98rOfX3C2J/Q==",
+ "version": "7.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.40.0.tgz",
+ "integrity": "sha512-eHImTvzPEg4GWAuzcagyc2tArc6neA2sbqvybpd5JzhEpgv/Q0zcKwLfUKI05kYaaPI/Rg5WXgeXDxjGYpq5hA==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/a11y": "^4.20.0",
- "@wordpress/compose": "^7.20.0",
- "@wordpress/data": "^10.20.0",
- "@wordpress/deprecated": "^4.20.0",
- "@wordpress/element": "^6.20.0",
- "@wordpress/escape-html": "^3.20.0",
- "@wordpress/i18n": "^5.20.0",
- "@wordpress/keycodes": "^4.20.0",
+ "@wordpress/a11y": "^4.40.0",
+ "@wordpress/compose": "^7.40.0",
+ "@wordpress/data": "^10.40.0",
+ "@wordpress/deprecated": "^4.40.0",
+ "@wordpress/dom": "^4.40.0",
+ "@wordpress/element": "^6.40.0",
+ "@wordpress/escape-html": "^3.40.0",
+ "@wordpress/i18n": "^6.13.0",
+ "@wordpress/keycodes": "^4.40.0",
+ "@wordpress/private-apis": "^1.40.0",
+ "colord": "2.9.3",
"memize": "^2.1.0"
},
"engines": {
@@ -3890,14 +4679,33 @@
"react": "^18.0.0"
}
},
+ "node_modules/@wordpress/rich-text/node_modules/@wordpress/i18n": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz",
+ "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.40.0",
+ "gettext-parser": "^1.3.1",
+ "memize": "^2.1.0",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/undo-manager": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.20.0.tgz",
- "integrity": "sha512-IG3/u0uR0nfZ/kXRfC6DVFK52hbbNx4aMB/c5DAMQgKtJElE7Mz1Mf5zgU1XNlpBOdguQp6oo/nMpyJUIasipQ==",
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.40.0.tgz",
+ "integrity": "sha512-QvhHke/bVaOSPeaV5mNvsuIQpc2dJFDhXZ7gUnpuzyuNHh74Xk6Ar0vvYcfXiALst4ejKqWCoKOBi7ve1h2ppg==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/is-shallow-equal": "^5.20.0"
+ "@wordpress/is-shallow-equal": "^5.40.0"
},
"engines": {
"node": ">=18.12.0",
@@ -3905,12 +4713,11 @@
}
},
"node_modules/@wordpress/url": {
- "version": "4.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.20.0.tgz",
- "integrity": "sha512-IUkph25ewBDTxuSC9wXvMbec6IB2A3pNz0Xkm1Ffzm2ngk/f+0+Ko2WSKdXqqR8U67Eyb+ZUZFtBPmEsKvEZ4A==",
+ "version": "4.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.40.0.tgz",
+ "integrity": "sha512-DVAJlW7bdocKfQp8G7tS73vnobAC8TBbIHHdxeLQKwzT8mOkG4W/rpzN2KTxkiJKFXUu5in4F8a6T+Cy/Lt1eQ==",
"license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
"remove-accents": "^0.5.0"
},
"engines": {
@@ -3919,9 +4726,9 @@
}
},
"node_modules/@wordpress/warning": {
- "version": "3.20.0",
- "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.20.0.tgz",
- "integrity": "sha512-IQRvlWwNWO6kncZ/qQEX/KCvsrm/0FIcuCXrTXlGP4OslRG7XtU9xs2lOP34Y6G3onMwhpD8mXFUK7udq305EQ==",
+ "version": "3.40.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.40.0.tgz",
+ "integrity": "sha512-0l3OFa1Z+UdhWRRHX9JWWKofo7Lbi2MqOFzzzn0MC26HOyfieQycjLVLNVNXaaodIKUhap6uDQq+JXbbHm881A==",
"license": "GPL-2.0-or-later",
"engines": {
"node": ">=18.12.0",
@@ -3950,7 +4757,9 @@
}
},
"node_modules/acorn": {
- "version": "8.12.1",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -3960,6 +4769,19 @@
"node": ">=0.4.0"
}
},
+ "node_modules/acorn-import-phases": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
+ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "peerDependencies": {
+ "acorn": "^8.14.0"
+ }
+ },
"node_modules/acorn-jsx": {
"version": "5.3.2",
"dev": true,
@@ -3980,14 +4802,16 @@
}
},
"node_modules/ajv": {
- "version": "8.12.0",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fast-deep-equal": "^3.1.1",
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
+ "require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
@@ -4023,9 +4847,8 @@
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"type-fest": "^0.21.3"
},
@@ -4046,8 +4869,6 @@
},
"node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4061,15 +4882,11 @@
}
},
"node_modules/ansis": {
- "version": "1.5.2",
+ "version": "4.0.0-node10",
"dev": true,
"license": "ISC",
"engines": {
- "node": ">=12.13"
- },
- "funding": {
- "type": "patreon",
- "url": "https://patreon.com/biodiscus"
+ "node": ">=10"
}
},
"node_modules/archiver": {
@@ -4106,16 +4923,11 @@
"node": ">= 14"
}
},
- "node_modules/archiver-utils/node_modules/brace-expansion": {
- "version": "2.0.1",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/archiver-utils/node_modules/glob": {
- "version": "10.4.5",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4219,6 +5031,16 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/array-buffer-byte-length": {
"version": "1.0.2",
"dev": true,
@@ -4235,16 +5057,20 @@
}
},
"node_modules/array-includes": {
- "version": "3.1.8",
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
"define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
- "es-object-atoms": "^1.0.0",
- "get-intrinsic": "^1.2.4",
- "is-string": "^1.0.7"
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -4255,8 +5081,6 @@
},
"node_modules/array-union": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4283,16 +5107,19 @@
}
},
"node_modules/array.prototype.findlastindex": {
- "version": "1.2.5",
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
"define-properties": "^1.2.1",
- "es-abstract": "^1.23.2",
+ "es-abstract": "^1.23.9",
"es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
- "es-shim-unscopables": "^1.0.2"
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -4302,14 +5129,16 @@
}
},
"node_modules/array.prototype.flat": {
- "version": "1.3.2",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "es-abstract": "^1.22.1",
- "es-shim-unscopables": "^1.0.0"
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -4370,10 +5199,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/astral-regex": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
- "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4395,6 +5229,8 @@
},
"node_modules/asynckit": {
"version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
@@ -4447,17 +5283,37 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/axe-core": {
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz",
+ "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/axios": {
- "version": "1.8.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
- "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT",
"dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/b4a": {
"version": "1.6.6",
"dev": true,
@@ -4665,13 +5521,28 @@
],
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.13",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz",
+ "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/boolbase": {
"version": "1.0.0",
"dev": true,
"license": "ISC"
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4691,7 +5562,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.24.2",
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
@@ -4709,10 +5582,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001669",
- "electron-to-chromium": "^1.5.41",
- "node-releases": "^2.0.18",
- "update-browserslist-db": "^1.1.1"
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -4746,9 +5620,8 @@
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "*"
}
@@ -4759,30 +5632,26 @@
"license": "MIT"
},
"node_modules/cacheable": {
- "version": "1.8.10",
- "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.10.tgz",
- "integrity": "sha512-0ZnbicB/N2R6uziva8l6O6BieBklArWyiGx4GkwAhLKhSHyQtRfM9T1nx7HHuHDKkYB/efJQhz3QJ6x/YqoZzA==",
+ "version": "1.9.0",
"dev": true,
"license": "MIT",
"dependencies": {
- "hookified": "^1.8.1",
- "keyv": "^5.3.2"
+ "hookified": "^1.8.2",
+ "keyv": "^5.3.3"
}
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
- "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
- "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10.6.0"
}
},
"node_modules/cacheable-request": {
"version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
- "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
@@ -4798,8 +5667,6 @@
},
"node_modules/cacheable/node_modules/keyv": {
"version": "5.3.3",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.3.tgz",
- "integrity": "sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4824,8 +5691,9 @@
}
},
"node_modules/call-bind-apply-helpers": {
- "version": "1.0.1",
- "dev": true,
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -4836,12 +5704,14 @@
}
},
"node_modules/call-bound": {
- "version": "1.0.3",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "get-intrinsic": "^1.2.6"
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
@@ -4877,9 +5747,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001727",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
- "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
+ "version": "1.0.30001809",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
+ "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
"dev": true,
"funding": [
{
@@ -4941,9 +5811,8 @@
},
"node_modules/chardet": {
"version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/chokidar": {
"version": "4.0.1",
@@ -4987,9 +5856,8 @@
},
"node_modules/cli-cursor": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"restore-cursor": "^3.1.0"
},
@@ -4997,29 +5865,143 @@
"node": ">=8"
}
},
- "node_modules/cli-spinners": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
- "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
"engines": {
- "node": ">=6"
+ "node": ">=12"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/cli-width": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
- "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
"dev": true,
+ "license": "ISC",
"engines": {
"node": ">= 10"
}
},
"node_modules/clipboard": {
"version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"license": "MIT",
"dependencies": {
"good-listener": "^1.2.2",
@@ -5027,25 +6009,10 @@
"tiny-emitter": "^2.0.0"
}
},
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/clone": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.8"
}
@@ -5076,9 +6043,8 @@
},
"node_modules/clone-response": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
- "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"mimic-response": "^1.0.0"
},
@@ -5088,6 +6054,8 @@
},
"node_modules/clsx": {
"version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -5099,8 +6067,6 @@
},
"node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5112,8 +6078,6 @@
},
"node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
@@ -5128,6 +6092,8 @@
},
"node_modules/combined-stream": {
"version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
@@ -5194,12 +6160,11 @@
},
"node_modules/concat-stream": {
"version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"dev": true,
"engines": [
"node >= 0.8"
],
+ "license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
@@ -5223,9 +6188,8 @@
},
"node_modules/copy-dir": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.3.0.tgz",
- "integrity": "sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/core-js": {
"version": "3.33.2",
@@ -5352,8 +6316,6 @@
},
"node_modules/css-functions-list": {
"version": "3.2.3",
- "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz",
- "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5558,9 +6520,18 @@
"license": "CC0-1.0"
},
"node_modules/csstype": {
- "version": "3.1.2",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
"node_modules/data-view-buffer": {
"version": "1.0.2",
"dev": true,
@@ -5618,9 +6589,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -5636,9 +6607,8 @@
},
"node_modules/decompress-response": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
@@ -5651,9 +6621,8 @@
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5668,6 +6637,8 @@
},
"node_modules/deepmerge": {
"version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -5675,9 +6646,8 @@
},
"node_modules/defaults": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
- "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"clone": "^1.0.2"
},
@@ -5687,9 +6657,8 @@
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
- "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
}
@@ -5728,6 +6697,8 @@
},
"node_modules/delayed-stream": {
"version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
@@ -5735,6 +6706,8 @@
},
"node_modules/delegate": {
"version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"license": "MIT"
},
"node_modules/detect-libc": {
@@ -5750,7 +6723,9 @@
}
},
"node_modules/diff": {
- "version": "4.0.2",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
+ "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -5759,8 +6734,6 @@
},
"node_modules/dir-glob": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5772,9 +6745,8 @@
},
"node_modules/docker-compose": {
"version": "0.24.8",
- "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz",
- "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"yaml": "^2.2.2"
},
@@ -5862,7 +6834,6 @@
},
"node_modules/dunder-proto": {
"version": "1.0.1",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -5879,7 +6850,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.68",
+ "version": "1.5.286",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
+ "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
"dev": true,
"license": "ISC"
},
@@ -5897,20 +6870,21 @@
},
"node_modules/end-of-stream": {
"version": "1.4.5",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/enhanced-resolve": {
- "version": "5.17.1",
+ "version": "5.19.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
+ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
+ "tapable": "^2.3.0"
},
"engines": {
"node": ">=10.13.0"
@@ -5946,8 +6920,23 @@
"node": ">=4"
}
},
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/equivalent-key-map": {
"version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz",
+ "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==",
"license": "MIT"
},
"node_modules/error-ex": {
@@ -5958,7 +6947,9 @@
}
},
"node_modules/es-abstract": {
- "version": "1.23.9",
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5966,18 +6957,18 @@
"arraybuffer.prototype.slice": "^1.0.4",
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
+ "call-bound": "^1.0.4",
"data-view-buffer": "^1.0.2",
"data-view-byte-length": "^1.0.2",
"data-view-byte-offset": "^1.0.1",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
+ "es-object-atoms": "^1.1.1",
"es-set-tostringtag": "^2.1.0",
"es-to-primitive": "^1.3.0",
"function.prototype.name": "^1.1.8",
- "get-intrinsic": "^1.2.7",
- "get-proto": "^1.0.0",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
"get-symbol-description": "^1.1.0",
"globalthis": "^1.0.4",
"gopd": "^1.2.0",
@@ -5989,21 +6980,24 @@
"is-array-buffer": "^3.0.5",
"is-callable": "^1.2.7",
"is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
"is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
"is-shared-array-buffer": "^1.0.4",
"is-string": "^1.1.1",
"is-typed-array": "^1.1.15",
- "is-weakref": "^1.1.0",
+ "is-weakref": "^1.1.1",
"math-intrinsics": "^1.1.0",
- "object-inspect": "^1.13.3",
+ "object-inspect": "^1.13.4",
"object-keys": "^1.1.1",
"object.assign": "^4.1.7",
"own-keys": "^1.0.1",
- "regexp.prototype.flags": "^1.5.3",
+ "regexp.prototype.flags": "^1.5.4",
"safe-array-concat": "^1.1.3",
"safe-push-apply": "^1.0.0",
"safe-regex-test": "^1.1.0",
"set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
"string.prototype.trim": "^1.2.10",
"string.prototype.trimend": "^1.0.9",
"string.prototype.trimstart": "^1.0.8",
@@ -6012,7 +7006,7 @@
"typed-array-byte-offset": "^1.0.4",
"typed-array-length": "^1.0.7",
"unbox-primitive": "^1.1.0",
- "which-typed-array": "^1.1.18"
+ "which-typed-array": "^1.1.19"
},
"engines": {
"node": ">= 0.4"
@@ -6023,7 +7017,6 @@
},
"node_modules/es-define-property": {
"version": "1.0.1",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -6031,7 +7024,6 @@
},
"node_modules/es-errors": {
"version": "1.3.0",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -6064,13 +7056,16 @@
}
},
"node_modules/es-module-lexer": {
- "version": "1.4.0",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"dev": true,
"license": "MIT"
},
"node_modules/es-object-atoms": {
- "version": "1.0.0",
- "dev": true,
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -6081,7 +7076,6 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -6094,11 +7088,16 @@
}
},
"node_modules/es-shim-unscopables": {
- "version": "1.0.2",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "hasown": "^2.0.0"
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
"node_modules/es-to-primitive": {
@@ -6136,30 +7135,32 @@
}
},
"node_modules/eslint": {
- "version": "9.20.1",
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.0",
- "@eslint/core": "^0.11.0",
- "@eslint/eslintrc": "^3.2.0",
- "@eslint/js": "9.20.0",
- "@eslint/plugin-kit": "^0.2.5",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.1",
+ "@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -6212,24 +7213,25 @@
}
},
"node_modules/eslint-import-resolver-typescript": {
- "version": "3.7.0",
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"@nolyfill/is-core-module": "1.0.39",
- "debug": "^4.3.7",
- "enhanced-resolve": "^5.15.0",
- "fast-glob": "^3.3.2",
- "get-tsconfig": "^4.7.5",
- "is-bun-module": "^1.0.2",
- "is-glob": "^4.0.3",
- "stable-hash": "^0.0.4"
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
- "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
},
"peerDependencies": {
"eslint": "*",
@@ -6246,7 +7248,9 @@
}
},
"node_modules/eslint-module-utils": {
- "version": "2.12.0",
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6263,6 +7267,8 @@
},
"node_modules/eslint-module-utils/node_modules/debug": {
"version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6270,28 +7276,30 @@
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.31.0",
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rtsao/scc": "^1.1.0",
- "array-includes": "^3.1.8",
- "array.prototype.findlastindex": "^1.2.5",
- "array.prototype.flat": "^1.3.2",
- "array.prototype.flatmap": "^1.3.2",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
"debug": "^3.2.7",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.12.0",
+ "eslint-module-utils": "^2.12.1",
"hasown": "^2.0.2",
- "is-core-module": "^2.15.1",
+ "is-core-module": "^2.16.1",
"is-glob": "^4.0.3",
"minimatch": "^3.1.2",
"object.fromentries": "^2.0.8",
"object.groupby": "^1.0.3",
- "object.values": "^1.2.0",
+ "object.values": "^1.2.1",
"semver": "^6.3.1",
- "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimend": "^1.0.9",
"tsconfig-paths": "^3.15.0"
},
"engines": {
@@ -6331,8 +7339,40 @@
"strip-bom": "^3.0.0"
}
},
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
"node_modules/eslint-plugin-react": {
- "version": "7.37.4",
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6346,7 +7386,7 @@
"hasown": "^2.0.2",
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.1.2",
- "object.entries": "^1.1.8",
+ "object.entries": "^1.1.9",
"object.fromentries": "^2.0.8",
"object.values": "^1.2.1",
"prop-types": "^15.8.1",
@@ -6363,7 +7403,9 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "5.1.0",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6389,6 +7431,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/eslint-plugin-svg-jsx": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-svg-jsx/-/eslint-plugin-svg-jsx-1.3.0.tgz",
+ "integrity": "sha512-Aqpad1wZSfkKIazxw/WaTBkKyYPDB+8hBgfsor+VSwoyGMfaA6Tz7aQqv9NoA2cxvaMUrjnoVnfDk/ETAnNEUA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=5.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8"
+ }
+ },
"node_modules/eslint-scope": {
"version": "5.1.1",
"dev": true,
@@ -6410,11 +7465,13 @@
}
},
"node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -6471,16 +7528,10 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/eslint/node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
"node_modules/eslint/node_modules/ajv": {
"version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6495,7 +7546,9 @@
}
},
"node_modules/eslint/node_modules/eslint-scope": {
- "version": "8.2.0",
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -6509,17 +7562,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/eslint/node_modules/glob-parent": {
"version": "6.0.2",
"dev": true,
@@ -6533,40 +7575,22 @@
},
"node_modules/eslint/node_modules/json-schema-traverse": {
"version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/espree": {
- "version": "10.3.0",
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.14.0",
+ "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree/node_modules/acorn": {
- "version": "8.14.0",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
+ "eslint-visitor-keys": "^4.2.1"
},
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/espree/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -6574,19 +7598,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true,
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/esquery": {
"version": "1.6.0",
"dev": true,
@@ -6633,6 +7644,13 @@
"node": ">=6"
}
},
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/events": {
"version": "3.3.0",
"dev": true,
@@ -6641,11 +7659,89 @@
"node": ">=0.8.x"
}
},
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/execa/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/execa/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/execa/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/execa/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/external-editor": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
@@ -6657,9 +7753,8 @@
},
"node_modules/external-editor/node_modules/iconv-lite": {
"version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
@@ -6669,9 +7764,8 @@
},
"node_modules/extract-zip": {
"version": "1.7.0",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
- "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"concat-stream": "^1.6.2",
"debug": "^2.6.9",
@@ -6684,18 +7778,16 @@
},
"node_modules/extract-zip/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/extract-zip/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
@@ -6708,8 +7800,6 @@
},
"node_modules/fast-glob": {
"version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6725,6 +7815,8 @@
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
@@ -6733,6 +7825,23 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
"dev": true,
@@ -6751,18 +7860,16 @@
},
"node_modules/fd-slicer": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/figures": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
- "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"escape-string-regexp": "^1.0.5"
},
@@ -6775,9 +7882,8 @@
},
"node_modules/figures/node_modules/escape-string-regexp": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.8.0"
}
@@ -6845,13 +7951,13 @@
},
"node_modules/flatted": {
"version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
- "version": "1.15.6",
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
@@ -6869,19 +7975,29 @@
}
},
"node_modules/for-each": {
- "version": "0.3.3",
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-callable": "^1.1.3"
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/foreground-child": {
- "version": "3.3.0",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "cross-spawn": "^7.0.0",
+ "cross-spawn": "^7.0.6",
"signal-exit": "^4.0.1"
},
"engines": {
@@ -6892,11 +8008,15 @@
}
},
"node_modules/form-data": {
- "version": "4.0.0",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -6940,9 +8060,8 @@
},
"node_modules/fs.realpath": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.2",
@@ -6950,6 +8069,7 @@
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -7002,24 +8122,37 @@
},
"node_modules/get-caller-file": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
+ "license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
- "node_modules/get-intrinsic": {
- "version": "1.2.7",
+ "node_modules/get-east-asian-width": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
+ "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
+ "call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
- "es-object-atoms": "^1.0.0",
+ "es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
- "get-proto": "^1.0.0",
+ "get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
@@ -7034,7 +8167,6 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
- "dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -7046,9 +8178,8 @@
},
"node_modules/get-stream": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
@@ -7076,7 +8207,9 @@
}
},
"node_modules/get-tsconfig": {
- "version": "4.7.6",
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7095,13 +8228,16 @@
}
},
"node_modules/glob": {
- "version": "11.0.1",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
+ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^4.0.1",
- "minimatch": "^10.0.0",
+ "foreground-child": "^3.3.1",
+ "jackspeak": "^4.1.1",
+ "minimatch": "^10.1.1",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^2.0.0"
@@ -7129,32 +8265,35 @@
},
"node_modules/glob-to-regexp": {
"version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true,
"license": "BSD-2-Clause"
},
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "2.0.1",
+ "node_modules/glob/node_modules/@isaacs/cliui": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",
+ "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/glob/node_modules/jackspeak": {
- "version": "4.0.1",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz",
+ "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
- "@isaacs/cliui": "^8.0.2"
+ "@isaacs/cliui": "^9.0.0"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/glob/node_modules/lru-cache": {
@@ -7166,11 +8305,13 @@
}
},
"node_modules/glob/node_modules/minimatch": {
- "version": "10.0.1",
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz",
+ "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^5.0.2"
},
"engines": {
"node": "20 || >=22"
@@ -7204,8 +8345,6 @@
},
"node_modules/global-modules": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
- "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7217,8 +8356,6 @@
},
"node_modules/global-prefix": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
- "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7232,8 +8369,6 @@
},
"node_modules/global-prefix/node_modules/which": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -7271,8 +8406,6 @@
},
"node_modules/globby": {
"version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7292,13 +8425,13 @@
},
"node_modules/globjoin": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
- "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
"dev": true,
"license": "MIT"
},
"node_modules/good-listener": {
"version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"license": "MIT",
"dependencies": {
"delegate": "^3.1.2"
@@ -7306,7 +8439,6 @@
},
"node_modules/gopd": {
"version": "1.2.0",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -7317,9 +8449,8 @@
},
"node_modules/got": {
"version": "11.8.6",
- "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
- "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
@@ -7346,16 +8477,11 @@
"license": "ISC"
},
"node_modules/gradient-parser": {
- "version": "0.1.5",
+ "version": "1.0.2",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "dev": true,
- "license": "MIT"
- },
"node_modules/has-bigints": {
"version": "1.1.0",
"dev": true,
@@ -7402,7 +8528,6 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -7413,7 +8538,6 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
- "dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -7459,16 +8583,12 @@
"license": "MIT"
},
"node_modules/hookified": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.8.2.tgz",
- "integrity": "sha512-5nZbBNP44sFCDjSoB//0N7m508APCgbQ4mGGo1KJGBYyCKNHfry1Pvd0JVHZIxjdnqn8nFRBAN/eFB6Rk/4w5w==",
+ "version": "1.9.0",
"dev": true,
"license": "MIT"
},
"node_modules/html-tags": {
"version": "3.3.1",
- "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz",
- "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7480,15 +8600,13 @@
},
"node_modules/http-cache-semantics": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
- "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
- "dev": true
+ "dev": true,
+ "license": "BSD-2-Clause"
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
- "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
@@ -7497,6 +8615,16 @@
"node": ">=10.19.0"
}
},
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.6.3",
"license": "MIT",
@@ -7592,10 +8720,8 @@
},
"node_modules/inflight": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
+ "license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -7608,16 +8734,13 @@
},
"node_modules/ini": {
"version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true,
"license": "ISC"
},
"node_modules/inquirer": {
"version": "7.3.3",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
- "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-escapes": "^4.2.1",
"chalk": "^4.1.0",
@@ -7726,15 +8849,19 @@
}
},
"node_modules/is-bun-module": {
- "version": "1.1.0",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "semver": "^7.6.3"
+ "semver": "^7.7.1"
}
},
"node_modules/is-bun-module/node_modules/semver": {
- "version": "7.6.3",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -7756,7 +8883,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.15.1",
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -7823,8 +8952,6 @@
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7861,9 +8988,8 @@
},
"node_modules/is-interactive": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
- "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -7879,6 +9005,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"dev": true,
@@ -8025,11 +9164,13 @@
}
},
"node_modules/is-weakref": {
- "version": "1.1.0",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.2"
+ "call-bound": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@@ -8063,15 +9204,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/isnumeric": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/isnumeric/-/isnumeric-0.2.0.tgz",
- "integrity": "sha512-uSJoAwnN1eCKDFKi8hL3UCYJSkQv+NwhKzhevUPIn/QZ8ILO21f+wQnlZHU0eh1rsLO1gI4w/HQdeOSTKwlqMg==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.x"
- }
- },
"node_modules/isobject": {
"version": "3.0.1",
"dev": true,
@@ -8128,6 +9260,8 @@
},
"node_modules/jest-worker": {
"version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8141,6 +9275,8 @@
},
"node_modules/jest-worker/node_modules/supports-color": {
"version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8166,7 +9302,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8248,11 +9386,29 @@
},
"node_modules/known-css-properties": {
"version": "0.36.0",
- "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.36.0.tgz",
- "integrity": "sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==",
"dev": true,
"license": "MIT"
},
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/lazystream": {
"version": "1.0.1",
"dev": true,
@@ -8277,7 +9433,9 @@
}
},
"node_modules/lilconfig": {
- "version": "3.1.2",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8291,12 +9449,87 @@
"version": "1.2.4",
"license": "MIT"
},
+ "node_modules/lint-staged": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz",
+ "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.4.1",
+ "commander": "^13.1.0",
+ "debug": "^4.4.0",
+ "execa": "^8.0.1",
+ "lilconfig": "^3.1.3",
+ "listr2": "^8.2.5",
+ "micromatch": "^4.0.8",
+ "pidtree": "^0.6.0",
+ "string-argv": "^0.3.2",
+ "yaml": "^2.7.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/lint-staged/node_modules/commander": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz",
+ "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/loader-runner": {
- "version": "4.3.0",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
+ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
"node_modules/locate-path": {
@@ -8314,7 +9547,9 @@
}
},
"node_modules/lodash": {
- "version": "4.17.21",
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true,
"license": "MIT"
},
@@ -8335,8 +9570,6 @@
},
"node_modules/lodash.truncate": {
"version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
- "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
"dev": true,
"license": "MIT"
},
@@ -8347,9 +9580,8 @@
},
"node_modules/log-symbols": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
- "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chalk": "^2.4.2"
},
@@ -8359,9 +9591,8 @@
},
"node_modules/log-symbols/node_modules/ansi-styles": {
"version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -8371,9 +9602,8 @@
},
"node_modules/log-symbols/node_modules/chalk": {
"version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -8385,47 +9615,202 @@
},
"node_modules/log-symbols/node_modules/color-convert": {
"version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
- "node_modules/log-symbols/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "node_modules/log-symbols/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
"engines": {
- "node": ">=0.8.0"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/log-symbols/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
"engines": {
- "node": ">=4"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
- "node_modules/log-symbols/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "has-flag": "^3.0.0"
+ "ansi-regex": "^6.0.1"
},
"engines": {
- "node": ">=4"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/loose-envify": {
@@ -8447,9 +9832,8 @@
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
- "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -8469,7 +9853,6 @@
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -8477,8 +9860,6 @@
},
"node_modules/mathml-tag-names": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
- "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -8501,8 +9882,6 @@
},
"node_modules/meow": {
"version": "13.2.0",
- "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
- "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8556,18 +9935,29 @@
},
"node_modules/mimic-fn": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mimic-response": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=4"
}
@@ -8612,9 +10002,8 @@
},
"node_modules/mkdirp": {
"version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"minimist": "^1.2.6"
},
@@ -8624,13 +10013,17 @@
},
"node_modules/moment": {
"version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
- "version": "0.5.45",
+ "version": "0.5.48",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
+ "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==",
"license": "MIT",
"dependencies": {
"moment": "^2.29.4"
@@ -8641,19 +10034,18 @@
},
"node_modules/mousetrap": {
"version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
+ "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==",
"license": "Apache-2.0 WITH LLVM-exception"
},
"node_modules/ms": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/mute-stream": {
"version": "0.0.8",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
- "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
},
"node_modules/nanoid": {
"version": "3.3.8",
@@ -8672,6 +10064,22 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"dev": true,
@@ -8697,7 +10105,9 @@
"optional": true
},
"node_modules/node-releases": {
- "version": "2.0.18",
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true,
"license": "MIT"
},
@@ -8719,9 +10129,8 @@
},
"node_modules/normalize-url": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
- "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -8729,6 +10138,35 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/nth-check": {
"version": "2.1.1",
"dev": true,
@@ -8748,7 +10186,9 @@
}
},
"node_modules/object-inspect": {
- "version": "1.13.3",
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8786,13 +10226,16 @@
}
},
"node_modules/object.entries": {
- "version": "1.1.8",
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
"define-properties": "^1.2.1",
- "es-object-atoms": "^1.0.0"
+ "es-object-atoms": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
@@ -8847,18 +10290,16 @@
},
"node_modules/once": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/onetime": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"mimic-fn": "^2.1.0"
},
@@ -8887,9 +10328,8 @@
},
"node_modules/ora": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz",
- "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chalk": "^3.0.0",
"cli-cursor": "^3.1.0",
@@ -8909,9 +10349,8 @@
},
"node_modules/ora/node_modules/chalk": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -8920,15 +10359,6 @@
"node": ">=8"
}
},
- "node_modules/os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/own-keys": {
"version": "1.0.1",
"dev": true,
@@ -8947,9 +10377,8 @@
},
"node_modules/p-cancelable": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
- "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -9055,9 +10484,8 @@
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -9115,9 +10543,8 @@
},
"node_modules/pend": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/php-parser": {
"version": "3.2.2",
@@ -9138,6 +10565,19 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/pkg-dir": {
"version": "4.2.0",
"dev": true,
@@ -9198,12 +10638,13 @@
}
},
"node_modules/playwright": {
- "version": "1.55.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz",
- "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==",
+ "version": "1.58.2",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
+ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.55.0"
+ "playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
@@ -9216,10 +10657,11 @@
}
},
"node_modules/playwright-core": {
- "version": "1.55.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz",
- "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==",
+ "version": "1.58.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
+ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"dev": true,
+ "license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
@@ -9237,8 +10679,6 @@
},
"node_modules/postcss": {
"version": "8.5.3",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
- "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
@@ -9279,128 +10719,6 @@
"postcss": "^8.4.38"
}
},
- "node_modules/postcss-color-hsl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/postcss-color-hsl/-/postcss-color-hsl-2.0.0.tgz",
- "integrity": "sha512-4DNpOj3NWejHtjV4mLxf+rmE1KA+IKDJH8QSThgJOrjGFuiqOPxkFSZX1RQJ+XQISZD3MW/JDaZoNnmxS9pSBQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "postcss": "^6.0.1",
- "postcss-value-parser": "^3.3.0",
- "units-css": "^0.4.0"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/postcss-color-hsl/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/postcss-value-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/postcss-color-hsl/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postcss-color-hsl/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/postcss-colormin": {
"version": "7.0.2",
"dev": true,
@@ -9600,8 +10918,6 @@
},
"node_modules/postcss-media-query-parser": {
"version": "0.2.3",
- "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
- "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==",
"dev": true,
"license": "MIT"
},
@@ -9923,15 +11239,11 @@
},
"node_modules/postcss-resolve-nested-selector": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz",
- "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==",
"dev": true,
"license": "MIT"
},
"node_modules/postcss-safe-parser": {
"version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
- "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
"dev": true,
"funding": [
{
@@ -9957,8 +11269,6 @@
},
"node_modules/postcss-scss": {
"version": "4.0.9",
- "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz",
- "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==",
"dev": true,
"funding": [
{
@@ -10038,8 +11348,6 @@
},
"node_modules/prismjs": {
"version": "1.30.0",
- "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
- "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -10077,9 +11385,8 @@
},
"node_modules/pump": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@@ -10087,6 +11394,8 @@
},
"node_modules/punycode": {
"version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10119,9 +11428,8 @@
},
"node_modules/quick-lru": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
- "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -10131,6 +11439,8 @@
},
"node_modules/randombytes": {
"version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10176,8 +11486,6 @@
},
"node_modules/react-is": {
"version": "19.0.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
- "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==",
"dev": true,
"license": "MIT"
},
@@ -10246,14 +11554,6 @@
"minimatch": "^5.1.0"
}
},
- "node_modules/readdir-glob/node_modules/brace-expansion": {
- "version": "2.0.1",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/readdir-glob/node_modules/minimatch": {
"version": "5.1.6",
"dev": true,
@@ -10331,10 +11631,6 @@
"node": ">=4"
}
},
- "node_modules/regenerator-runtime": {
- "version": "0.14.0",
- "license": "MIT"
- },
"node_modules/regenerator-transform": {
"version": "0.15.2",
"dev": true,
@@ -10344,13 +11640,17 @@
}
},
"node_modules/regexp.prototype.flags": {
- "version": "1.5.3",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
+ "call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
"set-function-name": "^2.0.2"
},
"engines": {
@@ -10394,6 +11694,8 @@
},
"node_modules/rememo": {
"version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz",
+ "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==",
"license": "MIT"
},
"node_modules/remove-accents": {
@@ -10408,9 +11710,8 @@
},
"node_modules/require-directory": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -10440,9 +11741,8 @@
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
- "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/resolve-cwd": {
"version": "3.0.0",
@@ -10472,6 +11772,8 @@
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
@@ -10480,9 +11782,8 @@
},
"node_modules/responselike": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
- "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"lowercase-keys": "^2.0.0"
},
@@ -10492,9 +11793,8 @@
},
"node_modules/restore-cursor": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
@@ -10505,9 +11805,8 @@
},
"node_modules/restore-cursor/node_modules/signal-exit": {
"version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
},
"node_modules/reusify": {
"version": "1.0.4",
@@ -10518,12 +11817,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -10536,10 +11840,8 @@
},
"node_modules/rimraf/node_modules/glob": {
"version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -10557,9 +11859,8 @@
},
"node_modules/run-async": {
"version": "2.4.1",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
- "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -10594,9 +11895,8 @@
},
"node_modules/rxjs": {
"version": "6.6.7",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
- "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"tslib": "^1.9.0"
},
@@ -10606,9 +11906,8 @@
},
"node_modules/rxjs/node_modules/tslib": {
"version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
+ "dev": true,
+ "license": "0BSD"
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
@@ -10747,7 +12046,9 @@
}
},
"node_modules/schema-utils": {
- "version": "4.2.0",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10757,7 +12058,7 @@
"ajv-keywords": "^5.1.0"
},
"engines": {
- "node": ">= 12.13.0"
+ "node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
@@ -10766,6 +12067,8 @@
},
"node_modules/select": {
"version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==",
"license": "MIT"
},
"node_modules/semver": {
@@ -10787,6 +12090,8 @@
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -10936,8 +12241,6 @@
},
"node_modules/signal-exit": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
@@ -10949,9 +12252,8 @@
},
"node_modules/simple-git": {
"version": "3.28.0",
- "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz",
- "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
"@kwsites/promise-deferred": "^1.1.1",
@@ -10964,8 +12266,6 @@
},
"node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10974,8 +12274,6 @@
},
"node_modules/slice-ansi": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
- "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11015,6 +12313,8 @@
},
"node_modules/source-map-support": {
"version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11024,6 +12324,8 @@
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -11035,10 +12337,26 @@
"license": "BSD-3-Clause"
},
"node_modules/stable-hash": {
- "version": "0.0.4",
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
"dev": true,
"license": "MIT"
},
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/streamx": {
"version": "2.15.4",
"dev": true,
@@ -11059,12 +12377,20 @@
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"dev": true,
- "license": "MIT"
+ "license": "MIT"
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
},
"node_modules/string-width": {
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11097,11 +12423,24 @@
},
"node_modules/string-width/node_modules/emoji-regex": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/string.prototype.matchall": {
"version": "4.0.12",
"dev": true,
@@ -11221,6 +12560,19 @@
"node": ">=4"
}
},
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"dev": true,
@@ -11253,8 +12605,6 @@
},
"node_modules/style-search": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
- "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==",
"dev": true,
"license": "ISC"
},
@@ -11275,8 +12625,6 @@
},
"node_modules/stylelint": {
"version": "16.19.1",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.19.1.tgz",
- "integrity": "sha512-C1SlPZNMKl+d/C867ZdCRthrS+6KuZ3AoGW113RZCOL0M8xOGpgx7G70wq7lFvqvm4dcfdGFVLB/mNaLFChRKw==",
"dev": true,
"funding": [
{
@@ -11338,8 +12686,6 @@
},
"node_modules/stylelint-config-recommended": {
"version": "16.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz",
- "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==",
"dev": true,
"funding": [
{
@@ -11361,8 +12707,6 @@
},
"node_modules/stylelint-config-recommended-scss": {
"version": "14.1.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz",
- "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11385,8 +12729,6 @@
},
"node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": {
"version": "14.0.1",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz",
- "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==",
"dev": true,
"funding": [
{
@@ -11408,8 +12750,6 @@
},
"node_modules/stylelint-config-standard": {
"version": "38.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-38.0.0.tgz",
- "integrity": "sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==",
"dev": true,
"funding": [
{
@@ -11434,8 +12774,6 @@
},
"node_modules/stylelint-config-standard-scss": {
"version": "14.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-14.0.0.tgz",
- "integrity": "sha512-6Pa26D9mHyi4LauJ83ls3ELqCglU6VfCXchovbEqQUiEkezvKdv6VgsIoMy58i00c854wVmOw0k8W5FTpuaVqg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11457,8 +12795,6 @@
},
"node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-recommended": {
"version": "14.0.1",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz",
- "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==",
"dev": true,
"funding": [
{
@@ -11480,8 +12816,6 @@
},
"node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-standard": {
"version": "36.0.1",
- "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz",
- "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==",
"dev": true,
"funding": [
{
@@ -11506,8 +12840,6 @@
},
"node_modules/stylelint-scss": {
"version": "6.12.0",
- "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.12.0.tgz",
- "integrity": "sha512-U7CKhi1YNkM1pXUXl/GMUXi8xKdhl4Ayxdyceie1nZ1XNIdaUgMV6OArpooWcDzEggwgYD0HP/xIgVJo9a655w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11529,8 +12861,6 @@
},
"node_modules/stylelint-scss/node_modules/css-tree": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
- "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11543,22 +12873,16 @@
},
"node_modules/stylelint-scss/node_modules/css-tree/node_modules/mdn-data": {
"version": "2.12.2",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
- "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/stylelint-scss/node_modules/mdn-data": {
"version": "2.21.0",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.21.0.tgz",
- "integrity": "sha512-+ZKPQezM5vYJIkCxaC+4DTnRrVZR1CgsKLu5zsQERQx6Tea8Y+wMx5A24rq8A8NepCeatIQufVAekKNgiBMsGQ==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/stylelint-scss/node_modules/postcss-selector-parser": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
- "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11571,8 +12895,6 @@
},
"node_modules/stylelint-use-logical": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/stylelint-use-logical/-/stylelint-use-logical-2.1.2.tgz",
- "integrity": "sha512-4ffvPNk/swH4KS3izExWuzQOuzLmi0gb0uOhvxWJ20vDA5W5xKCjcHHtLoAj1kKvTIX6eGIN5xGtaVin9PD0wg==",
"dev": true,
"license": "CC0-1.0",
"engines": {
@@ -11584,8 +12906,6 @@
},
"node_modules/stylelint/node_modules/@csstools/selector-specificity": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
- "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
"dev": true,
"funding": [
{
@@ -11607,15 +12927,11 @@
},
"node_modules/stylelint/node_modules/balanced-match": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
- "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
"dev": true,
"license": "MIT"
},
"node_modules/stylelint/node_modules/cosmiconfig": {
"version": "9.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
- "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11641,8 +12957,6 @@
},
"node_modules/stylelint/node_modules/css-tree": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
- "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11654,31 +12968,25 @@
}
},
"node_modules/stylelint/node_modules/file-entry-cache": {
- "version": "10.0.8",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.8.tgz",
- "integrity": "sha512-FGXHpfmI4XyzbLd3HQ8cbUcsFGohJpZtmQRHr8z8FxxtCe2PcpgIlVLwIgunqjvRmXypBETvwhV4ptJizA+Y1Q==",
+ "version": "10.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
- "flat-cache": "^6.1.8"
+ "flat-cache": "^6.1.9"
}
},
"node_modules/stylelint/node_modules/flat-cache": {
- "version": "6.1.8",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.8.tgz",
- "integrity": "sha512-R6MaD3nrJAtO7C3QOuS79ficm2pEAy++TgEUD8ii1LVlbcgZ9DtASLkt9B+RZSFCzm7QHDMlXPsqqB6W2Pfr1Q==",
+ "version": "6.1.9",
"dev": true,
"license": "MIT",
"dependencies": {
- "cacheable": "^1.8.9",
+ "cacheable": "^1.9.0",
"flatted": "^3.3.3",
- "hookified": "^1.8.1"
+ "hookified": "^1.8.2"
}
},
"node_modules/stylelint/node_modules/ignore": {
"version": "7.0.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
- "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11687,15 +12995,11 @@
},
"node_modules/stylelint/node_modules/mdn-data": {
"version": "2.12.2",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
- "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/stylelint/node_modules/postcss-selector-parser": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
- "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11708,8 +13012,6 @@
},
"node_modules/stylelint/node_modules/resolve-from": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11733,8 +13035,6 @@
},
"node_modules/supports-hyperlinks": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
- "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11760,8 +13060,6 @@
},
"node_modules/svg-tags": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
- "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
"dev": true
},
"node_modules/svgo": {
@@ -11790,8 +13088,6 @@
},
"node_modules/table": {
"version": "6.9.0",
- "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz",
- "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -11813,11 +13109,17 @@
}
},
"node_modules/tapable": {
- "version": "2.2.1",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
"node_modules/tar-stream": {
@@ -11832,9 +13134,8 @@
},
"node_modules/terminal-link": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-escapes": "^4.2.1",
"supports-hyperlinks": "^2.0.0"
@@ -11848,9 +13149,8 @@
},
"node_modules/terminal-link/node_modules/supports-hyperlinks": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
- "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0",
"supports-color": "^7.0.0"
@@ -11860,12 +13160,14 @@
}
},
"node_modules/terser": {
- "version": "5.31.6",
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
+ "acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
@@ -11877,15 +13179,17 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.10",
+ "version": "5.3.16",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
+ "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.20",
+ "@jridgewell/trace-mapping": "^0.3.25",
"jest-worker": "^27.4.5",
- "schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.1",
- "terser": "^5.26.0"
+ "schema-utils": "^4.3.0",
+ "serialize-javascript": "^6.0.2",
+ "terser": "^5.31.1"
},
"engines": {
"node": ">= 10.13.0"
@@ -11909,76 +13213,80 @@
}
}
},
- "node_modules/terser-webpack-plugin/node_modules/ajv": {
- "version": "6.12.6",
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
},
"funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
+ "url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
- "version": "3.5.2",
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
"peerDependencies": {
- "ajv": "^6.9.1"
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
- "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
- "version": "3.3.0",
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- },
"engines": {
- "node": ">= 10.13.0"
+ "node": ">=12"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "dev": true
- },
- "node_modules/tiny-emitter": {
- "version": "2.1.0",
- "license": "MIT"
- },
"node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"dev": true,
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
+ "license": "MIT",
"engines": {
- "node": ">=0.6.0"
+ "node": ">=14.14"
}
},
"node_modules/to-regex-range": {
@@ -11993,7 +13301,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "2.0.1",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -12005,8 +13315,6 @@
},
"node_modules/ts-loader": {
"version": "9.5.2",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
- "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12025,9 +13333,7 @@
}
},
"node_modules/ts-loader/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.1",
"dev": true,
"license": "ISC",
"bin": {
@@ -12039,8 +13345,6 @@
},
"node_modules/ts-loader/node_modules/source-map": {
"version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -12119,9 +13423,8 @@
},
"node_modules/type-fest": {
"version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"dev": true,
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
@@ -12201,9 +13504,8 @@
},
"node_modules/typedarray": {
"version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/typescript": {
"version": "5.7.3",
@@ -12218,13 +13520,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.24.0",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz",
+ "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.24.0",
- "@typescript-eslint/parser": "8.24.0",
- "@typescript-eslint/utils": "8.24.0"
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "@typescript-eslint/typescript-estree": "8.56.0",
+ "@typescript-eslint/utils": "8.56.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -12234,8 +13539,8 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/unbox-primitive": {
@@ -12296,19 +13601,45 @@
"node": ">=4"
}
},
- "node_modules/units-css": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/units-css/-/units-css-0.4.0.tgz",
- "integrity": "sha512-WijzYC+chwzg2D6HmNGUSzPAgFRJfuxVyG9oiY28Ei5E+g6fHoPkhXUr5GV+5hE/RTHZNd9SuX2KLioYHdttoA==",
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
"dev": true,
+ "hasInstallScript": true,
"license": "MIT",
"dependencies": {
- "isnumeric": "^0.2.0",
- "viewport-dimensions": "^0.2.0"
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+ "@unrs/resolver-binding-android-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-x64": "1.11.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.1",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
@@ -12327,7 +13658,7 @@
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
- "picocolors": "^1.1.0"
+ "picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -12352,6 +13683,8 @@
},
"node_modules/uri-js": {
"version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -12372,6 +13705,8 @@
},
"node_modules/use-memo-one": {
"version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
@@ -12405,19 +13740,14 @@
"dev": true,
"license": "MIT"
},
- "node_modules/viewport-dimensions": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz",
- "integrity": "sha512-94JqlKxEP4m7WO+N3rm4tFRGXZmXXwSPQCoV+EPxDnn8YAGiLU3T+Ha1imLreAjXsHl0K+ELnIqv64i1XZHLFQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/w3c-keyname": {
"version": "2.2.8",
"license": "MIT"
},
"node_modules/watchpack": {
- "version": "2.4.2",
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
+ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12430,41 +13760,44 @@
},
"node_modules/wcwidth": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
- "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"defaults": "^1.0.3"
}
},
"node_modules/webpack": {
- "version": "5.97.1",
+ "version": "5.105.2",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz",
+ "integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
- "@types/estree": "^1.0.6",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
- "acorn": "^8.14.0",
- "browserslist": "^4.24.0",
+ "acorn": "^8.15.0",
+ "acorn-import-phases": "^1.0.3",
+ "browserslist": "^4.28.1",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.17.1",
- "es-module-lexer": "^1.2.1",
+ "enhanced-resolve": "^5.19.0",
+ "es-module-lexer": "^2.0.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
- "loader-runner": "^4.2.0",
+ "loader-runner": "^4.3.1",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
- "schema-utils": "^3.2.0",
- "tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.10",
- "watchpack": "^2.4.1",
- "webpack-sources": "^3.2.3"
+ "schema-utils": "^4.3.3",
+ "tapable": "^2.3.0",
+ "terser-webpack-plugin": "^5.3.16",
+ "watchpack": "^2.5.1",
+ "webpack-sources": "^3.3.3"
},
"bin": {
"webpack": "bin/webpack.js"
@@ -12545,11 +13878,11 @@
}
},
"node_modules/webpack-remove-empty-scripts": {
- "version": "1.0.4",
+ "version": "1.1.1",
"dev": true,
"license": "ISC",
"dependencies": {
- "ansis": "1.5.2"
+ "ansis": "4.0.0-node10"
},
"engines": {
"node": ">=12.14"
@@ -12563,69 +13896,15 @@
}
},
"node_modules/webpack-sources": {
- "version": "3.2.3",
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz",
+ "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
- "node_modules/webpack/node_modules/acorn": {
- "version": "8.14.0",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/webpack/node_modules/ajv": {
- "version": "6.12.6",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/webpack/node_modules/ajv-keywords": {
- "version": "3.5.2",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "ajv": "^6.9.1"
- }
- },
- "node_modules/webpack/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/webpack/node_modules/schema-utils": {
- "version": "3.3.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- },
- "engines": {
- "node": ">= 10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"dev": true,
@@ -12702,14 +13981,17 @@
}
},
"node_modules/which-typed-array": {
- "version": "1.1.18",
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
- "call-bound": "^1.0.3",
- "for-each": "^0.3.3",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
},
@@ -12734,17 +14016,18 @@
}
},
"node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
@@ -12767,16 +14050,80 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
},
"node_modules/write-file-atomic": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
- "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -12787,15 +14134,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/yallist": {
"version": "3.1.1",
"dev": true,
@@ -12803,9 +14141,8 @@
},
"node_modules/yaml": {
"version": "2.8.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
- "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"dev": true,
+ "license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
@@ -12813,38 +14150,10 @@
"node": ">= 14.6"
}
},
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/yauzl": {
"version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
diff --git a/package.json b/package.json
index 1d1404791..417d03066 100644
--- a/package.json
+++ b/package.json
@@ -2,30 +2,37 @@
"name": "code-snippets",
"description": "Manage code snippets running on a WordPress-powered site through a graphical interface.",
"homepage": "https://codesnippets.pro",
- "version": "3.9.6",
+ "version": "3.10.0-beta.1",
"main": "src/dist/edit.js",
"directories": {
"test": "tests"
},
"scripts": {
- "test:playwright": "playwright test -c tests/playwright/playwright.config.ts",
+ "prepare": "git config core.hooksPath .githooks || true",
+ "test:php": "WP_TESTS_DIR=./.wp-tests-lib src/vendor/bin/phpunit -c phpunit.xml",
+ "test:php:watch": "npm run test:php -- --testdox",
+ "test:playwright": "playwright test -c config/playwright/playwright.config.ts",
"test:playwright:debug": "npm run test:playwright -- --debug",
"test:playwright:ui": "npm run test:playwright -- --ui",
"prepare-environment:ci": "npm ci",
"wp-env:start": "wp-env start",
"wp-env:stop": "wp-env stop",
"wp-env:clean": "wp-env clean all",
- "test:setup:playwright": "wp-env run cli wp plugin activate code-snippets",
+ "test:setup:php": "ts-node scripts/test-setup-phpunit.ts",
+ "test:setup:playwright": "ts-node scripts/test-setup-playwright.ts",
"build": "webpack",
"watch": "webpack --watch",
"bundle": "ts-node scripts/bundle.ts",
"lint": "npm run lint:styles && npm run lint:js && npm run lint:php",
+ "lint:fix": "npm run lint:styles:fix && npm run lint:js:fix && npm run lint:php:fix",
"lint:styles": "stylelint 'src/css/**/*.scss'",
"lint:styles:fix": "stylelint --fix 'src/css/**/*.scss'",
- "lint:js": "eslint",
- "lint:js:fix": "eslint --fix",
- "lint:php": "src/vendor/bin/phpcs -s --colors ./src/phpcs.xml",
- "lint:php:fix": "src/vendor/bin/phpcbf ./src/phpcs.xml",
+ "lint:js": "eslint .",
+ "lint:js:fix": "eslint . --fix",
+ "lint:php": "src/vendor/bin/phpcs -s --colors --standard=phpcs.xml src/php tests",
+ "lint:php:fix": "src/vendor/bin/phpcbf --standard=phpcs.xml src/php tests",
+ "lint:readme": "ts-node scripts/linters/lint-readme.ts",
+ "lint:changelog": "ts-node scripts/linters/lint-changelog.ts",
"version": "ts-node scripts/version.ts",
"version-dev": "npm version --git-tag-version=false --preid=dev",
"version-alpha": "npm version --git-tag-version=false --preid=alpha",
@@ -47,11 +54,12 @@
"dependencies": {
"@codemirror/fold": "^0.19.4",
"@wordpress/components": "^29.3.0",
+ "@wordpress/date": "^5.43.0",
"@wordpress/dom-ready": "^4.17.0",
"@wordpress/element": "^6.28.0",
"@wordpress/i18n": "^5.17.0",
"@wordpress/url": "^4.20.0",
- "axios": "^1.7.9",
+ "axios": "^1.13.5",
"classnames": "^2.5.1",
"codemirror": "^5.29",
"php-parser": "^3.2.2",
@@ -61,8 +69,10 @@
"react-select": "^5.10.0"
},
"devDependencies": {
- "@eslint/eslintrc": "^3.2.0",
+ "@axe-core/playwright": "^4.11.2",
+ "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.20.0",
+ "@playwright/test": "^1.48.0",
"@stylistic/eslint-plugin": "^3.1.0",
"@stylistic/stylelint-plugin": "^3.1.2",
"@tsconfig/node18": "^18.2.4",
@@ -78,6 +88,7 @@
"@typescript-eslint/eslint-plugin": "^8.24.0",
"@typescript-eslint/parser": "^8.24.0",
"@wordpress/babel-preset-default": "^8.17.0",
+ "@wordpress/env": "^9.0.0",
"archiver": "^7.0.1",
"autoprefixer": "^10.4.20",
"babel-loader": "^9.2.1",
@@ -87,14 +98,16 @@
"eslint": "^9.20.1",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-svg-jsx": "^1.3.0",
"eslint-webpack-plugin": "^4.2.0",
- "glob": "^11.0.1",
+ "glob": "^11.1.0",
"globals": "^15.14.0",
+ "lint-staged": "^15.5.2",
"mini-css-extract-plugin": "^2.9.2",
"postcss": "^8.5.2",
- "postcss-color-hsl": "^2.0.0",
"postcss-hexrgba": "^2.1.0",
"postcss-load-config": "^6.0.1",
"postcss-loader": "^8.1.1",
@@ -114,12 +127,30 @@
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"webpack-merge": "^6.0.1",
- "webpack-remove-empty-scripts": "^1.0.4",
- "@playwright/test": "^1.48.0",
- "@wordpress/env": "^9.0.0"
+ "webpack-remove-empty-scripts": "^1.0.4"
+ },
+ "lint-staged": {
+ "*.{js,ts,jsx,tsx}": "npm run lint:js:fix --",
+ "*.{css,scss}": "npm run lint:styles:fix --",
+ "*.php": "npm run lint:php:fix --",
+ "src/readme.txt": "ts-node scripts/linters/lint-readme.ts",
+ "CHANGELOG.md": "ts-node scripts/linters/lint-changelog.ts"
},
"overrides": {
- "eslint": "^9.20.1",
+ "@eslint/eslintrc": {
+ "ajv": "6.12.6"
+ },
+ "eslint": {
+ "ajv": "6.12.6"
+ },
+ "@babel/runtime": "7.28.6",
+ "js-yaml": "4.1.1",
+ "lodash": "4.17.23",
+ "brace-expansion": "1.1.12",
+ "form-data": "4.0.5",
+ "tmp": "0.2.5",
+ "eslint-visitor-keys": "4.2.1",
+ "@typescript-eslint/visitor-keys": "8.55.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
diff --git a/src/phpcs.xml b/phpcs.xml
similarity index 86%
rename from src/phpcs.xml
rename to phpcs.xml
index 3d7e8ca71..fb812c580 100644
--- a/src/phpcs.xml
+++ b/phpcs.xml
@@ -8,8 +8,10 @@
-
-
+
+
+
+
@@ -35,20 +37,14 @@
-
+
-
*\.php$
-
-
- class-*\.php$
-
-
diff --git a/phpunit.xml b/phpunit.xml
index 79b627333..aa5a63135 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -6,8 +6,8 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true">
-
- ./tests/
+
+ ./tests/unit/
diff --git a/scripts/composer-fix-autoload.php b/scripts/composer-fix-autoload.php
new file mode 100644
index 000000000..593089352
--- /dev/null
+++ b/scripts/composer-fix-autoload.php
@@ -0,0 +1,103 @@
+ strlen( $a );
+ }
+);
+
+$prefix_segments = explode( '\\', $prefix );
+$prefix_tail = preg_quote( (string) end( $prefix_segments ), '#' );
+
+$patterns = [];
+$replacements = [];
+
+foreach ( $roots as $root ) {
+ $segments = array_map(
+ static function ( $segment ) {
+ return preg_quote( $segment, '#' );
+ },
+ explode( '\\', $root )
+ );
+
+ // A whole leading backslash run then the root, when the preceding segment is not the prefix tail.
+ $patterns[] = '#(?isFile() || 'php' !== strtolower( $file->getExtension() ) ) {
+ continue;
+ }
+
+ if ( 0 === strpos( $file->getPathname(), $skip_prefix ) ) {
+ continue;
+ }
+
+ $contents = file_get_contents( $file->getPathname() );
+ $updated = preg_replace( $patterns, $replacements, $contents );
+
+ if ( null !== $updated && $updated !== $contents ) {
+ file_put_contents( $file->getPathname(), $updated );
+ }
+}
diff --git a/scripts/install-wp-tests.sh b/scripts/install-wp-tests.sh
new file mode 100755
index 000000000..8bd2f07be
--- /dev/null
+++ b/scripts/install-wp-tests.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+
+if [ $# -lt 3 ]; then
+ echo "usage: $0 [db-host] [wp-version] [skip-database-creation]"
+ exit 1
+fi
+
+DB_NAME=$1
+DB_USER=$2
+DB_PASS=$3
+DB_HOST=${4-localhost}
+WP_VERSION=${5-latest}
+SKIP_DB_CREATE=${6-false}
+
+TMPDIR=${TMPDIR-/tmp}
+TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
+WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
+WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
+
+download() {
+ if [ `which curl` ]; then
+ curl -s "$1" > "$2";
+ elif [ `which wget` ]; then
+ wget -nv -O "$2" "$1"
+ fi
+}
+
+if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
+ WP_TESTS_TAG="tags/$WP_VERSION"
+else
+ # http serves a single offer, whereas https serves multiple. we only want one
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
+ if [[ -z "$LATEST_VERSION" ]]; then
+ echo "Latest WordPress version could not be found"
+ exit 1
+ fi
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
+fi
+set -ex
+
+install_wp() {
+
+ if [ -d $WP_CORE_DIR ]; then
+ return;
+ fi
+
+ mkdir -p $WP_CORE_DIR
+
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
+ mkdir -p $TMPDIR/wordpress-trunk
+ rm -rf $TMPDIR/wordpress-trunk/*
+ svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress
+ mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR
+ else
+ if [ $WP_VERSION == 'latest' ]; then
+ local ARCHIVE_NAME='latest'
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
+ # https serves multiple offers, whereas http serves single.
+ download https://wordpress.org/wordpress-$WP_VERSION.tar.gz $TMPDIR/wordpress.tar.gz
+ ARCHIVE_NAME="wordpress-$WP_VERSION"
+ fi
+
+ if [ ! -f $TMPDIR/wordpress.tar.gz ]; then
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
+ fi
+ tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
+ fi
+
+ download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
+}
+
+install_test_suite() {
+ # portable in-place argument for both GNU sed and Mac OSX sed
+ if [[ $(uname -s) == 'Darwin' ]]; then
+ local ioption='-i.bak'
+ else
+ local ioption='-i'
+ fi
+
+ # set up testing suite if it doesn't yet exist
+ if [ ! -d $WP_TESTS_DIR ]; then
+ # set up testing suite
+ mkdir -p $WP_TESTS_DIR
+ rm -rf $WP_TESTS_DIR/{includes,data}
+ svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
+ svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
+ fi
+
+ if [ ! -f "$WP_TESTS_DIR/wp-tests-config.php" ]; then
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
+ # remove all forward slashes in the end
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
+ # Support both older (/src/) and current (/wordpress/) sample config templates.
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s:dirname( __FILE__ ) . '/wordpress/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s|^define( 'WP_TESTS_MULTISITE', false );|define( 'WP_TESTS_MULTISITE', true );|" "$WP_TESTS_DIR"/wp-tests-config.php
+ fi
+
+}
+
+recreate_db() {
+ shopt -s nocasematch
+ if [[ $1 =~ ^(y|yes)$ ]]
+ then
+ mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA
+ create_db
+ echo "Recreated the database ($DB_NAME)."
+ else
+ echo "Leaving the existing database ($DB_NAME) in place."
+ fi
+ shopt -u nocasematch
+}
+
+create_db() {
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
+}
+
+install_db() {
+
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
+ return 0
+ fi
+
+ # parse DB_HOST for port or socket references
+ local PARTS=(${DB_HOST//\:/ })
+ local DB_HOSTNAME=${PARTS[0]};
+ local DB_SOCK_OR_PORT=${PARTS[1]};
+ local EXTRA=""
+
+ if ! [ -z $DB_HOSTNAME ] ; then
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
+ elif ! [ -z $DB_HOSTNAME ] ; then
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
+ fi
+ fi
+
+ # create database
+ if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ]
+ then
+ echo "Reinstalling will delete the existing test database ($DB_NAME)"
+ read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB
+ recreate_db $DELETE_EXISTING_DB
+ else
+ create_db
+ fi
+}
+
+install_wp
+install_test_suite
+install_db
diff --git a/scripts/linters/lint-changelog.ts b/scripts/linters/lint-changelog.ts
new file mode 100644
index 000000000..aff1abe51
--- /dev/null
+++ b/scripts/linters/lint-changelog.ts
@@ -0,0 +1,303 @@
+/**
+ * Lint-changelog.ts
+ *
+ * Lints and auto-fixes CHANGELOG.md for formatting consistency.
+ *
+ * Rules enforced (based on current file conventions):
+ *
+ * File title
+ * - First line must be exactly: # Changelog
+ *
+ * Release headers
+ * - Format: ## [X.Y.Z] (YYYY-MM-DD)
+ * or: ## [X.Y.Z-modifier.N] (YYYY-MM-DD)
+ * - Non-bracketed versions (e.g. "## 3.6.5.1 (...)") are normalised to the
+ * bracketed form: ## [3.6.5.1] (YYYY-MM-DD)
+ * - Date format: YYYY-MM-DD (required)
+ *
+ * Section sub-headings
+ * - Allowed: ### Added, ### Changed, ### Fixed, ### Removed,
+ * ### Deprecated, ### Security
+ * - Bold variants (**Added**, __Added__) are promoted to ### headings
+ * - Casing is normalised to the canonical form above
+ *
+ * Lists
+ * - Items start with "* " (not "- ")
+ * - No trailing whitespace
+ *
+ * Spacing
+ * - Exactly 1 blank line before every ## heading (not before the first one)
+ * - Exactly 1 blank line after every # and ## headings
+ * - No blank lines after ### headings
+ * - Exactly 1 blank line before every ### heading (not immediately after ##)
+ * - No consecutive blank lines (max 1)
+ * - File ends with exactly one newline
+ */
+
+import { existsSync, readFileSync, writeFileSync } from 'fs'
+import { resolve } from 'path'
+
+/* ── helpers ─────────────────────────────────────────────────────────── */
+
+const KNOWN_CHANGE_TYPES = ['Added', 'Changed', 'Fixed', 'Removed', 'Deprecated', 'Security']
+
+const CLI_ARGS_START_INDEX = 2
+
+const trimTrailing = (lines: string[]): string[] =>
+ lines.map(l => l.trimEnd())
+
+const collapseBlankLines = (lines: string[]): string[] => {
+ const out: string[] = []
+ let prevBlank = false
+ for (const l of lines) {
+ const blank = '' === l.trim()
+ if (blank && prevBlank) {continue}
+ out.push(l)
+ prevBlank = blank
+ }
+ return out
+}
+
+/**
+ * Ensure exactly `n` blank lines appear immediately before every line matching
+ * `headingRe`. Lines matching `skipAfterRe` suppress spacing for the immediately
+ * following heading (used to avoid a blank line between a ## and its first ###).
+ */
+const normaliseBlanksBefore = (
+ lines: string[],
+ headingRe: RegExp,
+ n: number,
+ skipAfterRe?: RegExp
+): string[] => {
+ const out: string[] = []
+ let suppressNext = true // Suppress before very first heading
+
+ for (const line of lines) {
+ if (skipAfterRe?.test(line)) {
+ suppressNext = true
+ out.push(line)
+ continue
+ }
+
+ if (headingRe.test(line)) {
+ if (!suppressNext) {
+ while (0 < out.length && '' === out[out.length - 1].trim()) {out.pop()}
+ for (let b = 0; b < n; b += 1) {out.push('')}
+ }
+ out.push(line)
+ suppressNext = false
+ continue
+ }
+
+ if ('' !== line.trim()) {suppressNext = false}
+ out.push(line)
+ }
+ return out
+}
+
+/** Ensure exactly 1 blank line immediately after every line matching `headingRe`. */
+const normaliseBlankAfter = (lines: string[], headingRe: RegExp): string[] => {
+ const out: string[] = []
+ let i = 0
+ while (i < lines.length) {
+ const line = lines[i]
+ out.push(line)
+ if (headingRe.test(line)) {
+ i += 1
+ while (i < lines.length && '' === lines[i].trim()) {i += 1}
+ if (i < lines.length) {out.push('')}
+ continue
+ }
+ i += 1
+ }
+ return out
+}
+
+/** Remove any blank lines immediately after lines matching `headingRe`. */
+const removeBlankAfter = (lines: string[], headingRe: RegExp): string[] => {
+ const out: string[] = []
+ let i = 0
+ while (i < lines.length) {
+ const line = lines[i]
+ out.push(line)
+ if (headingRe.test(line)) {
+ i += 1
+ // Skip all blank lines following the heading
+ while (i < lines.length && '' === lines[i].trim()) {i += 1}
+ continue
+ }
+ i += 1
+ }
+ return out
+}
+
+/* ── linter ─────────────────────────────────────────────────────────── */
+
+const ensureTitle = (lines: string[], errors: string[]): string[] => {
+ if ('# Changelog' !== lines[0]) {
+ if (/^#\s+changelog/i.test(lines[0])) {
+ lines[0] = '# Changelog'
+ } else {
+ errors.push('CHANGELOG.md: First line must be "# Changelog"')
+ }
+ }
+
+ return lines
+}
+
+const promoteBoldChangeTypeLabels = (lines: string[]): string[] =>
+ lines.map(line => {
+ const match = /^\s*(?:\*\*|__)(?\w+)(?:\*\*|__)\s*$/.exec(line)
+ if (!match?.groups) {return line}
+ const { type } = match.groups
+ const canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === type.toLowerCase())
+ return canonical ? `### ${canonical}` : line
+ })
+
+const normaliseReleaseHeaders = (lines: string[], errors: string[]): string[] =>
+ lines.map(line => {
+ const bracketed = /^## \[(?[^\]]+)\]\s*\((?\d{4}-\d{2}-\d{2}|[A-Z][A-Z0-9-]*)\)/.exec(line)
+ if (bracketed?.groups) {return `## [${bracketed.groups.ver}] (${bracketed.groups.date})`}
+
+ const bracketedMissingClose = /^## \[(?[^\]]+)\]\s*\((?\d{4}-\d{2}-\d{2}|[A-Z][A-Z0-9-]*)$/.exec(line)
+ if (bracketedMissingClose?.groups) {
+ return `## [${bracketedMissingClose.groups.ver}] (${bracketedMissingClose.groups.date})`
+ }
+
+ const bracketedNoDate = /^## \[(?[^\]]+)\]/.exec(line)
+ if (bracketedNoDate) {
+ errors.push(`CHANGELOG.md: Release header missing or malformed date: ${line}`)
+ return line
+ }
+
+ const plain = /^## (?\d[^\s(]+)\s*(?:\((?\d{4}-\d{2}-\d{2}|[A-Z][A-Z0-9-]*)\))?/.exec(line)
+ if (!plain?.groups) {return line}
+ if (plain.groups.date) {return `## [${plain.groups.ver}] (${plain.groups.date})`}
+ errors.push(`CHANGELOG.md: Release header missing date: ${line}`)
+ return `## [${plain.groups.ver}]`
+ })
+
+const normaliseSectionNames = (lines: string[]): string[] =>
+ lines.map(line => {
+ const match = /^###\s+(?.+)$/.exec(line)
+ if (!match?.groups) {return line}
+ let key = match.groups.name.trim()
+ let canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === key.toLowerCase())
+ if (!canonical && /s$/i.test(key)) {
+ const singular = key.replace(/s$/i, '')
+ canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === singular.toLowerCase())
+ if (canonical) {key = singular}
+ }
+ return canonical ? `### ${canonical}` : `### ${key}`
+ })
+
+const normaliseIndentedSubListItems = (lines: string[]): string[] => {
+ const out: string[] = []
+
+ for (const line of lines) {
+ const indented = /^ {2}[*-] (?.*)$/.exec(line)
+ if (!indented?.groups) {
+ out.push(line)
+ continue
+ }
+
+ const text = indented.groups.text.trimEnd()
+ let parentIdx = -1
+ for (let j = out.length - 1; 0 <= j; j -= 1) {
+ if ('' === out[j].trim()) {break}
+ if (out[j].startsWith('* ')) {
+ parentIdx = j
+ break
+ }
+ }
+
+ if (-1 !== parentIdx) {
+ const parentTrimmed = out[parentIdx].trimEnd()
+ if (!parentTrimmed.endsWith(':')) {
+ out[parentIdx] = `${parentTrimmed.replace(/[.,;]$/, '')}:`
+ }
+ out.push(` - ${text}`)
+ } else {
+ out.push(`* ${text}`)
+ }
+ }
+
+ return out
+}
+
+const normaliseTopLevelBulletMarkers = (lines: string[]): string[] =>
+ lines.map(line => {
+ const match = /^(?[*-]) (?.*)$/.exec(line)
+ if (match?.groups) {return `* ${match.groups.content.trimEnd()}`}
+ return line
+ })
+
+const applySpacingRules = (lines: string[]): string[] => {
+ const RELEASE_RE = /^## /
+ const SECTION_RE = /^### /
+ const TITLE_RE = /^# Changelog/
+
+ let out = lines
+ out = normaliseBlankAfter(out, TITLE_RE)
+ out = normaliseBlanksBefore(out, RELEASE_RE, 1, TITLE_RE)
+ out = normaliseBlankAfter(out, RELEASE_RE)
+ out = normaliseBlanksBefore(out, SECTION_RE, 1, RELEASE_RE)
+ out = removeBlankAfter(out, SECTION_RE)
+ return out
+}
+
+const finaliseLines = (lines: string[]): string[] => {
+ const out = collapseBlankLines(lines)
+ while (0 < out.length && '' === out[out.length - 1]) {out.pop()}
+ out.push('')
+ return out
+}
+
+export const lintChangelog = (src: string): { fixed: string; errors: string[] } => {
+ const errors: string[] = []
+ let lines = src.split('\n')
+
+ lines = ensureTitle(lines, errors)
+ lines = trimTrailing(lines)
+ lines = promoteBoldChangeTypeLabels(lines)
+ lines = normaliseReleaseHeaders(lines, errors)
+ lines = normaliseSectionNames(lines)
+ lines = normaliseIndentedSubListItems(lines)
+ lines = normaliseTopLevelBulletMarkers(lines)
+ lines = applySpacingRules(lines)
+ lines = finaliseLines(lines)
+
+ return { fixed: lines.join('\n'), errors }
+}
+
+/* ── entry point ─────────────────────────────────────────────────────── */
+
+const root = resolve(__dirname, '../..')
+const args = process.argv.slice(CLI_ARGS_START_INDEX)
+const files = 0 < args.length ? args : [resolve(root, 'CHANGELOG.md')]
+let anyErrors = false
+let anyProcessed = false
+
+for (const f of files) {
+ const abs = resolve(f)
+ if (!abs.endsWith('CHANGELOG.md')) {continue}
+ anyProcessed = true
+
+ if (!existsSync(abs)) { console.error(`lint-changelog: file not found – ${abs}`); anyErrors = true; continue }
+
+ const src = readFileSync(abs, 'utf8')
+ const { fixed, errors } = lintChangelog(src)
+
+ errors.forEach(e => console.error(` ✗ ${e}`))
+ if (0 < errors.length) {anyErrors = true}
+
+ if (fixed !== src) {
+ writeFileSync(abs, fixed, 'utf8')
+ console.log(` ✔ auto-fixed: ${abs}`)
+ } else {
+ console.log(` ✔ no changes: ${abs}`)
+ }
+}
+
+if (!anyProcessed) {process.exit(0)}
+process.exit(anyErrors ? 1 : 0)
diff --git a/scripts/linters/lint-readme.ts b/scripts/linters/lint-readme.ts
new file mode 100644
index 000000000..3f51746c2
--- /dev/null
+++ b/scripts/linters/lint-readme.ts
@@ -0,0 +1,402 @@
+/**
+ * Lint-readme.ts
+ *
+ * Lints and auto-fixes src/readme.txt for WordPress.org formatting consistency.
+ *
+ * Rules enforced (based on current file conventions + WordPress.org spec):
+ *
+ * Header block
+ * - First line: === Plugin Name ===
+ * - Each header field: "Key: Value" (single space after colon, no trailing whitespace)
+ * - Exactly 1 blank line between the last header field and the short description
+ * - Required fields must be present: Contributors, Donate link, Tags, License,
+ * License URI, Stable tag, Requires at least, Tested up to, Requires PHP
+ * - Short description line immediately follows header (non-empty, ≤ 150 chars, no markup)
+ *
+ * Sections
+ * - Top-level: == Section Name == (known names normalised, title-case otherwise)
+ * - Sub-sections: = Sub Section = (title-case)
+ * - Known section names: Description, Installation, Frequently Asked Questions,
+ * Screenshots, Changelog, Upgrade Notice
+ *
+ * Changelog section (inside readme.txt)
+ * - Version sub-headers: = X.Y.Z (YYYY-MM-DD) = or an uppercase status token, e.g. = X.Y.Z (UPCOMING) =
+ * - Change-type labels: __Added__, __Changed__, __Fixed__, __Removed__,
+ * __Deprecated__, __Security__
+ * (### headings and **Bold** variants are demoted / normalised)
+ *
+ * Lists
+ * - Items start with "* " (not "- ")
+ * - No trailing whitespace
+ *
+ * Spacing
+ * - Exactly 1 blank line before every == section (not the very first)
+ * - Exactly 1 blank line after every == section heading
+ * - Exactly 1 blank line before every = sub-section (not right after == heading)
+ * - Exactly 1 blank line after every = sub-section inside == Changelog == only
+ * - Exactly 1 blank line before __Type__ labels inside Changelog (not right after = heading)
+ * - Exactly 1 blank line after __Type__ labels inside Changelog
+ * - No consecutive blank lines (max 1)
+ * - File ends with exactly one newline
+ */
+
+import { existsSync, readFileSync, writeFileSync } from 'fs'
+import { resolve } from 'path'
+
+/* ── helpers ─────────────────────────────────────────────────────────── */
+
+const KNOWN_CHANGE_TYPES = ['Added', 'Changed', 'Fixed', 'Removed', 'Deprecated', 'Security']
+
+const CLI_ARGS_START_INDEX = 2
+const SUBSECTION_TITLECASE_MAX_WORDS = 2
+const LIST_MARKER_LENGTH = 2
+
+const RE_DATE_SRC = '(?:\\d{4}-\\d{2}-\\d{2}|[A-Z][A-Z0-9-]*)'
+const RE_VERSION_SRC = '\\d+\\.\\d+(?:\\.\\d+)*(?:-[a-zA-Z0-9.]+)?'
+
+/** Known == Section == names (canonical capitalisation). */
+const KNOWN_SECTIONS: Record = {
+ 'description': 'Description',
+ 'installation': 'Installation',
+ 'frequently asked questions': 'Frequently Asked Questions',
+ 'faq': 'Frequently Asked Questions',
+ 'screenshots': 'Screenshots',
+ 'changelog': 'Changelog',
+ 'upgrade notice': 'Upgrade Notice'
+}
+
+const titleCase = (s: string): string =>
+ s.replace(/\w\S*/g, t => t.charAt(0).toUpperCase() + t.slice(1).toLowerCase())
+
+const sentenceCase = (s: string): string =>
+ s.charAt(0).toUpperCase() + s.slice(1)
+
+const subsectionCase = (s: string): string => {
+ const wordCount = s.trim().split(/\s+/).length
+ return SUBSECTION_TITLECASE_MAX_WORDS >= wordCount ? titleCase(s) : sentenceCase(s)
+}
+
+const normaliseSectionName = (raw: string): string => {
+ const key = raw.trim().toLowerCase()
+ return KNOWN_SECTIONS[key] ?? titleCase(raw.trim())
+}
+
+const trimTrailing = (lines: string[]): string[] =>
+ lines.map(l => l.trimEnd())
+
+const collapseBlankLines = (lines: string[]): string[] => {
+ const out: string[] = []
+ let prevBlank = false
+ for (const l of lines) {
+ const blank = '' === l.trim()
+ if (blank && prevBlank) {
+ continue
+ }
+ out.push(l)
+ prevBlank = blank
+ }
+ return out
+}
+
+/**
+ * Ensure exactly `n` blank lines appear immediately before every line matching
+ * `headingRe`. Lines matching `skipAfterRe` reset the "just-saw-section-start"
+ * flag, suppressing spacing for the immediately following heading.
+ */
+const normaliseBlanksBefore = (
+ lines: string[],
+ headingRe: RegExp,
+ n: number,
+ skipAfterRe?: RegExp
+): string[] => {
+ const out: string[] = []
+ let suppressNext = true
+
+ for (const line of lines) {
+ if (skipAfterRe?.test(line)) {
+ suppressNext = true
+ out.push(line)
+ continue
+ }
+
+ if (headingRe.test(line)) {
+ if (!suppressNext) {
+ while (0 < out.length && '' === out[out.length - 1].trim()) {
+ out.pop()
+ }
+ for (let b = 0; b < n; b += 1) {
+ out.push('')
+ }
+ }
+ out.push(line)
+ suppressNext = false
+ continue
+ }
+
+ if ('' !== line.trim()) {
+ suppressNext = false
+ }
+ out.push(line)
+ }
+ return out
+}
+
+/** Ensure exactly 1 blank line immediately after every line matching `headingRe`. */
+const normaliseBlankAfter = (lines: string[], headingRe: RegExp): string[] => {
+ const out: string[] = []
+ let i = 0
+ while (i < lines.length) {
+ const line = lines[i]
+ out.push(line)
+ if (headingRe.test(line)) {
+ i += 1
+ while (i < lines.length && '' === lines[i].trim()) {
+ i += 1
+ }
+ if (i < lines.length) {
+ out.push('')
+ }
+ continue
+ }
+ i += 1
+ }
+ return out
+}
+
+/**
+ * Like normaliseBlankAfter but only operates on lines that fall within a
+ * specific section (between `sectionStartRe` and the next `== ... ==` heading).
+ */
+const normaliseBlankAfterInSection = (
+ lines: string[],
+ sectionStartRe: RegExp,
+ headingRe: RegExp
+): string[] => {
+ const out: string[] = []
+ let inSection = false
+ let i = 0
+ while (i < lines.length) {
+ const line = lines[i]
+ if (sectionStartRe.test(line)) {
+ inSection = true
+ } else if (/^== .+ ==$/.test(line)) {
+ inSection = false
+ }
+
+ out.push(line)
+ if (inSection && headingRe.test(line)) {
+ i += 1
+ while (i < lines.length && '' === lines[i].trim()) {
+ i += 1
+ }
+ if (i < lines.length) {
+ out.push('')
+ }
+ continue
+ }
+ i += 1
+ }
+ return out
+}
+
+/* ── linter ─────────────────────────────────────────────────────────── */
+
+const normalisePluginHeader = (lines: string[], errors: string[]): string[] => {
+ if (!/^=== .+ ===$/.test(lines[0])) {
+ errors.push('readme.txt: First line must be "=== Plugin Name ==="')
+ }
+ return lines
+}
+
+const normaliseHeaderFieldSpacing = (lines: string[]): string[] => {
+ let inHeader = true
+ return lines.map((line, i) => {
+ if (inHeader && 0 < i && '' === line.trim()) {
+ inHeader = false
+ return line
+ }
+ if (!inHeader) {
+ return line
+ }
+ const match = /^(?[A-Za-z][A-Za-z ]+):\s*(?.*)$/.exec(line)
+ if (match?.groups) {
+ return `${match.groups.key.trim()}: ${match.groups.value.trim()}`
+ }
+ return line
+ })
+}
+
+const ensureBlankAfterLastHeaderField = (lines: string[]): string[] => {
+ const FIELD_RE = /^[A-Za-z][A-Za-z ]+: /
+ let lastFieldIdx = -1
+
+ for (const [i, line] of lines.entries()) {
+ if (0 === i) {
+ continue
+ }
+ if (line.startsWith('== ')) {
+ break
+ }
+ if (FIELD_RE.test(line)) {
+ lastFieldIdx = i
+ }
+ }
+
+ if (-1 !== lastFieldIdx && lastFieldIdx + 1 < lines.length) {
+ if ('' !== lines[lastFieldIdx + 1].trim()) {
+ lines.splice(lastFieldIdx + 1, 0, '')
+ }
+ }
+
+ return lines
+}
+
+const normaliseSectionHeadings = (lines: string[]): string[] =>
+ lines.map(line => {
+ const match = /^==\s+(?.+?)\s+==$/.exec(line)
+ if (match?.groups) {
+ return `== ${normaliseSectionName(match.groups.name)} ==`
+ }
+ return line
+ })
+
+const normaliseSubSectionHeadings = (lines: string[]): string[] =>
+ lines.map(line => {
+ const match = /^=\s+(?.+?)\s+=$/.exec(line)
+ if (!match?.groups) {
+ return line
+ }
+ const inner = match.groups.name.trim()
+ const ver = new RegExp(`^(?${RE_VERSION_SRC})\\s+\\((?${RE_DATE_SRC})\\)$`).exec(inner)
+ if (ver?.groups) {
+ return `= ${ver.groups.ver} (${ver.groups.date}) =`
+ }
+ return `= ${subsectionCase(inner)} =`
+ })
+
+const normaliseChangelogChangeTypes = (lines: string[]): string[] => {
+ let inChangelog = false
+ return lines.map(line => {
+ if (/^== Changelog ==$/.test(line)) {
+ inChangelog = true
+ return line
+ }
+ if (line.startsWith('== ')) {
+ inChangelog = false
+ return line
+ }
+ if (!inChangelog) {
+ return line
+ }
+
+ const hashM = /^###\s+(?\w+)\s*$/.exec(line)
+ if (hashM?.groups) {
+ const canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === hashM.groups?.type.toLowerCase())
+ if (canonical) {
+ return `__${canonical}__`
+ }
+ }
+ const boldM = /^\*\*(?\w+)\*\*\s*$/.exec(line)
+ if (boldM?.groups) {
+ const canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === boldM.groups?.type.toLowerCase())
+ if (canonical) {
+ return `__${canonical}__`
+ }
+ }
+ const underM = /^__(?\w+)__\s*$/.exec(line)
+ if (underM?.groups) {
+ const canonical = KNOWN_CHANGE_TYPES.find(t => t.toLowerCase() === underM.groups?.type.toLowerCase())
+ if (canonical) {
+ return `__${canonical}__`
+ }
+ }
+ return line
+ })
+}
+
+const normaliseListItems = (lines: string[]): string[] =>
+ lines.map(line => line.startsWith('- ') ? `* ${line.slice(LIST_MARKER_LENGTH)}` : line)
+
+const applySpacingRules = (lines: string[]): string[] => {
+ const SECTION_RE = /^== .+ ==$/
+ const SUBSECTION_RE = /^= .+ =$/
+ const CHANGETYPE_RE = /^__(?:Added|Changed|Fixed|Removed|Deprecated|Security)__$/
+
+ let out = lines
+ out = normaliseBlanksBefore(out, SECTION_RE, 1, /^=== .+ ===/)
+ out = normaliseBlankAfter(out, SECTION_RE)
+ out = normaliseBlanksBefore(out, SUBSECTION_RE, 1, SECTION_RE)
+ out = normaliseBlankAfterInSection(out, /^== Changelog ==$/, SUBSECTION_RE)
+ out = normaliseBlanksBefore(out, CHANGETYPE_RE, 1, SUBSECTION_RE)
+ out = normaliseBlankAfterInSection(out, /^== Changelog ==$/, CHANGETYPE_RE)
+ return out
+}
+
+const finaliseLines = (lines: string[]): string[] => {
+ const out = collapseBlankLines(lines)
+ while (0 < out.length && '' === out[out.length - 1]) {
+ out.pop()
+ }
+ out.push('')
+ return out
+}
+
+export const lintReadme = (src: string): { fixed: string; errors: string[] } => {
+ const errors: string[] = []
+ let lines = src.split('\n')
+
+ lines = normalisePluginHeader(lines, errors)
+ lines = trimTrailing(lines)
+ lines = normaliseHeaderFieldSpacing(lines)
+ lines = ensureBlankAfterLastHeaderField(lines)
+ lines = normaliseSectionHeadings(lines)
+ lines = normaliseSubSectionHeadings(lines)
+ lines = normaliseChangelogChangeTypes(lines)
+ lines = normaliseListItems(lines)
+ lines = applySpacingRules(lines)
+ lines = finaliseLines(lines)
+
+ return { fixed: lines.join('\n'), errors }
+}
+
+/* ── entry point ─────────────────────────────────────────────────────── */
+
+const root = resolve(__dirname, '../..')
+const args = process.argv.slice(CLI_ARGS_START_INDEX)
+const files = 0 < args.length ? args : [resolve(root, 'src/readme.txt')]
+let anyErrors = false
+let anyProcessed = false
+
+for (const f of files) {
+ const abs = resolve(f)
+ if (!abs.endsWith('readme.txt')) {
+ continue
+ }
+ anyProcessed = true
+
+ if (!existsSync(abs)) {
+ console.error(`lint-readme: file not found – ${abs}`)
+ anyErrors = true
+ continue
+ }
+
+ const src = readFileSync(abs, 'utf8')
+ const { fixed, errors } = lintReadme(src)
+
+ errors.forEach(e => console.error(` ✗ ${e}`))
+ if (0 < errors.length) {
+ anyErrors = true
+ }
+
+ if (fixed !== src) {
+ writeFileSync(abs, fixed, 'utf8')
+ console.log(` ✔ auto-fixed: ${abs}`)
+ } else {
+ console.log(` ✔ no changes: ${abs}`)
+ }
+}
+
+if (!anyProcessed) {
+ process.exit(0)
+}
+process.exit(anyErrors ? 1 : 0)
diff --git a/scripts/test-setup-phpunit.ts b/scripts/test-setup-phpunit.ts
new file mode 100644
index 000000000..7bd57f27b
--- /dev/null
+++ b/scripts/test-setup-phpunit.ts
@@ -0,0 +1,95 @@
+#!/usr/bin/env ts-node
+
+import { execFileSync } from 'node:child_process'
+import { resolve } from 'node:path'
+
+const getEnv = (key: string, fallback: string): string => process.env[key] ?? fallback
+
+const run = (cmd: string, args: readonly string[], options: { env?: NodeJS.ProcessEnv } = {}) => {
+ const extraEnv = options.env ?? {}
+ execFileSync(cmd, args, { stdio: 'inherit', env: { ...process.env, ...extraEnv } })
+}
+
+const buildMysqlArgs = (options: { user: string; password: string; host: string }) => {
+ const args = ['-u', options.user]
+
+ if (options.password) {
+ args.push(`--password=${options.password}`)
+ }
+
+ if (options.host) {
+ args.push('-h', options.host)
+ }
+
+ return args
+}
+
+const assertSafeDbName = (dbName: string) => {
+ if (!/^[A-Za-z0-9_]+$/.test(dbName)) {
+ throw new Error(`Invalid DB name "${dbName}". Use only letters, numbers, and underscore.`)
+ }
+}
+
+const main = () => {
+ const dbName = getEnv('WP_PHPUNIT_DB_NAME', 'code_snippets_phpunit')
+ const dbUser = getEnv('WP_PHPUNIT_DB_USER', 'root')
+ const dbPass = getEnv('WP_PHPUNIT_DB_PASS', '')
+ const dbHost = getEnv('WP_PHPUNIT_DB_HOST', '127.0.0.1')
+ const wpVersion = getEnv('WP_PHPUNIT_WP_VERSION', 'latest')
+
+ assertSafeDbName(dbName)
+
+ const wpTestsDir = resolve(process.cwd(), '.wp-tests-lib')
+ const wpCoreDir = resolve(process.cwd(), '.wp-core')
+ const wpTestsConfig = resolve(wpTestsDir, 'wp-tests-config.php')
+ const installScript = resolve(process.cwd(), 'scripts', 'install-wp-tests.sh')
+
+ // Create the database if needed (avoid install-wp-tests.sh prompt / destructive behavior).
+ const mysqlArgs = buildMysqlArgs({ user: dbUser, password: dbPass, host: dbHost })
+ run('mysql', [...mysqlArgs, '-e', `CREATE DATABASE IF NOT EXISTS \`${dbName}\`;`])
+
+ // Ensure config is regenerated with current DB settings.
+ run('rm', ['-f', wpTestsConfig, `${wpTestsConfig}.bak`])
+
+ run('bash', [
+ installScript,
+ dbName,
+ dbUser,
+ dbPass,
+ dbHost,
+ wpVersion,
+ 'true'
+ ], {
+ env: {
+ WP_TESTS_DIR: wpTestsDir,
+ WP_CORE_DIR: wpCoreDir
+ }
+ })
+
+ // Ensure a clean test schema before WordPress bootstraps installation.
+ run('mysql', [
+ ...mysqlArgs,
+ dbName,
+ '-e',
+ [
+ 'SET FOREIGN_KEY_CHECKS = 0',
+ 'SET @tables = (SELECT GROUP_CONCAT(table_name)' +
+ ` FROM information_schema.tables WHERE table_schema = '${dbName}' AND table_name LIKE 'wptests\\_%')`,
+ "SET @drop = IF(@tables IS NULL, 'SELECT 1', CONCAT('DROP TABLE ', @tables))",
+ 'PREPARE stmt FROM @drop',
+ 'EXECUTE stmt',
+ 'DEALLOCATE PREPARE stmt',
+ 'SET FOREIGN_KEY_CHECKS = 1'
+ ].join('; ')
+ ])
+
+ // Initialize WordPress test tables so `npm run test:php` works on a fresh DB.
+ run('php', [resolve(wpTestsDir, 'includes', 'install.php'), wpTestsConfig])
+}
+
+try {
+ main()
+} catch (error: unknown) {
+ console.error(error)
+ process.exitCode = 1
+}
diff --git a/scripts/test-setup-playwright.ts b/scripts/test-setup-playwright.ts
new file mode 100644
index 000000000..7ebdcd3b4
--- /dev/null
+++ b/scripts/test-setup-playwright.ts
@@ -0,0 +1,65 @@
+#!/usr/bin/env ts-node
+
+import { execFileSync } from 'node:child_process'
+import { readFileSync } from 'node:fs'
+import { resolve } from 'node:path'
+
+const run = (cmd: string, args: readonly string[]) => {
+ execFileSync(cmd, args, { stdio: 'inherit' })
+}
+
+const runWpEnvCli = (args: readonly string[]) => run('npx', ['wp-env', 'run', 'cli', ...args])
+
+const getPluginSlug = (): string => {
+ const prefix = 'wp-content/plugins/'
+ const config = <{ mappings?: Record }>JSON.parse(readFileSync(resolve(process.cwd(), '.wp-env.json'), 'utf8'))
+ const mapping = Object.keys(config.mappings ?? {}).find(key => key.startsWith(prefix))
+
+ if (!mapping) {
+ throw new Error('No plugin mapping found in .wp-env.json')
+ }
+
+ return mapping.slice(prefix.length)
+}
+
+const main = () => {
+ // Ensure a clean slate for file-based execution tests:
+ // - remove flat-file execution directory (stale indexes can break the WP site)
+ // - ensure plugin is active
+ // - force enable_flat_files=false so the Playwright setup test can flip it to true
+ // - delete all DB snippets with an E2E prefix (keeps list clean across runs)
+
+ runWpEnvCli(['sh', '-lc', 'rm -rf wp-content/code-snippets'])
+ runWpEnvCli(['wp', 'plugin', 'activate', getPluginSlug()])
+
+ runWpEnvCli([
+ 'wp',
+ 'eval',
+ `
+ $settings = get_option('code_snippets_settings', []);
+ $settings['general']['enable_flat_files'] = false;
+ update_option('code_snippets_settings', $settings);
+ `
+ ])
+
+ runWpEnvCli([
+ 'wp',
+ 'eval',
+ `
+ global $wpdb;
+ $wpdb->query(
+ $wpdb->prepare(
+ "DELETE FROM {$wpdb->prefix}snippets WHERE name LIKE %s",
+ "E2E%"
+ )
+ );
+ `
+ ])
+}
+
+try {
+ main()
+} catch (error: unknown) {
+ console.error(error)
+ process.exitCode = 1
+}
diff --git a/scripts/utils/files.ts b/scripts/utils/files.ts
index fa79628dc..002123e37 100644
--- a/scripts/utils/files.ts
+++ b/scripts/utils/files.ts
@@ -1,5 +1,5 @@
import { mkdir, rm, stat } from 'fs/promises'
-import { createReadStream, createWriteStream } from 'fs'
+import { createReadStream, createWriteStream, readFileSync, writeFileSync } from 'fs'
import { dirname, join } from 'path'
import { glob } from 'glob'
@@ -29,3 +29,9 @@ export const copy = async (patterns: string[], dest: string, transform?: (filena
}
}
}
+
+export const replaceInFile = (filename: string, transform: (contents: string) => string) => {
+ const file = resolve(filename)
+ const contents = readFileSync(file, 'utf8')
+ writeFileSync(file, transform(contents), 'utf8')
+}
diff --git a/scripts/version.ts b/scripts/version.ts
index c5b70e018..6e28a2c09 100644
--- a/scripts/version.ts
+++ b/scripts/version.ts
@@ -1,12 +1,5 @@
-import { readFileSync, writeFileSync } from 'fs'
import plugin from '../package.json'
-import { resolve } from './utils/files'
-
-const replaceInFile = (filename: string, transform: (contents: string) => string) => {
- const file = resolve(filename)
- const contents = readFileSync(file, 'utf8')
- writeFileSync(file, transform(contents), 'utf8')
-}
+import { replaceInFile } from './utils/files'
replaceInFile(
'src/code-snippets.php',
diff --git a/src/code-snippets.php b/src/code-snippets.php
index 6bd3a959d..f9df2c2cc 100644
--- a/src/code-snippets.php
+++ b/src/code-snippets.php
@@ -8,14 +8,14 @@
* License: GPL-2.0-or-later
* License URI: license.txt
* Text Domain: code-snippets
- * Version: 3.9.6
+ * Version: 3.10.0-beta.1
* Requires PHP: 7.4
- * Requires at least: 5.0
+ * Requires at least: 5.5
*
- * @version 3.9.6
+ * @version 3.10.0-beta.1
* @package Code_Snippets
* @author Shea Bunge
- * @copyright 2012-2024 Code Snippets Pro
+ * @copyright 2012-2026 Code Snippets Pro
* @license GPL-2.0-or-later https://spdx.org/licenses/GPL-2.0-or-later.html
* @link https://github.com/codesnippetspro/code-snippets
*
@@ -37,7 +37,7 @@
*
* @const string
*/
- define( 'CODE_SNIPPETS_VERSION', '3.9.6' );
+ define( 'CODE_SNIPPETS_VERSION', '3.10.0-beta.1' );
/**
* The full path to the main file of this plugin.
@@ -54,11 +54,11 @@
* Used to determine which version of Code Snippets is running.
*
* @since 3.0.0
- * @onst boolean
+ * @const bool
*/
define( 'CODE_SNIPPETS_PRO', false );
- require_once dirname( __FILE__ ) . '/php/load.php';
+ require_once dirname( __FILE__ ) . '/php/Core/load.php';
} else {
- require_once dirname( __FILE__ ) . '/php/deactivation-notice.php';
+ require_once dirname( __FILE__ ) . '/php/Core/deactivation-notice.php';
}
diff --git a/src/composer.json b/src/composer.json
index be43b7946..ecb505d47 100644
--- a/src/composer.json
+++ b/src/composer.json
@@ -21,23 +21,28 @@
"source": "https://github.com/codesnippetspro/code-snippets"
},
"autoload": {
- "classmap": [
- "php/"
+ "files": [
+ "php/Admin/Menus/Manage/Manage_Menu.php",
+ "php/Utils/requests.php"
],
"psr-4": {
+ "Code_Snippets\\": "php/"
}
},
"require": {
"php": ">=7.4",
"ext-dom": "*",
"ext-json": "*",
+ "ext-zip": "*",
"composer/installers": "^2.3",
"typisttech/imposter-plugin": "^0.6.2"
},
"require-dev": {
"wp-coding-standards/wpcs": "^3.1",
"phpcompatibility/phpcompatibility-wp": "^2.1",
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0"
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "phpunit/phpunit": "^9.6",
+ "yoast/phpunit-polyfills": "^2.0"
},
"config": {
"platform": {
@@ -53,5 +58,9 @@
"imposter": {
"namespace": "Code_Snippets\\Vendor"
}
+ },
+ "scripts": {
+ "post-install-cmd": "@php ../scripts/composer-fix-autoload.php",
+ "post-update-cmd": "@php ../scripts/composer-fix-autoload.php"
}
}
diff --git a/src/composer.lock b/src/composer.lock
index 56ee40d10..8ab294720 100644
--- a/src/composer.lock
+++ b/src/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "c322fb32f6db8844392d9f78341fcefb",
+ "content-hash": "118984772f6b448937a6130f407c8a8b",
"packages": [
{
"name": "composer/installers",
@@ -330,16 +330,16 @@
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
- "version": "v1.2.0",
+ "version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/composer-installer.git",
- "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1"
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1",
- "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
+ "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd",
"shasum": ""
},
"require": {
@@ -422,7 +422,312 @@
"type": "thanks_dev"
}
],
- "time": "2025-11-11T04:32:07+00:00"
+ "time": "2026-05-06T08:26:05+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
+ "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^11",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.30 || ^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-30T00:15:36+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0"
+ },
+ "time": "2026-07-04T14:30:18+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
},
{
"name": "phpcompatibility/php-compatibility",
@@ -639,21 +944,21 @@
},
{
"name": "phpcsstandards/phpcsextra",
- "version": "1.5.0",
+ "version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
- "reference": "b598aa890815b8df16363271b659d73280129101"
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101",
- "reference": "b598aa890815b8df16363271b659d73280129101",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/39467533fdb742446d68c1d10ac33d625ee0311c",
+ "reference": "39467533fdb742446d68c1d10ac33d625ee0311c",
"shasum": ""
},
"require": {
"php": ">=5.4",
- "phpcsstandards/phpcsutils": "^1.2.0",
+ "phpcsstandards/phpcsutils": "^1.2.3",
"squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1"
},
"require-dev": {
@@ -717,20 +1022,20 @@
"type": "thanks_dev"
}
],
- "time": "2025-11-12T23:06:57+00:00"
+ "time": "2026-07-27T11:13:17+00:00"
},
{
"name": "phpcsstandards/phpcsutils",
- "version": "1.2.0",
+ "version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
- "reference": "fa82d14ad1c1713224a52c66c78478145fe454ba"
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/fa82d14ad1c1713224a52c66c78478145fe454ba",
- "reference": "fa82d14ad1c1713224a52c66c78478145fe454ba",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/5f35d9408c54d7b529501f3c688b6eae562aea1f",
+ "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f",
"shasum": ""
},
"require": {
@@ -810,117 +1115,1592 @@
"type": "thanks_dev"
}
],
- "time": "2025-11-11T00:17:56+00:00"
+ "time": "2026-07-27T10:28:41+00:00"
},
{
- "name": "squizlabs/php_codesniffer",
- "version": "3.13.5",
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.32",
"source": {
"type": "git",
- "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
- "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4"
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
- "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5",
+ "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5",
"shasum": ""
},
"require": {
- "ext-simplexml": "*",
- "ext-tokenizer": "*",
+ "ext-dom": "*",
+ "ext-libxml": "*",
"ext-xmlwriter": "*",
- "php": ">=5.4.0"
+ "nikic/php-parser": "^4.19.1 || ^5.1.0",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.6",
+ "phpunit/php-text-template": "^2.0.4",
+ "sebastian/code-unit-reverse-lookup": "^2.0.3",
+ "sebastian/complexity": "^2.0.3",
+ "sebastian/environment": "^5.1.5",
+ "sebastian/lines-of-code": "^1.0.4",
+ "sebastian/version": "^3.0.2",
+ "theseer/tokenizer": "^1.2.3"
},
"require-dev": {
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ "phpunit/phpunit": "^9.6"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
},
- "bin": [
- "bin/phpcbf",
- "bin/phpcs"
- ],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "9.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
- "name": "Greg Sherwood",
- "role": "Former lead"
- },
- {
- "name": "Juliette Reinders Folmer",
- "role": "Current lead"
- },
- {
- "name": "Contributors",
- "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
- "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
- "phpcs",
- "standards",
- "static analysis"
+ "coverage",
+ "testing",
+ "xunit"
],
"support": {
- "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
- "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
- "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
- "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32"
},
"funding": [
{
- "url": "https://github.com/PHPCSStandards",
- "type": "github"
- },
- {
- "url": "https://github.com/jrfnl",
+ "url": "https://github.com/sebastianbergmann",
"type": "github"
- },
- {
- "url": "https://opencollective.com/php_codesniffer",
- "type": "open_collective"
- },
- {
- "url": "https://thanks.dev/u/gh/phpcsstandards",
- "type": "thanks_dev"
}
],
- "time": "2025-11-04T16:30:35+00:00"
+ "time": "2024-08-22T04:23:01+00:00"
},
{
- "name": "wp-coding-standards/wpcs",
- "version": "3.2.0",
+ "name": "phpunit/php-file-iterator",
+ "version": "3.0.6",
"source": {
"type": "git",
- "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
- "reference": "d2421de7cec3274ae622c22c744de9a62c7925af"
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af",
- "reference": "d2421de7cec3274ae622c22c744de9a62c7925af",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
- "ext-filter": "*",
- "ext-libxml": "*",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:58:55+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:16:10+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "9.6.35",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0edba2f3a0c48df3553cb9b640810b30df60302b",
+ "reference": "0edba2f3a0c48df3553cb9b640810b30df60302b",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.5.0 || ^2",
+ "ext-dom": "*",
+ "ext-filter": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=7.3",
+ "phpunit/php-code-coverage": "^9.2.32",
+ "phpunit/php-file-iterator": "^3.0.6",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.4",
+ "phpunit/php-timer": "^5.0.3",
+ "sebastian/cli-parser": "^1.0.2",
+ "sebastian/code-unit": "^1.0.8",
+ "sebastian/comparator": "^4.0.10",
+ "sebastian/diff": "^4.0.6",
+ "sebastian/environment": "^5.1.5",
+ "sebastian/exporter": "^4.0.8",
+ "sebastian/global-state": "^5.0.8",
+ "sebastian/object-enumerator": "^4.0.4",
+ "sebastian/resource-operations": "^3.0.4",
+ "sebastian/type": "^3.2.1",
+ "sebastian/version": "^3.0.2"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.35"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsoring.html",
+ "type": "other"
+ }
+ ],
+ "time": "2026-07-06T14:48:07+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T06:27:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:08:54+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:30:19+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "4.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-24T09:22:56+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-22T06:19:30+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T06:30:58+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "5.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:03:51+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "4.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:03:27+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "5.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T07:10:35+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-22T06:20:34+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:12:34+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:14:26+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0",
+ "reference": "539c6691e0623af6dc6f9c20384c120f963465a0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T06:57:39+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "3.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-14T16:00:52+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:13:03+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-11-04T16:30:35+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-17T20:03:58+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "3.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/ec2ff942335f33683a5957a85d138753876a05cf",
+ "reference": "ec2ff942335f33683a5957a85d138753876a05cf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "ext-libxml": "*",
"ext-tokenizer": "*",
"ext-xmlreader": "*",
- "php": ">=5.4",
- "phpcsstandards/phpcsextra": "^1.4.0",
- "phpcsstandards/phpcsutils": "^1.1.0",
- "squizlabs/php_codesniffer": "^3.13.0"
+ "php": ">=7.2",
+ "phpcsstandards/phpcsextra": "^1.5.1",
+ "phpcsstandards/phpcsutils": "^1.2.3",
+ "squizlabs/php_codesniffer": "^3.13.5"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.4.0",
- "phpcompatibility/php-compatibility": "^9.0",
+ "phpcompatibility/php-compatibility": "^10.0.0@dev",
"phpcsstandards/phpcsdevtools": "^1.2.0",
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ "phpunit/phpunit": "^8.0 || ^9.0"
},
"suggest": {
"ext-iconv": "For improved results",
@@ -955,7 +2735,70 @@
"type": "custom"
}
],
- "time": "2025-07-24T20:08:31+00:00"
+ "time": "2026-07-27T11:53:23+00:00"
+ },
+ {
+ "name": "yoast/phpunit-polyfills",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Yoast/PHPUnit-Polyfills.git",
+ "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27",
+ "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6",
+ "phpunit/phpunit": "^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "yoast/yoastcs": "^3.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "phpunitpolyfills-autoload.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Team Yoast",
+ "email": "support@yoast.com",
+ "homepage": "https://yoast.com"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors"
+ }
+ ],
+ "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests",
+ "homepage": "https://github.com/Yoast/PHPUnit-Polyfills",
+ "keywords": [
+ "phpunit",
+ "polyfill",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues",
+ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy",
+ "source": "https://github.com/Yoast/PHPUnit-Polyfills"
+ },
+ "time": "2025-08-10T05:13:49+00:00"
}
],
"aliases": [],
@@ -966,11 +2809,12 @@
"platform": {
"php": ">=7.4",
"ext-dom": "*",
- "ext-json": "*"
+ "ext-json": "*",
+ "ext-zip": "*"
},
"platform-dev": {},
"platform-overrides": {
"php": "7.4"
},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/src/css/admin-bar.scss b/src/css/admin-bar.scss
new file mode 100644
index 000000000..8da2b46fc
--- /dev/null
+++ b/src/css/admin-bar.scss
@@ -0,0 +1,119 @@
+#wpadminbar {
+ #wp-admin-bar-code-snippets > .ab-item {
+ .code-snippets-admin-bar-icon {
+ inline-size: 16px;
+ block-size: 16px;
+ inset-block-start: 5px;
+
+ &::before {
+ content: '';
+ display: block;
+ inline-size: 16px;
+ block-size: 16px;
+ mask-image: url('../assets/menu-icon.svg');
+ mask-repeat: no-repeat;
+ mask-position: center;
+ mask-size: contain;
+ background-color: rgb(240 245 250 / 60%);
+ }
+ }
+ }
+
+ .code-snippets-pagination-node > .ab-item {
+ padding: 0;
+ margin-block-end: 6px;
+ }
+
+ .code-snippets-safe-mode-active {
+ > .ab-item,
+ &.ab-item {
+ background: #b32d2e;
+ color: #fff;
+ }
+ }
+
+ .code-snippets-disabled > .ab-item {
+ opacity: 0.6;
+ }
+
+ .code-snippets-pagination-controls {
+ display: flex;
+ align-items: stretch;
+ inline-size: 100%;
+ white-space: nowrap;
+
+ .code-snippets-pagination-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex: 1 1 0;
+ min-block-size: 32px;
+ padding: 0 10px;
+ background: rgb(240 245 250 / 8%);
+ color: inherit;
+ text-decoration: none;
+ line-height: 1.2;
+ box-sizing: border-box;
+
+ & + .code-snippets-pagination-button {
+ border-inline-start: 1px solid rgb(240 245 250 / 15%);
+ }
+
+ &[aria-disabled='true'] {
+ opacity: 0.4;
+ pointer-events: none;
+ }
+ }
+
+ a.code-snippets-pagination-button:hover {
+ background: rgb(240 245 250 / 16%);
+ }
+
+ a[data-action='first'],
+ a[data-action='last'] {
+ flex: 0 0 36px;
+ padding: 0;
+ }
+
+ .code-snippets-pagination-page {
+ flex: 0 0 auto;
+ cursor: default;
+ opacity: 0.85;
+ }
+ }
+
+ .code-snippets-safe-mode-active:hover > .ab-item,
+ .code-snippets-safe-mode-active.hover > .ab-item {
+ background: #d63638;
+ color: #fff;
+ }
+
+ .code-snippets-disabled:hover > .ab-item,
+ .code-snippets-disabled.hover > .ab-item {
+ opacity: 1;
+ }
+
+ .code-snippets-safe-mode.code-snippets-safe-mode-active > .ab-item,
+ .code-snippets-safe-mode.code-snippets-safe-mode-active.ab-item {
+ font-weight: 600;
+ }
+
+ .code-snippets-safe-mode .code-snippets-external-icon {
+ font-family: dashicons;
+ display: inline-block;
+ position: relative;
+ line-height: 1;
+ opacity: .8;
+ inset-block-start: 6px;
+ }
+
+ .code-snippets-safe-mode:hover .code-snippets-external-icon,
+ .code-snippets-safe-mode.hover .code-snippets-external-icon {
+ opacity: 1;
+ }
+
+ #wp-admin-bar-code-snippets-active-snippets > .ab-sub-wrapper > .ab-submenu,
+ #wp-admin-bar-code-snippets-inactive-snippets > .ab-sub-wrapper > .ab-submenu {
+ padding-block-start: 0;
+ }
+}
diff --git a/src/css/common/_badges.scss b/src/css/common/_badges.scss
index 9d842b451..b00ea2f6c 100644
--- a/src/css/common/_badges.scss
+++ b/src/css/common/_badges.scss
@@ -3,6 +3,21 @@
@use 'sass:color';
@use 'theme';
+// Badges are wrapped in links that filter by type. The white ring separates the
+// focus indicator from the badge, matching the activation switch. `:focus` is
+// included because a click otherwise leaves the WordPress ring, which hugs the
+// badge.
+@mixin badge-link {
+ display: inline-flex;
+ border-radius: 3px;
+
+ &:focus,
+ &:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 2px #fff, 0 0 0 4px theme.$accent;
+ }
+}
+
.badge {
font-size: 12px;
font-weight: 700;
@@ -35,7 +50,7 @@
}
.network-shared {
- color: #2271b1;
+ color: theme.$accent;
font-size: 22px;
inline-size: 100%;
cursor: help;
@@ -62,6 +77,11 @@
gap: 2px;
}
+.nav-tab-button .dashicons-external {
+ font-size: 15px;
+ color: #666;
+}
+
@each $name, $colors in theme.$badges {
$text-color: #fff;
$background-color: list.nth($colors, 1);
@@ -77,16 +97,19 @@
background-color: $background-color;
}
- .badge.#{$name}-badge:hover {
+ a.badge.#{$name}-badge:hover,
+ button.badge.#{$name}-badge:hover {
color: $text-color;
background-color: color.adjust($background-color, $lightness: -5%);
}
}
+// #646970 (WP gray-50) keeps white badge text at 5.5:1 so locked/unlicensed
+// states stay WCAG AA without hover; #a7aaad was 2.3:1.
.nav-tab-inactive .badge,
.inverted-badges .badge {
color: #fff;
- background-color: #a7aaad;
+ background-color: #646970;
border-color: #fff !important;
.dashicons {
@@ -99,6 +122,8 @@
$text-color: list.nth($colors, 2);
$background-color: list.nth($colors, 1);
+ background: transparent;
+
.badge.pro-badge {
color: $text-color;
background-color: $background-color;
@@ -116,11 +141,29 @@
}
}
-.nav-tab-inactive {
- background: transparent;
+// Override WordPress' .wp-core-ui .button .dashicons { line-height: 1.9 } —
+// inside a button the inflated line-box would shift the glyph below the
+// badge's bottom border. Placed at end-of-file so it follows all other
+// .dashicons rules in source order (no-descending-specificity).
+.wp-core-ui .button .badge .dashicons {
+ line-height: 1;
}
-.nav-tab-button .dashicons-external {
- font-size: 15px;
- color: #666;
+// Pro badge consolidated to the navigation pro-chip style: a light accent pill
+// in every context, overriding the badge colour map and the inverted/nav-tab
+// variants (whose white !important border must be beaten here). This is a
+// deliberate final override, so it intentionally follows higher-specificity
+// context rules above.
+/* stylelint-disable no-descending-specificity */
+.badge.pro-badge,
+.inverted-badges .badge.pro-badge,
+.nav-tab-inactive .badge.pro-badge {
+ color: theme.$accent;
+ background-color: #eff5f9;
+ border: 1px solid rgb(34 113 177 / 10%) !important;
+ border-radius: 999px;
+ padding-block: 3px;
+ padding-inline: 10px;
+ line-height: normal;
}
+/* stylelint-enable no-descending-specificity */
diff --git a/src/css/common/_banners.scss b/src/css/common/_banners.scss
new file mode 100644
index 000000000..8769e0ac7
--- /dev/null
+++ b/src/css/common/_banners.scss
@@ -0,0 +1,43 @@
+@use '../common/theme';
+@use 'sass:map';
+@use 'sass:list';
+
+@mixin banners {
+ .banner {
+ border: 0;
+ border-radius: 5px;
+ display: flex;
+ align-items: center;
+ padding: 6px 10px;
+ gap: 8px;
+ margin: 0;
+
+ .banner-dismiss {
+ position: unset;
+ margin-inline-start: auto;
+ padding: 0;
+
+ &, &::before {
+ color: inherit;
+ }
+ }
+
+ .wp-core-ui &.is-dismissible {
+ position: unset;
+ padding-inline-end: 10px;
+ }
+ }
+
+ @each $name, $colors in theme.$notices {
+ .banner-#{$name} {
+ color: list.nth($colors, 2);
+ background-color: list.nth($colors, 1);
+ }
+ }
+
+ .banner-success::before {
+ content: '✓';
+ font-weight: bold;
+ font-size: 16px;
+ }
+}
diff --git a/src/css/common/_cards.scss b/src/css/common/_cards.scss
new file mode 100644
index 000000000..055bea08a
--- /dev/null
+++ b/src/css/common/_cards.scss
@@ -0,0 +1,99 @@
+@use 'checkbox';
+@use 'theme';
+
+.code-snippets-cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(580px, 1fr));
+ gap: 20px;
+ margin: 0;
+}
+
+.code-snippets-card {
+ background: #fff;
+ border: 1px solid theme.$control-border;
+ border-radius: 5px;
+ margin: 0;
+ display: flex;
+ flex-flow: column;
+ box-sizing: border-box;
+
+ a {
+ text-decoration: none;
+ color: theme.$accent;
+ }
+
+ .card-inner {
+ padding: 24px;
+ }
+
+ footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ background: #f7f7f8;
+ margin-block-start: auto;
+ border-block-start: 1px solid theme.$control-border;
+ padding-inline: 24px;
+ padding-block: 12px;
+ border-end-start-radius: 5px;
+ border-end-end-radius: 5px;
+ }
+
+ // The status label shrinks and clips away under pressure rather than
+ // pushing the action buttons onto a second line.
+ .snippet-card-footer-status {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 0 1 auto;
+ min-inline-size: 0;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+
+ .snippet-card-footer-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: nowrap;
+ gap: 8px;
+ flex: 0 0 auto;
+
+ .button:not(.kebab-menu-trigger) {
+ min-inline-size: 103px;
+ text-align: center;
+ justify-content: center;
+ }
+ }
+
+ &.is-selectable {
+ position: relative;
+ }
+
+ .snippet-card-corner {
+ position: absolute;
+ inset-block-start: 20px;
+ inset-inline-end: 24px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+ .snippet-card-select {
+ margin: 0;
+ }
+ }
+
+ input[type='checkbox'].snippet-card-select {
+ @include checkbox.canonical;
+ }
+
+ &.is-selectable .card-inner > h3 {
+ margin-inline-end: 48px;
+ }
+
+ &.is-selected {
+ border-color: theme.$accent;
+ box-shadow: 0 0 0 1px theme.$accent;
+ }
+}
diff --git a/src/css/common/_checkbox.scss b/src/css/common/_checkbox.scss
new file mode 100644
index 000000000..91d507b6f
--- /dev/null
+++ b/src/css/common/_checkbox.scss
@@ -0,0 +1,39 @@
+@use 'theme';
+
+@mixin canonical {
+ appearance: none;
+ display: inline-grid;
+ place-content: center;
+ vertical-align: middle;
+ inline-size: 20px;
+ block-size: 20px;
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+ background: #fff;
+ border: 1.5px solid theme.$control-border;
+ border-radius: 5px;
+ box-shadow: 0 2px 2px rgb(0 0 0 / 5%);
+ cursor: pointer;
+
+ &:focus {
+ outline: 2px solid var(--wp-admin-theme-color);
+ }
+
+ &::before {
+ content: none;
+ }
+
+ &:checked {
+ background: theme.$accent;
+ border-color: theme.$accent;
+
+ &::before {
+ content: '';
+ inline-size: 20px;
+ block-size: 20px;
+ margin: 0;
+ background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M14.83 4.89l1.34.94-5.81 8.38H9.02L5.78 9.67l1.34-1.25 2.57 2.4z' fill='%23fff'/%3E%3C/svg%3E") center/20px no-repeat;
+ }
+ }
+}
diff --git a/src/css/common/_codemirror.scss b/src/css/common/_codemirror.scss
index f92e36d0a..f6852c474 100644
--- a/src/css/common/_codemirror.scss
+++ b/src/css/common/_codemirror.scss
@@ -1,4 +1,4 @@
-.CodeMirror {
+.CodeMirror, .snippet-condition-editor-container {
border: 1px solid #dfdfdf;
border-radius: 3px;
block-size: auto !important;
@@ -102,6 +102,10 @@
color: #666;
}
+.CodeMirror .codemirror-colorview {
+ border: 0;
+}
+
.CodeMirror-foldmarker {
color: inherit;
margin-inline: 0.25em;
diff --git a/src/css/common/_kebab-menu.scss b/src/css/common/_kebab-menu.scss
new file mode 100644
index 000000000..83f225eac
--- /dev/null
+++ b/src/css/common/_kebab-menu.scss
@@ -0,0 +1,106 @@
+@use 'theme';
+
+// Generic kebab ("more actions") menu: a square accent-outlined trigger
+// button that opens a small popover of actions anchored to its
+// bottom-end corner, flipping above the trigger near the viewport edge.
+.kebab-menu {
+ position: relative;
+ display: inline-flex;
+}
+
+.kebab-menu-trigger {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ inline-size: 38px;
+ block-size: 38px;
+ box-sizing: border-box;
+ padding: 0;
+ background: #fff;
+ color: theme.$accent;
+ border: 1px solid theme.$accent;
+ border-radius: 5px;
+ cursor: pointer;
+
+ svg {
+ display: block;
+ }
+
+ &:hover,
+ &:focus {
+ background: #f0f6fc;
+ }
+}
+
+.kebab-menu-popover {
+ position: absolute;
+ inset-block-start: calc(100% + 4px);
+ inset-inline-end: 0;
+ z-index: 1000;
+ inline-size: 224px;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 6px 0;
+ list-style: none;
+ background: #fff;
+ border: 1px solid #e2e2e4;
+ border-radius: 5px;
+ overflow: hidden;
+ box-shadow: 0 3px 16px rgb(0 0 0 / 15%);
+
+ &.kebab-menu-popover-top {
+ inset-block: auto calc(100% + 4px);
+ }
+
+ li {
+ margin: 0;
+ }
+}
+
+.kebab-menu-item {
+ display: block;
+ inline-size: 100%;
+ box-sizing: border-box;
+ padding-block: 10px;
+ padding-inline: 14px;
+ background: none;
+ border: none;
+ font-family: inherit;
+ font-size: 14px;
+ line-height: 1.4;
+ color: #2c3337;
+ text-align: start;
+ cursor: pointer;
+
+ &:hover,
+ &:focus {
+ background: #f0f0f1;
+ }
+
+ &:disabled {
+ color: #a7aaad;
+ cursor: default;
+ background: none;
+ }
+
+ &.kebab-menu-item-destructive {
+ color: #d63638;
+ }
+}
+
+.kebab-menu-divider {
+ margin-block: 6px;
+ border-block-start: 1px solid #e2e2e4;
+}
+
+.kebab-menu-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ box-sizing: border-box;
+ padding-block: 10px;
+ padding-inline: 14px;
+ font-size: 14px;
+ line-height: 1.4;
+ color: #2c3337;
+}
diff --git a/src/css/common/_list-table.scss b/src/css/common/_list-table.scss
new file mode 100644
index 000000000..89c5eb866
--- /dev/null
+++ b/src/css/common/_list-table.scss
@@ -0,0 +1,4 @@
+@use 'list-table/layout';
+@use 'list-table/navigation';
+@use 'list-table/pagination';
+@use 'list-table/responsive';
diff --git a/src/css/common/_modal.scss b/src/css/common/_modal.scss
index dab7cca7b..bda203688 100644
--- a/src/css/common/_modal.scss
+++ b/src/css/common/_modal.scss
@@ -51,3 +51,119 @@
}
}
}
+
+.components-modal__frame.code-snippets-preview-modal {
+ min-inline-size: 520px;
+ min-block-size: 240px;
+ inline-size: min(900px, 90vw);
+ max-block-size: 85vh;
+
+ @media (width <= 600px) {
+ min-inline-size: 90vw;
+ }
+
+ .components-modal__header {
+ border-block-end-color: #ddd;
+ }
+
+ .code-snippets-preview-modal__badge {
+ display: flex;
+ flex-shrink: 0;
+ margin-inline-end: 8px;
+ }
+
+ // Header and footer stay pinned; the CodeMirror editor is the only
+ // scroll region, scrolling long code both vertically and horizontally.
+ .components-modal__content {
+ display: flex;
+ flex-flow: column;
+ min-block-size: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+
+ // Newer Modal versions wrap children in an unstyled focus container.
+ .components-modal__header + div {
+ display: flex;
+ flex-flow: column;
+ flex: 1;
+ min-block-size: 0;
+ overflow: hidden;
+ }
+
+ .code-snippets-preview-modal__editor {
+ display: flex;
+ flex-flow: column;
+ flex: 1;
+ min-block-size: 0;
+
+ .CodeMirror {
+ flex: 1;
+ min-block-size: 0;
+ block-size: 100%;
+ }
+
+ textarea {
+ flex: 1;
+ min-block-size: 0;
+ inline-size: 100%;
+ resize: none;
+ font-family: monospace;
+ }
+ }
+
+ .code-snippets-preview-modal__footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-shrink: 0;
+ gap: 16px;
+ padding: 11px 19px;
+ border-block-start: 1px solid #e2e2e4;
+ background: #f6f7f7;
+ }
+
+ .code-snippets-preview-modal__priority {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ color: #646970;
+
+ .code-snippets-preview-modal__priority-value {
+ font-weight: 600;
+ color: #2c3337;
+ }
+ }
+
+ .code-snippets-preview-modal__buttons {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .button {
+ border-radius: 5px;
+ font-size: 14px;
+ }
+
+ .button-link.code-snippets-preview-modal__trash {
+ min-block-size: 38px;
+ padding-block: 0;
+ padding-inline: 16px;
+ border: 1px solid #b32d2e;
+ border-radius: 5px;
+ background: #f6f7f7;
+ line-height: 2.5715;
+ font-weight: 400;
+ color: #b32d2e;
+ text-decoration: none;
+
+ &:hover,
+ &:focus {
+ border-color: #d63638;
+ background: #f0f0f1;
+ color: #d63638;
+ }
+ }
+ }
+}
diff --git a/src/css/common/_notices.scss b/src/css/common/_notices.scss
new file mode 100644
index 000000000..8d5f6909c
--- /dev/null
+++ b/src/css/common/_notices.scss
@@ -0,0 +1,53 @@
+@use '../common/theme';
+@use 'sass:map';
+@use 'sass:list';
+
+@mixin notices {
+ .notice {
+ border: 0;
+ border-radius: 5px;
+ display: flex;
+ align-items: center;
+ padding: 6px 10px;
+ gap: 8px;
+ margin: 0;
+
+ .notice-dismiss {
+ position: unset;
+ margin-inline-start: auto;
+ padding: 0;
+
+ &, &::before {
+ color: inherit;
+ }
+ }
+
+ .wp-core-ui &.is-dismissible {
+ position: unset;
+ padding-inline-end: 10px;
+ }
+ }
+
+ @each $name, $colors in theme.$notices {
+ .notice-#{$name} {
+ color: list.nth($colors, 2);
+ background-color: list.nth($colors, 1);
+ }
+ }
+
+ .notice-success::before {
+ content: '✓';
+ font-weight: bold;
+ font-size: 16px;
+ }
+}
+
+
+.code-snippets-notice {
+ .notice-dismiss {
+ position: absolute;
+ transform: initial;
+ inset-inline-end: 0;
+ inset-block-start: 0;
+ }
+}
diff --git a/src/css/common/_page-header.scss b/src/css/common/_page-header.scss
new file mode 100644
index 000000000..60d07f53d
--- /dev/null
+++ b/src/css/common/_page-header.scss
@@ -0,0 +1,52 @@
+@use 'theme';
+
+// Contextual page header row shared by the plugin admin screens: the page
+// title on the inline-start side and the primary page action, if any,
+// aligned to the inline-end side of the same row.
+.snippets-page-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 16px 24px;
+ margin-block: 8px 16px;
+
+ h1,
+ h2 {
+ margin: 0;
+ padding: 0;
+ font-size: 26px;
+ font-weight: 510;
+ line-height: 1.25;
+ }
+
+ h1 {
+ color: #1d2327;
+ }
+
+ h2 {
+ color: theme.$control-text;
+ }
+}
+
+.wrap .snippets-page-header .button.button-primary {
+ padding: 12px;
+ min-block-size: 0;
+ border-radius: 5px;
+ line-height: 1;
+ font-size: 14px;
+ font-weight: 700;
+}
+
+// One-line supporting description shown directly beneath the page title.
+.snippets-page-description {
+ margin-block: 0 16px;
+ font-size: 14px;
+ line-height: 1.5;
+ color: #646970;
+
+ a {
+ color: theme.$accent;
+ text-decoration: underline;
+ }
+}
diff --git a/src/css/common/_select.scss b/src/css/common/_select.scss
index 452145df1..170e25eba 100644
--- a/src/css/common/_select.scss
+++ b/src/css/common/_select.scss
@@ -1,5 +1,5 @@
.code-snippets-select {
- input[type="text"]:focus {
+ input[type='text']:focus {
box-shadow: none;
}
}
diff --git a/src/css/common/_subnav.scss b/src/css/common/_subnav.scss
new file mode 100644
index 000000000..d0c5033a2
--- /dev/null
+++ b/src/css/common/_subnav.scss
@@ -0,0 +1,275 @@
+@use 'theme';
+
+// Snippet-type navigation: a boxed segmented control on a white band, with
+// each tab separated by a vertical rule and the active tab shown as a
+// filled tile.
+.snippet-type-nav-wrapper {
+ position: relative;
+
+ // Bleed across the full admin content width, flush against the plugin toolbar.
+ box-sizing: border-box;
+ min-block-size: 53px;
+ margin-block-start: -1px;
+ margin-inline-start: -22px;
+ inline-size: calc(100% + 42px);
+ overflow: hidden;
+ background: #fff;
+
+ &::before,
+ &::after {
+ position: absolute;
+ inset-block: 0;
+ z-index: 1;
+ inline-size: 32px;
+ content: '';
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 150ms ease;
+ }
+
+ &::before {
+ inset-inline-start: 0;
+ background: linear-gradient(to right, #fff, transparent);
+ }
+
+ &::after {
+ inset-inline-end: 0;
+ background: linear-gradient(to left, #fff, transparent);
+ }
+
+ &.has-scroll-start::before,
+ &.has-scroll-end::after {
+ opacity: 1;
+ }
+
+ @media (width <= 782px) {
+ margin-inline-start: -10px;
+ inline-size: calc(100% + 22px);
+ }
+}
+
+.snippet-type-nav {
+ // The tab band scrolls sideways instead of wrapping at any width.
+ overflow-x: auto;
+ scrollbar-width: thin;
+
+ // The row line is painted on the scrollport so it remains fixed while tabs scroll.
+ background: linear-gradient(to top, #e2e2e4 1px, #fff 1px);
+
+ @media (width <= 960px) {
+ li {
+ flex: 1 1 0;
+ }
+
+ .snippet-type-link {
+ inline-size: 100%;
+ min-inline-size: 100px;
+ padding-inline: 4px;
+ }
+ }
+
+ @media (width <= 782px) {
+ scrollbar-width: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+
+ ul {
+ display: flex;
+ align-items: stretch;
+ flex-wrap: nowrap;
+ min-block-size: 53px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+
+ li {
+ display: flex;
+ flex: 0 0 auto;
+ margin: 0;
+ }
+
+ button.snippet-type-link {
+ background: none;
+ border-block: none;
+ border-inline-start: none;
+ font-family: inherit;
+ }
+
+ .snippet-type-icon {
+ font-size: 22px;
+ inline-size: 24px;
+ block-size: 24px;
+ }
+
+ .snippet-type-link {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ min-inline-size: 100px;
+ padding-block: 0;
+ padding-inline: 24px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.5;
+ white-space: nowrap;
+ color: #646970;
+ text-decoration: none;
+ box-sizing: border-box;
+ border-inline-end: 1px solid #e2e2e4;
+
+ // The text-only "All Snippets" tab uses a slightly wider gap between
+ // its label and count than tabs that lead with a badge or icon.
+ &.all-type-link {
+ gap: 12px;
+ }
+
+ svg {
+ display: block;
+ inline-size: 24px;
+ block-size: 24px;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
+ color: #2c3337;
+ }
+
+ &.active-type {
+ background: #f0f0f1;
+ color: theme.$accent;
+ }
+
+ // Suppress the persistent focus ring left behind after a mouse click;
+ // keyboard navigation still gets a visible outline via :focus-visible.
+ &:focus-visible {
+ outline: 2px solid theme.$accent;
+ outline-offset: -2px;
+ box-shadow: none;
+ }
+
+ &:focus:not(:focus-visible) {
+ outline: none;
+ box-shadow: none;
+ }
+
+ &.pro-locked-type {
+ color: #646970;
+ }
+ }
+
+}
+
+// Card/table view toggle: a two-icon segmented pair of square tiles with
+// the active view filled in the accent colour.
+.snippet-view-toggle {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .snippet-view-toggle-option {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ inline-size: 38px;
+ block-size: 38px;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+ background: #f0f0f1;
+ color: #646970;
+ border: none;
+ border-radius: 4.5px;
+ cursor: pointer;
+
+ svg {
+ display: block;
+ }
+
+ &:hover,
+ &:focus {
+ color: #2c3337;
+ }
+
+ &.active-view {
+ background: theme.$accent;
+ color: #fff;
+
+ &:hover,
+ &:focus {
+ color: #fff;
+ }
+ }
+ }
+}
+
+// Slot that adopts the WordPress Screen Options / Help tabs so they appear
+// directly below the snippet-type nav instead of above the page content.
+.snippets-screen-meta-slot {
+ #screen-meta-links {
+ float: none;
+ display: flex;
+ justify-content: flex-end;
+ margin: 0;
+ border-color: #e2e2e4;
+
+ .show-settings {
+ border-color: #e2e2e4;
+ }
+ }
+
+ #screen-meta {
+ margin: 0;
+ }
+}
+
+// Item count shown after a subnav tab's label, using the label colour at reduced opacity.
+.snippet-type-nav .subnav-count {
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.5;
+ color: currentcolor;
+ opacity: 0.65;
+}
+
+// Keep the active count tied to its active label rather than assigning a separate colour.
+.snippet-type-nav .snippet-type-link.active-type .subnav-count {
+ color: currentcolor;
+}
+
+// The short "All" form of the all-snippets label only replaces "All Snippets"
+// once the bar collapses; the full form is shown at every wider width.
+.snippet-type-name-short {
+ display: none;
+}
+
+// Collapse at mid-width and below (through mobile): drop the type names so each
+// tab shows just its badge and count, and shorten "All Snippets" to "All". Only
+// the badged manage nav owns a .snippet-type-name; the text-only import/settings
+// subnav tabs are untouched. Below 782px the bar still scrolls sideways (above),
+// now with the compact collapsed tabs.
+@media (width <= 1210px) {
+ .snippet-type-nav .snippet-type-link:not(.all-type-link) .snippet-type-name {
+ display: none;
+ }
+
+ .snippet-type-nav .all-type-link .snippet-type-name-full {
+ display: none;
+ }
+
+ .snippet-type-nav .all-type-link .snippet-type-name-short {
+ display: inline;
+ }
+}
+
+// When an admin notice pushes the page content down, restore the top border
+// above the subnav bar (no extra margin — the wrapper sits flush).
+.notice + .wrap .snippet-type-nav-wrapper {
+ border-block-start: 1px solid #e2e2e4;
+}
diff --git a/src/css/common/_switch.scss b/src/css/common/_switch.scss
index 3704c81fa..32e5f2145 100644
--- a/src/css/common/_switch.scss
+++ b/src/css/common/_switch.scss
@@ -1,7 +1,5 @@
@use 'theme';
-$off-color: #789;
-
.snippet-execution-button,
.snippet-activation-switch,
input[type='checkbox'].switch {
@@ -9,17 +7,19 @@ input[type='checkbox'].switch {
position: relative;
}
+// Activation switch: an accent-coloured 36x19 pill shared by the list-table
+// activate column, snippet cards and settings toggles.
.snippet-activation-switch,
input[type='checkbox'].switch {
appearance: none;
outline: 0;
cursor: pointer;
margin: 0;
- inline-size: 32px;
+ inline-size: 36px;
block-size: 19px;
border-radius: 34px;
text-align: start;
- border: 1px solid $off-color;
+ border: 1px solid theme.$accent;
box-sizing: border-box;
&::before {
@@ -28,19 +28,24 @@ input[type='checkbox'].switch {
block-size: 13px;
inline-size: 13px;
display: inline-block;
- background-color: $off-color;
+ background-color: theme.$accent;
border-radius: 50%;
margin: 2px;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
}
}
.active-snippet .snippet-activation-switch,
input[type='checkbox'].switch:checked {
- background-color: #0073aa;
+ background-color: theme.$accent;
+ // Travel distance: pill width minus the knob, its margins and the borders.
&::before {
background-color: white;
- transform: translateX(calc(100% * var(--cs-direction-multiplier)));
+ transform: translateX(calc((100% + 4px) * var(--cs-direction-multiplier)));
}
}
@@ -50,7 +55,7 @@ input[type='checkbox'].switch:checked {
text-align: center;
font-weight: bold;
line-height: 1;
- color: #bbb;
+ color: #1d2327;
}
a.snippet-condition-count {
@@ -98,5 +103,18 @@ a.snippet-condition-count {
border-color: theme.$accent;
transition: border-color 0.6s;
}
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+
+ &::before {
+ transition-duration: 0.01s;
+ }
+ }
}
}
+
+.code-snippets-card .snippet-execution-button {
+ margin-block-start: 0;
+ margin-inline-end: 9px;
+}
diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss
index 4cb4346ce..f4f40c544 100644
--- a/src/css/common/_theme.scss
+++ b/src/css/common/_theme.scss
@@ -9,30 +9,61 @@ $brand-facebook: #3b5998;
$cloud: #00bcd4;
$cloud-update: #ff9800;
+// Plugin admin control tokens, applied to native form controls on all plugin
+// screens regardless of WordPress version (see common/_wp-admin.scss).
+$accent-hover: #0a4b78;
+$control-border: #c3c4c7;
+$control-text: #2c3337;
+$control-height: 38px;
+$control-radius: 5px;
+
+// Runtime design tokens. Sass variables keep compile-time helpers available;
+// custom properties expose the approved system to every rendered component.
+:root {
+ --cs-color-accent: #2271b1;
+ --cs-color-accent-hover: #0a4b78;
+ --cs-color-text: #2c3337;
+ --cs-color-text-muted: #646970;
+ --cs-color-surface: #fff;
+ --cs-color-surface-subtle: #f6f7f7;
+ --cs-color-border: #c3c4c7;
+ --cs-color-border-subtle: #dcdcde;
+ --cs-control-height: 38px;
+ --cs-control-radius: 5px;
+ --cs-font-size-body: 14px;
+ --cs-line-height-body: 1.5;
+}
+
/* format: background-color [color] */
$badges: (
- php: #1d97c6,
- html: #ef6a36,
+ // php/html/cond darkened for WCAG AA — at least 4.5:1 against white 12px
+ // bold text; php reuses $accent, html/cond keep hue at reduced lightness.
+ php: $accent,
+ html: #cd4510,
css: #9b59b6,
- js: #ffeb3b #1c1f20,
- cond: #2eae95,
- core: #61c5cb,
+ js: #f7d67a #1c1f20,
+ cond: #22826f,
+ core: #4fa1a6,
pro: #f7e8e3 #df9279,
- cloud: $cloud,
+ cloud: #009fb4,
bundles: #50575e,
- cloud_search: #ff9800,
- private: #f7e6be #ca961b,
+ cloud_search: #d27c00,
+ private: #f7e6be #a27813,
public: #dbebf7 $accent,
success: #d3e8d1 #447340,
failure: #fad7c1 #a24b16,
info: #d2e6f4 #2b71a3,
- neutral: #e2e5e5 #6c7e7e,
+ neutral: #f6f7f7 #646970,
+ neutralDark: #e2e5e5 #6c7e7e,
special: #dfc5ef #6e249c
);
$notices: (
success: #d3e9d3 #377a37,
warning: #f2ebc3 #b0730a,
error: #f8d7da #721c24,
+ info: #d2e6f4 #2b71a3,
+ neutral: #e2e5e5 #6c7e7e,
+ special: #dfc5ef #6e249c
);
@function contrasting-text-color($bg-color) {
diff --git a/src/css/common/_toolbar.scss b/src/css/common/_toolbar.scss
new file mode 100644
index 000000000..d3a16967a
--- /dev/null
+++ b/src/css/common/_toolbar.scss
@@ -0,0 +1,331 @@
+@use 'theme';
+@use 'upsell';
+
+$toolbar-block-size: 136px;
+$wpcontent-inline-start-indent: 20px;
+
+#wpbody {
+ padding-block-start: $toolbar-block-size;
+}
+
+.code-snippets-toolbar {
+ color: #2c3337;
+ background: #fff;
+ font-family: 'SF Pro', sans-serif;
+ margin-inline-start: -$wpcontent-inline-start-indent - 2;
+ position: absolute;
+ inline-size: calc(100% + $wpcontent-inline-start-indent);
+ block-size: $toolbar-block-size;
+ inset-block-start: 0;
+ display: flex;
+ flex-direction: column;
+
+ ul, li {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+}
+
+.code-snippets-toolbar-upper {
+ justify-content: space-between;
+ block-size: 76px;
+ box-sizing: border-box;
+ padding-block: 16px;
+ padding-inline: 24px;
+ border-block-end: 1px solid rgb(195 196 199 / 50%);
+ display: flex;
+ align-items: center;
+
+ .logo {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ img {
+ block-size: 42px;
+ }
+
+ div {
+ font-size: 18px;
+ font-weight: 700;
+ }
+ }
+
+ h1 {
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.5;
+ }
+
+ ul {
+ display: flex;
+ gap: 22px;
+ align-items: center;
+ }
+
+ a {
+ color: inherit;
+ text-decoration: none;
+ }
+
+ @media (width <= 480px) {
+ padding-inline: 10px;
+
+ .logo img {
+ block-size: 34px;
+ }
+
+ .logo div {
+ display: none;
+ }
+
+ .toolbar-upgrade-item .button {
+ padding-inline: 10px;
+ }
+ }
+}
+
+.code-snippets-toolbar-lower {
+ border-block-end: 1px solid #e2e2e4;
+ block-size: 60px;
+ box-sizing: border-box;
+
+ nav,
+ ul {
+ block-size: 100%;
+ }
+
+ ul {
+ display: flex;
+ align-items: stretch;
+ }
+
+ li {
+ display: flex;
+ }
+
+ // Items fill the 60px band; the active tab is indicated by accent colour on
+ // its icon and label, with no underline.
+ li a {
+ display: flex;
+ margin: 0;
+ block-size: 100%;
+ padding-block: 0;
+ padding-inline: 24px;
+ color: #2c3337;
+ gap: 12px;
+ align-items: center;
+ font-size: 16px;
+ font-weight: 600;
+ letter-spacing: -0.16px;
+ text-decoration: none;
+ box-sizing: border-box;
+ cursor: pointer;
+
+ svg {
+ inline-size: 26px;
+ block-size: 26px;
+ }
+
+ &.active-link, &:hover, &:focus, &:active {
+ color: theme.$accent;
+ z-index: 1;
+ }
+ }
+
+ li:not(.toolbar-end-item) + li.toolbar-end-item {
+ margin-inline-start: auto;
+ }
+
+ // Narrow widths: centre each item's label directly beneath its icon and
+ // shrink the type so the nav still fits on one row. The pro chip becomes a
+ // small badge pinned to the item's top corner, out of the flow so it never
+ // shifts the icon off-centre from its label. Below 782px the row scrolls
+ // sideways (see the mobile block below) rather than wrapping.
+ @media (width <= 1140px) {
+ li a {
+ position: relative;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 4px;
+ padding-inline: 14px;
+ font-size: 13px;
+ text-align: center;
+
+ .pro-chip {
+ position: absolute;
+ inset-block-start: 7px;
+ inset-inline-end: 4px;
+ padding-block: 1px;
+ padding-inline: 5px;
+ font-size: 8px;
+ line-height: normal;
+ }
+ }
+ }
+
+ // On narrow screens Settings remains available through the WordPress submenu.
+ // The remaining primary destinations fill the toolbar evenly rather than
+ // becoming a second horizontally-scrolling navigation row.
+ @media (width <= 782px) {
+ li.toolbar-end-item {
+ display: none;
+ }
+
+ li:not(.toolbar-end-item) {
+ flex: 1 1 0;
+ min-inline-size: 0;
+ }
+
+ li:not(.toolbar-end-item) + li.toolbar-end-item {
+ margin-inline-start: 0;
+ }
+
+ li a {
+ inline-size: 100%;
+ padding-inline: 8px;
+ white-space: normal;
+ }
+ }
+}
+
+.code-snippets-toolbar-upper li a.active-link,
+.code-snippets-toolbar-upper li a:hover,
+.code-snippets-toolbar-upper li a:focus,
+.code-snippets-toolbar-upper li a:active {
+ color: theme.$accent;
+}
+
+.code-snippets-toolbar-lower .pro-chip,
+.nav-tab .pro-chip,
+.snippet-type-link .pro-chip {
+ color: theme.$accent;
+ background: #eff5f9;
+ border: 1px solid rgb(34 113 177 / 10%);
+ text-transform: uppercase;
+ border-radius: 999px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-sizing: border-box;
+ padding: 5px 12px;
+ font-weight: 700;
+ line-height: 1;
+}
+
+
+.code-snippets-toolbar-lower .pro-chip {
+ font-size: 12px;
+}
+
+.nav-tab .pro-chip,
+.snippet-type-link .pro-chip {
+ font-size: 10px;
+}
+
+.code-snippets-return-link {
+ float: inline-end;
+}
+
+.code-snippets-toolbar-upper .toolbar-more-item {
+ display: none;
+}
+
+.code-snippets-toolbar-upper .toolbar-more-menu {
+ position: relative;
+
+ .dashicons, .dashicons::before {
+ inline-size: 16px;
+ block-size: 16px;
+ font-size: 16px;
+ }
+}
+
+.code-snippets-toolbar-upper .toolbar-more-menu summary {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ cursor: pointer;
+}
+
+.code-snippets-toolbar-upper .toolbar-more-menu ul {
+ position: absolute;
+ inset-block-start: calc(100% + 8px);
+ inset-inline-end: 0;
+ z-index: 2;
+ display: grid;
+ min-inline-size: 180px;
+ block-size: auto;
+ margin: 0;
+ padding: 8px;
+ background: #fff;
+ border: 1px solid theme.$control-border;
+ box-shadow: 0 3px 8px rgb(0 0 0 / 15%);
+}
+
+.code-snippets-toolbar-upper .toolbar-more-menu li {
+ margin: 0;
+}
+
+// The upper and lower toolbars are distinct navigation trees; keep this rule
+// after the lower-toolbar link state rules so the compact menu owns its hover state.
+// stylelint-disable-next-line no-descending-specificity
+.code-snippets-toolbar-upper .toolbar-more-menu a {
+ display: block;
+ padding: 8px;
+ white-space: nowrap;
+}
+
+.code-snippets-toolbar-upper .toolbar-more-menu a:hover,
+.code-snippets-toolbar-upper .toolbar-more-menu a:focus {
+ background: #f6f7f7;
+}
+
+@media (width <= 960px) {
+ .code-snippets-toolbar-upper {
+ gap: 12px;
+ padding-inline: 16px;
+ }
+
+ .code-snippets-toolbar-upper .logo {
+ flex: 0 1 auto;
+ min-inline-size: 0;
+ }
+
+ .code-snippets-toolbar-upper nav {
+ margin-inline-start: auto;
+ }
+
+ .code-snippets-toolbar-upper > nav > ul {
+ gap: 8px;
+ }
+
+ .code-snippets-toolbar-upper > nav > ul > li:not(.toolbar-upgrade-item, .toolbar-more-item) {
+ display: none;
+ }
+
+ .code-snippets-toolbar-upper .toolbar-more-item {
+ display: flex;
+ }
+
+ .code-snippets-toolbar-upper .toolbar-more-menu summary {
+ padding: 8px;
+ white-space: nowrap;
+ }
+
+ .code-snippets-toolbar-upper .toolbar-upgrade-item .button {
+ display: block;
+ max-inline-size: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+}
+
+@media (width <= 640px) {
+ .code-snippets-toolbar-upper .logo div {
+ display: none;
+ }
+}
diff --git a/src/css/common/_tooltips.scss b/src/css/common/_tooltips.scss
index 3feec5298..dfc7a99d0 100644
--- a/src/css/common/_tooltips.scss
+++ b/src/css/common/_tooltips.scss
@@ -1,38 +1,29 @@
-$bg-color: hsl(0deg 0% 20% / 90%);
+$bg-color: rgb(19 18 18 / 90%);
-.help-tooltip {
- display: inline-flex;
- flex-direction: column;
- justify-content: center;
- border-block-end: 1px dotted;
+.tooltip {
position: relative;
- vertical-align: middle;
-}
-
-.help-tooltip-anchor {
- cursor: help;
- padding-block: 0.3em 0;
- padding-inline: 0.3em;
display: inline-block;
- font-size: 10px;
- background: transparent !important;
}
-.tooltip {
- cursor: help !important;
- position: relative;
- display: inline-block;
+.tooltip-trigger {
+ display: inline-flex;
+ align-items: center;
+ background: transparent;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ border-radius: 50%;
+ color: inherit;
+ font: inherit;
- .dashicons {
- color: lightslategrey;
+ &:hover,
+ &:focus {
+ box-shadow: none;
}
- &.badge {
- display: inline-flex;
-
- .dashicons {
- color: inherit;
- }
+ &:focus-visible {
+ outline: 2px solid #2271b1;
+ outline-offset: 2px;
}
}
@@ -46,6 +37,10 @@ $bg-color: hsl(0deg 0% 20% / 90%);
transform 0.2s cubic-bezier(0.71, 1.7, 0.77, 1.24);
transform: translate3d(0, 0, 0);
pointer-events: none;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
}
.tooltip::before {
@@ -62,14 +57,15 @@ $bg-color: hsl(0deg 0% 20% / 90%);
.tooltip-content {
z-index: 1000;
padding: 8px;
- background-color: $bg-color;
+ background-color: rgb(19 18 18 / 90%);
color: #fff;
border-radius: 6px;
position: absolute;
- font-size: small;
+ font-size: 12px;
font-weight: normal;
text-transform: none;
- min-inline-size: 200px;
+ inline-size: max-content;
+ max-inline-size: 200px;
backdrop-filter: blur(3px);
.tooltip-block & {
@@ -148,7 +144,8 @@ $bg-color: hsl(0deg 0% 20% / 90%);
}
.tooltip:hover,
-.tooltip:focus {
+.tooltip:focus,
+.tooltip:focus-within {
&::before, .tooltip-content {
visibility: visible;
opacity: 1;
@@ -156,29 +153,54 @@ $bg-color: hsl(0deg 0% 20% / 90%);
}
.tooltip-block.tooltip-start:hover,
-.tooltip-block.tooltip-start:focus {
+.tooltip-block.tooltip-start:focus,
+.tooltip-block.tooltip-start:focus-within {
&::before, .tooltip-content {
transform: translateY(-10px);
}
}
.tooltip-block.tooltip-end:hover,
-.tooltip-block.tooltip-end:focus {
+.tooltip-block.tooltip-end:focus,
+.tooltip-block.tooltip-end:focus-within {
&::before, .tooltip-content {
transform: translateY(10px);
}
}
.tooltip-inline.tooltip-end:hover,
-.tooltip-inline.tooltip-end:focus {
+.tooltip-inline.tooltip-end:focus,
+.tooltip-inline.tooltip-end:focus-within {
&::before, .tooltip-content {
transform: translateX(calc(10px * var(--cs-direction-multiplier)));
}
}
.tooltip-inline.tooltip-start:hover,
-.tooltip-inline.tooltip-start:focus {
+.tooltip-inline.tooltip-start:focus,
+.tooltip-inline.tooltip-start:focus-within {
&::before, .tooltip-content {
transform: translateX(calc(-10px * var(--cs-direction-multiplier)));
}
}
+
+
+.help-tooltip {
+ cursor: help !important;
+
+ .tooltip-trigger {
+ cursor: help;
+ }
+
+ .dashicons {
+ color: lightslategrey;
+ }
+
+ &.badge {
+ display: inline-flex;
+
+ .dashicons {
+ color: inherit;
+ }
+ }
+}
diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss
index 7f6b54b17..afa61420c 100644
--- a/src/css/common/_upsell.scss
+++ b/src/css/common/_upsell.scss
@@ -1,4 +1,3 @@
-@use 'sass:color';
@use 'theme';
.code-snippets-upsell-banner {
@@ -26,7 +25,7 @@
margin-inline-start: auto;
&:hover, &:focus {
- background-color: color.adjust(theme.$secondary, $lightness: -10%);
+ background-color: #0ca0a9;
}
}
@@ -34,36 +33,14 @@
text-decoration: none;
font-weight: normal;
padding: 0;
- color: #a7aaad;
+ color: #646970;
line-height: 1;
+ border: none;
}
}
-.code-snippets-upsell-dialog {
- background: linear-gradient(116.04deg, #edfcff -0.75%, #fcdfd4 93.04%);
- inline-size: 794px;
- box-shadow: 0 4px 80px rgb(0 0 0 / 10%);
- border-radius: 8px;
- font-family: 'SF Pro', sans-serif;
- color: #1c3f41;
- line-height: 1.5;
- max-block-size: unset;
-
- .components-modal__content {
- margin: 0;
- padding-block: 48px;
- padding-inline: 80px;
- }
-
- .components-modal__content > div:last-child {
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
- gap: 18px;
- overflow: auto;
- }
-
+.code-snippets-upsell-dialog,
+.wrap .code-snippets-upsell-page {
h1 {
font-size: 32px;
margin: 0;
@@ -76,26 +53,20 @@
p {
margin-block: 0;
margin-inline: 2em;
- }
-
- h1 + p {
font-size: 16px;
+ max-inline-size: 750px;
}
img {
inline-size: 82px;
}
- h2 {
- text-transform: uppercase;
- font-size: 12px;
- }
-
ul {
display: grid;
inline-size: 100%;
grid-auto-flow: column;
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
+ max-inline-size: 750px;
}
li {
@@ -127,8 +98,57 @@
margin-inline: auto;
border-color: currentcolor;
- &:hover {
- background-color: color.adjust(#d46f4d, $lightness: -10%);
+ // Matches the specificity of the native-control override in _wp-admin.scss
+ // (.wrap .button-primary:not(.button-link):hover:not(:disabled)) so this
+ // upsell-specific hover styling isn't overridden by the shared button rule.
+ &:hover:not(.button-link, :disabled) {
+ background-color: #0ca0a9;
+ border-color: #0ca0a9;
}
}
}
+
+.code-snippets-upsell-page,
+.code-snippets-upsell-dialog .components-modal__content {
+ margin: 0;
+ padding-block: 48px;
+ padding-inline: 80px;
+}
+
+.code-snippets-upsell-page,
+.code-snippets-upsell-dialog {
+ color: #1c3f41;
+ line-height: 1.5;
+ background: linear-gradient(116.04deg, #edfcff -0.75%, #fcdfd4 93.04%);
+ box-shadow: 0 4px 80px rgb(0 0 0 / 10%);
+ border-radius: 8px;
+}
+
+// Bleed the upsell to the full width of the admin content area and fill the
+// remaining viewport height, so it reads as a full page rather than a small
+// boxed card floating in the subpage. 168px accounts for the WP admin bar
+// (32px) plus the plugin toolbar ($toolbar-block-size, 136px in _toolbar.scss).
+.code-snippets-upsell-page {
+ box-sizing: border-box;
+ margin-inline-start: -22px;
+ inline-size: calc(100% + 42px);
+ min-block-size: calc(100vh - 168px);
+ border-radius: 0;
+ justify-content: center;
+}
+
+.code-snippets-upsell-page,
+.code-snippets-upsell-dialog .components-modal__content > div:last-child {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ gap: 18px;
+}
+
+.code-snippets-upsell-dialog {
+ max-block-size: unset;
+ font-family: 'SF Pro', sans-serif;
+ inline-size: 794px;
+ overflow: auto;
+}
diff --git a/src/css/common/_wp-admin.scss b/src/css/common/_wp-admin.scss
new file mode 100644
index 000000000..1dd6f035a
--- /dev/null
+++ b/src/css/common/_wp-admin.scss
@@ -0,0 +1,185 @@
+@use 'checkbox';
+@use 'theme';
+
+// Match the active subnavigation tile so it blends into the admin canvas.
+body.wp-admin {
+ background: #f0f0f1;
+}
+
+// Remove the WordPress core 10px top margin on the manage snippets container
+// so the subnav band sits flush against the toolbar.
+#manage-snippets-container.wrap {
+ margin-block-start: 0;
+}
+
+/**
+ * Native form control styling.
+ *
+ * WordPress core ships different control metrics between versions (30/32px classic,
+ * 40px with a 2px radius in the WP 7.0 "modern" redesign) and recolours secondary
+ * buttons with the modern accent (#3858E9). These rules apply the plugin's own control
+ * tokens — 38px height, 5px radius, theme control borders and the brand accent —
+ * to native elements inside our admin pages so the layout renders consistently across
+ * WP versions. They deliberately leave the admin menu, admin bar and
+ * `@wordpress/components` React widgets untouched — only native inputs/buttons
+ * rendered inside our own markup are normalised.
+ */
+
+// Scope to our admin page content. We intentionally keep this to a single `.wrap` class so
+// the rules sit just above WordPress core defaults (which we must override) but below the
+// plugin's own more-specific per-control rules, which are emitted later in each bundle and so
+// win on equal specificity. This lets bespoke controls (conditions button, row-action links,
+// priority input, etc.) keep their intended styling without per-element exclusions here.
+.wrap,
+// Modals render through a portal on `document.body`, outside the page wrapper, so
+// the plugin's own dialogs are listed here to pick up the same control tokens.
+.code-snippets-preview-modal,
+.code-snippets-modal {
+ // Native single-line text controls and selects: token height + border.
+ // `@wordpress/components` inputs/selects (`.components-*`) keep their own design — they
+ // are excluded so React widgets such as the tags token field are left untouched.
+ // `.snippet-priority` (the borderless list-table priority field) is excluded so it keeps
+ // its plain transparent styling rather than gaining a bordered box.
+ input[type='text']:not([class*='components-'], [id^='react-select-']),
+ input[type='search']:not([class*='components-']),
+ input[type='number']:not([class*='components-'], .snippet-priority),
+ input[type='email']:not([class*='components-']),
+ input[type='url']:not([class*='components-']),
+ input[type='password']:not([class*='components-']),
+ input[type='tel']:not([class*='components-']) {
+ // `min-block-size` (not a fixed `block-size`) so controls floor at the token height but
+ // can still flex-stretch where the layout calls for it (e.g. the 54px cloud search bar).
+ min-block-size: var(--cs-control-height);
+ padding-block: 0;
+ padding-inline: 8px;
+ border: 1px solid var(--cs-color-border);
+ border-radius: var(--cs-control-radius);
+ line-height: 2;
+ color: var(--cs-color-text);
+ font-size: 14px;
+
+ &:focus {
+ border-color: var(--cs-color-accent);
+ box-shadow: 0 0 0 1px var(--cs-color-accent);
+ outline: 2px solid transparent;
+ }
+ }
+
+ // Selects: same metrics, but preserve room for the WP dropdown chevron on the inline-end
+ // side. WP 7.0 keeps the chevron as a background image; clamping both paddings to 8px (as
+ // for text inputs) would let the arrow overlap the text, so the inline-end padding is wider.
+ select:not([class*='components-']) {
+ min-block-size: var(--cs-control-height);
+ padding-block: 0;
+ padding-inline: 8px 24px;
+ border: 1px solid var(--cs-color-border);
+ border-radius: var(--cs-control-radius);
+
+ // WP 7.0 leaves a tall (≈38px) line-height on selects, pushing the value off-centre.
+ // Pin it so the text stays vertically centred within the token height.
+ line-height: 2;
+ color: var(--cs-color-text);
+ font-size: 14px;
+
+ &:focus {
+ border-color: var(--cs-color-accent);
+ box-shadow: 0 0 0 1px var(--cs-color-accent);
+ outline: 2px solid transparent;
+ }
+ }
+
+ // Secondary buttons: token geometry, the grey fill and the brand accent border/text in
+ // place of WP 7.0's transparent + modern-blue treatment. `.button-primary` is excluded
+ // so its solid fill below is not overwritten. The line-height centres text vertically
+ // in anchor-based buttons, which lack a button element's automatic centring.
+ .button:not(.button-primary, .button-link),
+ .button-secondary,
+ .page-title-action {
+ min-block-size: var(--cs-control-height);
+ padding-block: 0;
+ padding-inline: 12px;
+ border-radius: var(--cs-control-radius);
+ line-height: 2.5715;
+ font-size: 14px;
+ font-weight: 400;
+ background: #f6f7f7;
+ border-color: var(--cs-color-accent);
+ color: var(--cs-color-accent);
+
+ &:hover:not(:disabled) {
+ background: #f0f0f1;
+ border-color: var(--cs-color-accent-hover);
+ color: var(--cs-color-accent-hover);
+ }
+
+ &:focus:not(:disabled) {
+ border-color: #3582c4;
+ box-shadow: 0 0 0 1px #3582c4;
+ outline: 2px solid transparent;
+ }
+ }
+
+ // Primary buttons: WP 7.0 recolours these with the modern accent and drops the solid
+ // fill. Restore the classic solid brand-accent fill with white text. The `:not`
+ // matches the secondary rule's specificity so a `.button-small.button-primary`
+ // keeps the token height instead of collapsing to the 26px small size.
+ .button-primary:not(.button-link) {
+ min-block-size: var(--cs-control-height);
+ padding-block: 0;
+ padding-inline: 12px;
+ border-radius: var(--cs-control-radius);
+ line-height: 2.5715;
+ font-size: 14px;
+ font-weight: 700;
+ background: var(--cs-color-accent);
+ border-color: var(--cs-color-accent);
+ color: #fff;
+
+ &:hover:not(:disabled) {
+ background: var(--cs-color-accent-hover);
+ border-color: var(--cs-color-accent-hover);
+ color: #fff;
+ }
+
+ &:focus:not(:disabled) {
+ background: var(--cs-color-accent-hover);
+ border-color: var(--cs-color-accent-hover);
+ box-shadow: 0 0 0 1px #fff, 0 0 0 3px var(--cs-color-accent);
+ outline: 2px solid transparent;
+ }
+ }
+
+ // Small buttons stay compact rather than inheriting the 40px control height.
+ .button-small {
+ min-block-size: 26px;
+ line-height: 2.1818;
+ font-size: 11px;
+ }
+}
+
+.wp-core-ui .wrap .button .dashicons {
+ color: inherit;
+
+ // WP 7.0's taller button line-height leaks onto dashicon glyphs inside buttons (e.g. the
+ // conditions badge icon), pushing them out of vertical alignment. Reset it so icons stay
+ // centred — mirrors the existing `.button svg` rule for SVG icons.
+ line-height: 1;
+}
+
+// The Screen Options panel sits outside the page wrapper, in WordPress' own
+// screen meta region. These stylesheets are only loaded on the plugin's screens,
+// so its checkboxes are styled to match the ones in the page below it.
+#adv-settings {
+ input[type='checkbox'] {
+ @include checkbox.canonical;
+ }
+
+ // The canonical checkbox drops the margin WordPress relies on here, and the
+ // gap otherwise comes from whitespace in the markup, which differs between
+ // the fieldsets this plugin prints and the ones WordPress prints.
+ label {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ }
+}
diff --git a/src/css/common/list-table/_layout.scss b/src/css/common/list-table/_layout.scss
new file mode 100644
index 000000000..713742696
--- /dev/null
+++ b/src/css/common/list-table/_layout.scss
@@ -0,0 +1,335 @@
+@use '../badges';
+@use '../checkbox';
+@use '../theme';
+
+.column-name {
+ .extra-icons {
+ float: inline-end;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ }
+
+ .dashicons-lock {
+ color: #646970;
+ opacity: 0.7;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+}
+
+.active-snippet {
+ td, th {
+ background-color: rgb(120 200 230 / 6%);
+ }
+
+ th.check-column {
+ border-inline-start: 2px solid #2ea2cc;
+ }
+
+ .column-name > .snippet-name {
+ font-weight: 600;
+ }
+}
+
+.inactive-snippet {
+ @include theme.link-colors(#579);
+}
+
+.paging-input {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+}
+
+.wp-list-table {
+ .check-column input[type='checkbox'] {
+ @include checkbox.canonical;
+ }
+
+ // Shared cell rhythm for snippet and Cloud tables. Padding, rather than a
+ // fixed row height, keeps multi-line values comfortable and uncropped.
+ td,
+ th {
+ padding-block: 16px;
+ padding-inline: 8px;
+ }
+
+ th:first-child,
+ td:first-child {
+ padding-inline-start: 16px;
+ }
+
+ th:last-child,
+ td:last-child {
+ padding-inline-end: 16px;
+ }
+
+ // Shared horizontal rhythm for every snippet table, so the cloud table lines
+ // up with the snippets table rather than keeping the wider WordPress default.
+ th:not(.check-column),
+ td:not(.check-column) {
+ padding-inline: 8px;
+ }
+
+ td.column-id {
+ text-align: center;
+ }
+
+ tr {
+ background: #fff;
+ }
+
+ ol, ul {
+ margin-block: 0 1.5em;
+ margin-inline: 1.5em 0;
+ }
+
+ ul {
+ list-style: disc;
+ }
+
+ .sortable-column-title {
+ display: flex;
+ }
+
+ th.sortable .list-table-sort-button,
+ th.sorted .list-table-sort-button {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ inline-size: 100%;
+ margin: 0;
+ border: none;
+ background: none;
+ font: inherit;
+ color: theme.$accent;
+ cursor: pointer;
+ text-align: start;
+ overflow: hidden;
+ padding-block: 8px;
+ padding-inline: 0;
+ }
+
+ th.sortable .list-table-sort-button:focus-visible,
+ th.sorted .list-table-sort-button:focus-visible {
+ outline: 2px solid theme.$accent;
+ outline-offset: 2px;
+ border-radius: 2px;
+ }
+
+ // The type badge is wrapped in an unclassed link that lays out as an inline
+ // box shorter than the badge, so wrap the badge exactly and draw our own
+ // focus ring: the inherited one resolves to a transparent outline.
+ .column-type a {
+ @include badges.badge-link;
+ }
+
+ .row-actions {
+ color: #646970;
+ position: relative;
+ inset-inline-start: 0;
+
+ a:not(.delete) {
+ color: theme.$accent;
+
+ &:hover,
+ &:focus {
+ color: theme.$accent-hover;
+ }
+ }
+
+ // Row-action buttons render as plain inline links. On WP 7.0 the generic `.button`
+ // compatibility styling would otherwise give them a fill, border, radius and fixed
+ // height, so fully neutralise it here (this rule is emitted after the wp-admin layer
+ // at equal specificity, so it wins). Non-delete links inherit the accent text colour.
+ .button-link {
+ block-size: auto;
+ min-block-size: 0;
+ padding: 0;
+ border: 0;
+ border-radius: 0;
+ background: none;
+ box-shadow: none;
+ line-height: inherit;
+ font-weight: 400;
+ color: theme.$accent;
+
+ &:hover:not(:disabled),
+ &:focus:not(:disabled) {
+ background: none;
+ border: 0;
+ box-shadow: none;
+ }
+
+ &:hover:not(:disabled, .delete, .snippet-cloud-update),
+ &:focus:not(:disabled, .delete, .snippet-cloud-update) {
+ color: theme.$accent-hover;
+ }
+
+ &.delete {
+ color: #b32d2e;
+ }
+
+ &.snippet-cloud-update {
+ color: #ff851b;
+ }
+ }
+
+ .snippet-row-action-feedback,
+ .snippet-row-action-error {
+ display: inline-flex;
+ align-items: center;
+ gap: .5em;
+
+ .components-spinner {
+ margin: 0;
+ inline-size: 1em;
+ block-size: 1em;
+ }
+ }
+
+ .snippet-row-action-error {
+ color: #b32d2e;
+ }
+
+ .delete.disabled {
+ color: #a7aaad;
+ cursor: not-allowed;
+ pointer-events: none;
+ }
+ }
+
+ .column-activate {
+ text-align: center;
+
+ .snippet-activation-switch,
+ input[type='checkbox'].switch {
+ margin-inline: auto;
+ }
+ }
+
+ .clear-filters {
+ vertical-align: middle;
+ }
+
+ thead th.check-column,
+ thead td.check-column,
+ tfoot th.check-column,
+ tfoot td.check-column,
+ tbody th.check-column,
+ tbody td.check-column {
+ padding-block: 16px;
+ padding-inline-start: 8px;
+ vertical-align: middle;
+ }
+
+ // Line every checkbox up on the same starting edge. Rows flagged as active
+ // draw a 2px accent border on this cell, so the same width is reserved as a
+ // transparent border elsewhere; without it the header, footer and inactive
+ // rows sit 2px further in, and rows shift as snippets are toggled.
+ thead th.check-column,
+ thead td.check-column,
+ tfoot th.check-column,
+ tfoot td.check-column,
+ tbody tr:not(.active-snippet) th.check-column,
+ tbody tr:not(.active-snippet) td.check-column {
+ border-inline-start: 2px solid transparent;
+ }
+
+ .active-snippet, .inactive-snippet {
+ td, th {
+ box-shadow: inset 0 -1px 0 rgb(0 0 0 / 10%);
+ vertical-align: middle;
+ }
+ }
+
+ tr.active-snippet + tr.inactive-snippet th,
+ tr.active-snippet + tr.inactive-snippet td {
+ border-block-start: 1px solid rgb(0 0 0 / 3%);
+ box-shadow: inset 0 1px 0 rgb(0 0 0 / 2%), inset 0 -1px 0 #e1e1e1;
+ }
+
+ .delete {
+ color: #b32d2e;
+ }
+
+ a.delete:not(.disabled) {
+ &:hover, &:focus, &:active {
+ border-block-end: 1px solid #f00;
+ color: #f00;
+ }
+ }
+
+ td.column-date, th.column-date {
+ white-space: nowrap;
+ inline-size: 130px; /* fixed column width */
+ min-inline-size: 130px;
+ max-inline-size: 130px;
+ text-align: end;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ td.column-date .modified-column-content {
+ display: block;
+ text-align: start;
+ }
+
+ // Snippet names always stay on a single line, truncating gracefully;
+ // the full name is exposed through the title attribute.
+ td.column-name > .snippet-name {
+ display: block;
+ max-inline-size: min(15rem, 30vw);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &.truncate-row-values td.column-desc .snippet-description-content {
+ display: block;
+ max-inline-size: min(25rem, 45vw);
+ overflow: clip;
+ text-overflow: ellipsis;
+ }
+}
+
+.snippets-table-toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ // The preceding page header owns the 16px gap above; a `wp-header-end`
+ // sits between them and prevents margin collapse, so this row keeps only its
+ // own 16px gap below (to the tablenav) and no top margin of its own.
+ margin-block: 0 16px;
+
+ .subsubsub {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ margin: 0;
+ padding: 0;
+ font-size: 13px;
+ line-height: 1.5;
+
+ li {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ margin: 0;
+ padding: 0;
+ }
+
+ // A thin vertical rule precedes every status after the first, sized to the
+ // 13px text rather than the taller line box.
+ li + li::before {
+ content: '';
+ inline-size: 1px;
+ block-size: 13px;
+ background: theme.$control-border;
+ }
+ }
+}
diff --git a/src/css/common/list-table/_navigation.scss b/src/css/common/list-table/_navigation.scss
new file mode 100644
index 000000000..ffd19344e
--- /dev/null
+++ b/src/css/common/list-table/_navigation.scss
@@ -0,0 +1,58 @@
+@use '../theme';
+
+.tablenav {
+ margin: 16px 0;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+
+ // The row gap only shows once controls wrap onto a second row (see the
+ // tablet collapse below, where the selection + pagination cluster drops to
+ // its own row); the 18px column gap spaces the left cluster on one line.
+ gap: 12px 18px;
+}
+
+/* WP 7.0 changes the default admin link colour to the modern blue. Pin the classic accent on active rows (names and
+ row-action links) so links keep their original colour; inactive rows are handled by the muted link-colors mixin,
+ and the trash button keeps its dark red from the `.delete` rules. */
+.wp-list-table .active-snippet a {
+ color: theme.$accent;
+
+ &:hover,
+ &:focus {
+ color: theme.$accent-hover;
+ }
+}
+
+/* Status filter links (All | Active | Inactive | …): the current view reads as plain
+ body text while the remaining views are accent-coloured links; every count stays
+ in the body-text colour regardless of its link state. */
+/* stylelint-disable no-descending-specificity -- unrelated element; ordering is fine. */
+.subsubsub a.current {
+ color: #2c3337;
+}
+
+.subsubsub a:not(.current) {
+ color: theme.$accent;
+
+ &:hover,
+ &:focus {
+ color: theme.$accent-hover;
+ }
+}
+
+.snippets-table-toolbar .subsubsub a .count {
+ color: #2c3337;
+}
+/* stylelint-enable no-descending-specificity */
+
+.wp-core-ui .button.clear-filters {
+ vertical-align: baseline;
+}
+
+.snippet-type-description {
+ border-block-end: 1px solid #ccc;
+ margin: 0;
+ padding-block: 1em;
+ padding-inline: 0;
+}
diff --git a/src/css/common/list-table/_pagination.scss b/src/css/common/list-table/_pagination.scss
new file mode 100644
index 000000000..137f791fb
--- /dev/null
+++ b/src/css/common/list-table/_pagination.scss
@@ -0,0 +1,119 @@
+@use '../checkbox';
+@use '../theme';
+
+// The pagination group and view toggle wrap together as one end-pinned
+// cluster so narrow viewports never split them across rows.
+.tablenav .tablenav-end-group {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ margin-inline-start: auto;
+}
+
+.tablenav .tablenav-pages-nav {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .tablenav-pages {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin: 0;
+ }
+}
+
+// "Select all" control shown in the toolbar beside the bulk actions.
+.tablenav .tablenav-select-all {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+
+ input[type='checkbox'] {
+ @include checkbox.canonical;
+ }
+}
+
+.snippets-search-area {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+ search {
+ flex: 1 1 auto;
+ min-inline-size: 0;
+ }
+
+ .search-box {
+ float: none;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin: 0;
+
+ input[type='search'] {
+ flex: 1 1 auto;
+ min-inline-size: 0;
+ }
+ }
+}
+
+// Inside the results toolbar the search area is fixed to the design width,
+// with the input flexing to fill the available space.
+.tablenav .snippets-search-area {
+ inline-size: 237px;
+}
+
+.wrap .snippets-search-area input[type='search'] {
+ border-color: theme.$control-border;
+}
+
+// Tablet widths: let the search area grow to fill the remainder of its row
+// while the fixed-width selects keep their sizing and groups wrap naturally.
+@media (width <= 1024px) {
+ .tablenav .snippets-search-area {
+ inline-size: auto;
+ flex: 1 1 237px;
+ max-inline-size: 100%;
+ }
+}
+
+@media (width <= 782px) {
+ // Wide table columns scroll inside the results region instead of extending
+ // the WordPress admin document horizontally.
+ .snippets-list-view {
+ max-inline-size: 100%;
+ overflow-x: auto;
+ overscroll-behavior-inline: contain;
+ }
+
+ p.search-box {
+ float: inline-start;
+ position: initial;
+ margin-block: 1em 0;
+ margin-inline: 0;
+ block-size: auto;
+ }
+
+
+ // Mobile keeps the filters compact. Pagination remains below the results,
+ // avoiding a second navigation cluster above the table.
+ .tablenav.top .tablenav-pages-nav {
+ display: none;
+ }
+
+ .tablenav.top .tablenav-end-group {
+ margin-inline-start: 0;
+ }
+
+ .tablenav .snippets-search-area {
+ flex: 0 1 20rem;
+ inline-size: min(100%, 20rem);
+ max-inline-size: 20rem;
+ }
+
+ .tablenav .alignleft.actions {
+ display: flex;
+ }
+}
diff --git a/src/css/common/list-table/_responsive.scss b/src/css/common/list-table/_responsive.scss
new file mode 100644
index 000000000..245159842
--- /dev/null
+++ b/src/css/common/list-table/_responsive.scss
@@ -0,0 +1,190 @@
+@use '../theme';
+
+// Toolbar squeeze: above the mobile breakpoint the row never wraps —
+// components shrink gracefully instead (flex-wrap would otherwise win
+// before flex-shrink ever engages). Wrapping remains the small-screen
+// fallback only.
+.snippets-list-view .tablenav {
+ flex-wrap: wrap;
+
+ @media (width > 782px) {
+ flex-wrap: nowrap;
+ }
+
+ .alignleft.actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ float: none;
+ margin: 0;
+ padding: 0;
+ flex: 0 1 auto;
+ min-inline-size: 0;
+
+ select {
+ inline-size: auto;
+ flex: 1 1 154px;
+ min-inline-size: 104px;
+ max-inline-size: 154px;
+ }
+ }
+
+ .tablenav-select-all {
+ flex-shrink: 0;
+ }
+
+ .snippets-search-area {
+ inline-size: auto;
+ flex: 1 1 237px;
+ min-inline-size: 150px;
+ max-inline-size: 237px;
+
+ // The inner search row never wraps — the field shrinks instead.
+ search,
+ form,
+ p.search-box {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+ gap: 8px;
+ min-inline-size: 0;
+ margin: 0;
+ }
+
+ input[type='search'] {
+ min-inline-size: 110px;
+ }
+
+ .button {
+ flex: 0 0 auto;
+ }
+ }
+
+ // Pagination labels stay on one line while the row squeezes.
+ .tablenav-pages,
+ .tablenav-pages .paging-input,
+ .tablenav-pages .tablenav-paging-text {
+ white-space: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ // Square pagination tiles with a compact page-number box between them.
+ // Enabled arrows pick up the accent border/text from the shared control
+ // styling; disabled arrows keep the native WordPress treatment.
+ .pagination-links {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ inline-size: 38px;
+ block-size: 38px;
+ min-block-size: 38px;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+ border-radius: 5.6px;
+ }
+
+ a.button {
+ color: theme.$accent;
+ }
+
+ .paging-input {
+ gap: 8px;
+ margin-inline: 4px;
+ }
+
+ .current-page {
+ inline-size: 45px;
+ min-block-size: 38px;
+ box-sizing: border-box;
+ margin: 0;
+ text-align: center;
+ color: #2c3337;
+ }
+
+ .tablenav-paging-text {
+ font-size: 14px;
+ color: #646970;
+ }
+ }
+
+ .tablenav-pages .displaying-num {
+ font-size: 14px;
+ color: #646970;
+ white-space: nowrap;
+ }
+
+ // The trailing float-clearing break would otherwise register as an
+ // empty flex item and contribute a stray column gap.
+ br.clear {
+ display: none;
+ }
+
+ .tablenav-end-group {
+ flex: 0 1 auto;
+ min-inline-size: 0;
+ }
+
+ // The item count is the first thing to give way when space runs short.
+ @media (width <= 1240px) {
+ .tablenav-pages .displaying-num {
+ display: none;
+ }
+ }
+}
+
+// Tablet collapse (iPad-class widths): rather than let the single toolbar row
+// overflow, break the top toolbar onto two rows. Row one keeps the working
+// controls — bulk actions, the tag filter and the search box (stretched to
+// fill the remainder). Row two carries the selection + navigation cluster:
+// "Select all" at the start, the pagination + view-toggle group pinned to the
+// end. A forced full-width break keeps the split deterministic regardless of
+// content width.
+@media (782px < width <= 1210px) {
+ .snippets-list-view .tablenav.top {
+ flex-wrap: wrap;
+ row-gap: 8px;
+
+ // Both rows spread edge-to-edge: the first control sits at the start,
+ // the last at the end, with the slack distributed between.
+ justify-content: space-between;
+
+ // Row one: bulk actions, tag filter, search — at its design width so the
+ // space-between slack lands in the gaps rather than stretching the field.
+ .bulkactions {
+ order: 1;
+ }
+
+ .alignleft.actions:not(.bulkactions) {
+ order: 2;
+ }
+
+ .snippets-search-area {
+ order: 3;
+ flex: 0 1 237px;
+ inline-size: 237px;
+ max-inline-size: 100%;
+ }
+
+ &::after {
+ content: '';
+ order: 4;
+ flex-basis: 100%;
+ block-size: 0;
+ }
+
+ // Row two: select all at the start, pagination + view-toggle at the end.
+ .tablenav-select-all {
+ order: 5;
+ }
+
+ .tablenav-end-group {
+ order: 6;
+ }
+ }
+}
diff --git a/src/css/edit.scss b/src/css/edit.scss
index 2485a6d43..6f270cea9 100644
--- a/src/css/edit.scss
+++ b/src/css/edit.scss
@@ -10,13 +10,17 @@
@use 'common/tooltips';
@use 'common/modal';
@use 'common/upsell';
+@use 'common/toolbar';
+@use 'common/wp-admin';
+@use 'common/list-table';
@use 'edit/form';
@use 'edit/sidebar';
@use 'edit/editor';
+@use 'edit/notices';
@use 'edit/conditions';
@use 'edit/gpt';
-.notice.error blockquote {
+.banner.error blockquote {
margin-block-end: 0;
}
@@ -27,7 +31,17 @@
margin: 0;
}
+#adminmenu a.code-snippets-edit-menu-link {
+ cursor: pointer;
+}
+
.snippet-description-container {
+ label {
+ font-size: 1.16em;
+ font-weight: 600;
+ text-transform: unset;
+ }
+
.wp-editor-tools {
padding-block-start: 5px;
}
@@ -44,20 +58,17 @@
}
}
-.components-form-token-field__input-container {
- background: #fff;
- border-color: #c3c4c7;
+.snippet-tags-container {
+ .components-form-token-field__input-container {
+ background: #fff;
+ border-color: #c3c4c7;
- > .components-flex {
- padding: 12px;
+ > .components-flex {
+ padding: 12px;
+ }
}
}
-#titlediv,
-.snippet-type-container {
- margin-block-end: 24px;
-}
-
.above-snippet-code {
margin-block: 0 15px;
display: flex;
@@ -65,15 +76,16 @@
margin-inline: 0;
gap: 8px;
- h2 {
- margin: 0;
+ label {
+ font-size: 1.16em;
+ font-weight: 600;
}
.expand-editor-button {
display: flex;
- gap: 5px;
align-items: center;
margin-inline-end: auto;
+ gap: 5px;
.dashicons {
inline-size: 18px;
@@ -83,6 +95,14 @@
}
}
+.wrap > h1:first-of-type {
+ font-size: 1.5rem;
+ font-weight: 400;
+ line-height: 1.3;
+ padding: 0;
+ margin-block: 50px 1rem;
+}
+
.snippet-name-wrapper {
display: flex;
gap: 0.5em;
@@ -95,13 +115,3 @@
form.condition-snippet .snippet-code-container {
display: none;
}
-
-.cs-back {
- cursor: pointer;
-
- &::before {
- content: '<';
- color: #2271b1;
- margin-inline-end: 3px;
- }
-}
\ No newline at end of file
diff --git a/src/css/edit/_editor.scss b/src/css/edit/_editor.scss
index 8063c8f68..2fd9e9a2b 100644
--- a/src/css/edit/_editor.scss
+++ b/src/css/edit/_editor.scss
@@ -59,9 +59,9 @@
}
.snippet-editor-help {
- position: absolute;
inset-inline-end: 5px;
inset-block-start: 5px;
+ position: absolute;
td {
&:first-child {
@@ -72,18 +72,27 @@
white-space: nowrap;
}
}
+}
+
+.mac-keyboard-shortcut {
+ text-align: end;
- .mac-key {
- display: none;
+ kbd {
+ font-family: inherit;
+ padding: 0;
+ margin: 0;
}
+}
- .platform-mac {
- .mac-key {
- display: inline;
- }
+.pc-keyboard-shortcut {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
- .pc-key {
- display: none;
- }
+ kbd {
+ font-family: inherit;
+ margin: 0;
+ padding-block: 0;
+ padding-inline: 2px;
}
}
diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss
index 45ead76fd..a9b1bbcee 100644
--- a/src/css/edit/_form.scss
+++ b/src/css/edit/_form.scss
@@ -3,12 +3,34 @@
$sidebar-width: 321px;
$sidebar-gap: 30px;
-.snippet-form #titlediv #title,
-.snippet-type-container {
- border-color: #ccc;
+.snippet-form #titlediv #title {
+ border-color: theme.$control-border;
block-size: 45px
}
+// The type selector is a react-select control that sizes to its content; pin
+// its bordered control box to the same 45px as the adjacent title field and
+// keep the selected value vertically centred within it.
+.snippet-type-container .code-snippets-select {
+ block-size: 100%;
+
+ // Target the control box only (:first-of-type). The open menu is a later
+ // sibling div; matching it here would clamp and flex-row the option list.
+ > div:first-of-type {
+ border-color: theme.$control-border;
+ min-block-size: 45px;
+ block-size: 45px;
+ max-block-size: 45px;
+ }
+
+ > div:first-of-type > div:first-child {
+ display: flex;
+ align-items: center;
+ block-size: 45px;
+ padding-block: 0;
+ }
+}
+
.snippet-type-option {
display: flex;
align-items: center;
@@ -16,26 +38,45 @@ $sidebar-gap: 30px;
flex-flow: row;
gap: 2em;
- .small-badge {
- margin-inline-start: 0.5em;
+ .snippet-type-option-main {
+ display: flex;
+ align-items: center;
+ gap: 8px;
}
- .badge {
- float: inline-end;
+ .small-badge {
+ margin-inline-start: 0.5em;
}
}
.conditions-editor-open {
+ .conditions-editor-header {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ gap: 8px;
+ margin-block-end: 8px;
+
+ strong {
+ font-size: 14px;
+ line-height: 20px;
+ }
+ }
+
.button.button-large {
- block-size: 100%;
display: flex;
align-items: center;
gap: 8px;
- overflow: hidden;
- border-color: #ccc;
- padding-block: 6px;
- padding-inline: 12px;
+ inline-size: 100%;
+ border-color: theme.$control-border;
+ background: transparent;
+ padding: 6px 12px;
+
+ &:hover:not(:disabled),
+ &:focus:not(:disabled) {
+ background: #f6f7f7;
+ }
}
&.no-condition .cond-badge {
@@ -44,7 +85,7 @@ $sidebar-gap: 30px;
border: 1px solid currentcolor;
}
- &.no-condition:hover .cond-badge {
+ &.no-condition:hover :not([disabled]) .cond-badge {
color: #fff;
background: theme.$accent;
border-color: theme.$accent;
@@ -59,6 +100,10 @@ $sidebar-gap: 30px;
grid-template-areas: 'upper sidebar' 'lower sidebar';
transition: all 700ms;
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
+
&.snippet-form-expanded {
grid-template-areas: 'upper upper' 'lower sidebar';
}
@@ -71,12 +116,26 @@ $sidebar-gap: 30px;
grid-area: lower;
}
+ .snippet-form-upper,
+ .snippet-form-lower {
+ display: flex;
+ flex-flow: column;
+ gap: 24px;
+ }
+
.snippet-editor-sidebar {
grid-area: span 3 / sidebar;
max-inline-size: $sidebar-width;
position: sticky;
- inset-block-start: 32px;
+
+ // Keep the sidebar clear of the fixed WordPress admin bar and leave a
+ // usable visual gap beneath it while the editor scrolls.
+ inset-block-start: 48px;
align-self: start;
+
+ @media (width <= 782px) {
+ position: static;
+ }
}
@media (width <= 1024px) {
@@ -97,3 +156,25 @@ $sidebar-gap: 30px;
}
}
}
+
+.above-editor-container {
+ display: flex;
+ flex-flow: row wrap;
+ gap: 1em;
+
+ > * {
+ flex: 1;
+ display: flex;
+ flex-flow: column;
+ min-inline-size: 180px;
+ }
+}
+
+// Match the location selector's control height to the adjacent conditions
+// button so the two sidebar controls line up. This targets a different
+// container than the type selector rules above, so their relative order does
+// not affect the cascade.
+// stylelint-disable-next-line no-descending-specificity
+.snippet-editor-sidebar .code-snippets-select.code-snippets-select-location > div:first-of-type {
+ min-block-size: 48px;
+}
diff --git a/src/css/edit/_gpt.scss b/src/css/edit/_gpt.scss
index 70702bb75..00778816b 100644
--- a/src/css/edit/_gpt.scss
+++ b/src/css/edit/_gpt.scss
@@ -12,7 +12,7 @@
box-shadow: none;
}
- .notice {
+ .banner {
margin-inline: 0;
}
}
@@ -32,6 +32,14 @@
}
}
+.snippet-tags-container {
+ label {
+ font-size: 1.16em;
+ font-weight: 600;
+ text-transform: unset;
+ }
+}
+
.code-line-explanation {
display: flex;
cursor: default;
diff --git a/src/css/edit/_notices.scss b/src/css/edit/_notices.scss
new file mode 100644
index 000000000..98cfd1584
--- /dev/null
+++ b/src/css/edit/_notices.scss
@@ -0,0 +1,28 @@
+
+.code-snippets-notice {
+ .notice-dismiss {
+ position: absolute;
+ transform: initial;
+ inset-inline-end: 0;
+ inset-block-start: 0;
+ }
+
+ details {
+ margin: .5em 0;
+ padding: 2px;
+
+ summary {
+ cursor: pointer;
+ }
+
+ pre {
+ max-inline-size: 100%;
+ overflow: auto hidden;
+ white-space: pre;
+ }
+
+ .stack-trace-hint {
+ opacity: 0.5;
+ }
+ }
+}
diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss
index 919ce70ef..3685abf51 100644
--- a/src/css/edit/_sidebar.scss
+++ b/src/css/edit/_sidebar.scss
@@ -1,66 +1,75 @@
@use '../common/theme';
-.code-snippets-modal {
- p h4 {
- margin-block-start: 0;
+.snippet-priority {
+ .priority-input-tooltip {
+ margin-inline-end: auto;
+ }
+
+ input {
+ inline-size: 4em;
}
}
-.snippet-editor-sidebar {
- .button-large {
- block-size: 48px;
+.code-snippets-copy-text.button {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+
+ .dashicons {
+ block-size: 18px;
+ inline-size: 18px;
+ font-size: 18px;
}
- .row-actions {
+ .spinner-wrapper {
+ block-size: 18px;
+ inline-size: 18px;
display: flex;
+ align-items: center;
+ }
- .button {
- background: none;
- border: none;
- }
+ .components-spinner {
+ block-size: 12px;
}
+}
- .delete-button {
- color: #cc1818;
+.activation-switch-container {
+ inline-size: 100%;
+ display: flex;
+ flex-flow: row;
+ gap: 5px;
+ justify-content: center;
+ align-items: center;
- &:hover {
- color: #9e1313;
- }
+ label {
+ font-weight: 600;
+ }
- &:focus {
- color: #710d0d;
- border-color: #710d0d;
- }
+ span:first-of-type {
+ margin-inline-start: auto;
}
+}
- .help-tooltip {
- margin-block: 0;
- margin-inline: 5px auto;
+.snippet-editor-sidebar {
+ .button-large {
+ block-size: 48px;
}
.box {
background-color: #fff;
- border: 1px solid #ccc;
+ border: 1px solid theme.$control-border;
border-radius: 4px;
padding: 1.5em;
display: flex;
flex-flow: column;
gap: 1em;
- h4 {
- margin-block: 0.5em;
- margin-inline: 0;
- }
-
.inline-form-field {
display: flex;
flex-flow: row wrap;
+ justify-content: space-between;
align-items: center;
gap: 5px;
-
- > :last-child {
- margin-inline-start: auto;
- }
}
.block-form-field {
@@ -68,23 +77,104 @@
flex-flow: column;
gap: 4px;
- h4 {
- margin-block-end: 0;
- }
}
}
- h4 .badge {
- float: inline-end;
+ .inline-form-field.row-actions {
+ display: flex;
- + .badge {
- margin-inline-end: 5px;
+ // Keep the export/download, trash and lock controls on a single row; the
+ // base .inline-form-field sets `row wrap`, which would otherwise drop the
+ // lock control onto a second line in the narrow sidebar.
+ flex-wrap: nowrap;
+ gap: 0;
+
+ // Lay the export + download pair out inline too — the group is a plain
+ // block by default, so its two links would otherwise stack vertically.
+ .snippet-export-buttons {
+ display: flex;
+ flex-wrap: nowrap;
+ gap: 12px;
+ min-inline-size: 0;
}
+
+ // Row-action buttons render as plain inline links. Neutralise the generic `.button`
+ // WP 7.0 compatibility styling (fill/border/radius/40px height) here — emitted after
+ // the wp-admin layer at equal specificity, so it wins. Export/Download inherit the accent.
+ .button {
+ block-size: auto;
+ min-block-size: theme.$control-height;
+ padding-block: 0;
+ padding-inline: 8px;
+ font-weight: 400;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+
+ .dashicons {
+ color: inherit;
+ line-height: 1;
+ vertical-align: text-bottom;
+ }
+
+ &:not(.snippet-lock-button) {
+ border: 0;
+ border-radius: 0;
+ padding-inline: 0;
+ background: none;
+ box-shadow: none;
+
+ // WordPress paints a fill on disabled buttons with !important;
+ // keep the disabled trash flat like the rest of the row.
+ &:disabled {
+ background: none !important;
+ border: 0;
+ box-shadow: none;
+ }
+
+ &:hover:not(:disabled),
+ &:focus:not(:disabled),
+ &:active:not(:disabled) {
+ background: none;
+ border: 0;
+ box-shadow: none;
+ }
+ }
+ }
+
+ // Nested under `.row-actions` so the dark-red trash colour outranks the generic
+ // `.button` accent text colour from the wp-admin compatibility layer.
+ .delete-button {
+ color: #cc1818;
+
+ &:disabled {
+ color: #a7aaad;
+ cursor: not-allowed;
+ }
+
+ &:hover:not(:disabled) {
+ color: #9e1313;
+ }
+
+ &:focus:not(:disabled) {
+ color: #710d0d;
+ }
+ }
+ }
+
+ .help-tooltip {
+ margin-block: 0;
+ margin-inline: 5px auto;
+ }
+
+ label {
+ font-weight: 600;
}
.beta-badge {
color: theme.$accent;
border: 1px solid currentcolor;
+ margin-inline-start: auto;
}
.components-form-token-field {
@@ -96,10 +186,6 @@
}
}
-.snippet-priority input {
- inline-size: 4em;
-}
-
p.submit {
display: flex;
flex-flow: column;
@@ -108,13 +194,6 @@ p.submit {
padding-block-start: 0;
}
-.activation-switch-container label {
- display: flex;
- flex-flow: row;
- gap: 5px;
- justify-content: center;
-}
-
.shortcode-tag-wrapper {
background: #fff;
min-block-size: 54px;
@@ -130,26 +209,3 @@ p.submit {
text-indent: -0.5em;
}
}
-
-.code-snippets-copy-text.button {
- display: flex;
- align-items: center;
- gap: 3px;
-
- .dashicons {
- block-size: 18px;
- inline-size: 18px;
- font-size: 18px;
- }
-
- .spinner-wrapper {
- block-size: 18px;
- inline-size: 18px;
- display: flex;
- align-items: center;
- }
-
- .components-spinner {
- block-size: 12px;
- }
-}
diff --git a/src/css/import.scss b/src/css/import.scss
new file mode 100644
index 000000000..7906ac04f
--- /dev/null
+++ b/src/css/import.scss
@@ -0,0 +1,13 @@
+@use 'common/checkbox';
+@use 'common/toolbar';
+@use 'common/subnav';
+@use 'common/wp-admin';
+@use 'common/page-header';
+@use 'import/page';
+@use 'import/card';
+@use 'import/upload';
+@use 'import/migrate';
+
+.wp-list-table .check-column input[type='checkbox'] {
+ @include checkbox.canonical;
+}
diff --git a/src/css/import/_card.scss b/src/css/import/_card.scss
new file mode 100644
index 000000000..4f96219e4
--- /dev/null
+++ b/src/css/import/_card.scss
@@ -0,0 +1,38 @@
+
+.snippets-table-card {
+ .wp-list-table {
+ border-radius: 5px;
+ }
+
+ th.check-column {
+ padding: 8px 0;
+ }
+
+ .tablenav {
+ display: flex;
+ gap: 10px;
+ block-size: auto;
+ padding: 0;
+ }
+
+ .tablenav.bottom {
+ margin-block-start: 1em;
+ }
+}
+
+.import-snippets-card {
+ background-color: #fff;
+ padding: 25px;
+ border-radius: 5px;
+ border: 1px solid #e0e0e0;
+ margin-block-end: 10px;
+ inline-size: 100%;
+ box-sizing: border-box;
+
+ &.status-display {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ margin-block-end: 20px;
+ }
+}
diff --git a/src/css/import/_migrate.scss b/src/css/import/_migrate.scss
new file mode 100644
index 000000000..d80360d20
--- /dev/null
+++ b/src/css/import/_migrate.scss
@@ -0,0 +1,149 @@
+
+.importer-selector-card {
+ h2 {
+ margin: 0 0 1em;
+ }
+
+ select {
+ display: block;
+ margin-block-start: 5px;
+ inline-size: 100%;
+ max-inline-size: 300px;
+ }
+
+ p {
+ margin: 10px 0 0;
+ color: #666;
+ font-size: 14px;
+ }
+}
+
+.import-options-card {
+ h3 {
+ margin: 0 0 1em;
+ }
+
+ label {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ cursor: pointer;
+
+ .description {
+ color: #666;
+ font-size: 0.9em;
+ }
+ }
+
+ input {
+ margin-block-start: 2px;
+ }
+
+ > div {
+ flex: 1;
+ }
+
+ .import-tag-entry {
+ margin-block-start: 12px;
+
+ input {
+ inline-size: 100%;
+ max-inline-size: 300px;
+ }
+ }
+}
+
+
+.import-section-status {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+
+ .error, .success {
+ border-radius: 50%;
+ inline-size: 24px;
+ block-size: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ margin-block-start: 2px;
+
+ span {
+ color: white;
+ font-size: 14px;
+ font-weight: bold;
+ }
+ }
+
+ .success {
+ background-color: #00a32a;
+ }
+
+ .error {
+ background-color: #d63638;
+ }
+
+ h4 {
+ margin: 0 0 8px;
+ font-size: 16px;
+ font-weight: 600;
+ }
+
+ p {
+ margin: 0;
+ color: #666;
+ }
+
+ a {
+ color: #2271b1;
+ text-decoration: none;
+ }
+}
+
+.no-snippets-card {
+ .card-inner {
+ text-align: center;
+ padding: 40px 20px;
+ color: #666;
+
+ p {
+ margin: 0;
+ font-size: 14px;
+ }
+ }
+
+ .card-icon {
+ font-size: 48px;
+ margin-block-end: 16px;
+ }
+
+ h4 {
+ margin: 0 0 8px;
+ font-size: 18px;
+ color: #333;
+ }
+
+}
+
+.migrate-snippets-table-card {
+ > div:first-child {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-block-end: 10px;
+
+ h3 {
+ margin: 0;
+ }
+
+ p {
+ margin: 0.5em 0 1em;
+ }
+ }
+
+ .column-id {
+ text-align: end;
+ inline-size: 50px;
+ }
+}
diff --git a/src/css/import/_page.scss b/src/css/import/_page.scss
new file mode 100644
index 000000000..885701a62
--- /dev/null
+++ b/src/css/import/_page.scss
@@ -0,0 +1,18 @@
+.wrap > h2:first-of-type {
+ font-size: 32px;
+ font-weight: 510;
+ line-height: 1.25;
+ color: #2c3337;
+ margin-block: 24px 16px;
+ padding: 0;
+}
+
+.import-snippets-section {
+ padding-block-start: 0;
+ display: none;
+ max-inline-size: 800px;
+
+ &.active-section {
+ display: block;
+ }
+}
diff --git a/src/css/import/_upload.scss b/src/css/import/_upload.scss
new file mode 100644
index 000000000..37c9ff4eb
--- /dev/null
+++ b/src/css/import/_upload.scss
@@ -0,0 +1,342 @@
+$type-colors: (
+ css: #9b59b6,
+ js: #ffeb3b,
+ html: #ef6a36
+);
+
+.import-upload-card {
+ h2 {
+ margin: 0 0 1em;
+ }
+
+ p.description {
+ margin-block-end: 1em;
+ }
+
+ footer {
+ text-align: center;
+
+ .button {
+ min-inline-size: 200px;
+ }
+ }
+}
+
+.upload-drop-zone {
+ border: 2px dashed #ccd0d4;
+ border-radius: 4px;
+ padding: 40px 20px;
+ text-align: center;
+ cursor: pointer;
+ background-color: #fafafa;
+ transition: all 0.3s ease;
+ opacity: 1;
+ display: block;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
+
+ &.drag-over {
+ border-color: #0073aa;
+ background-color: #f0f6fc;
+ }
+
+ &.disabled {
+ cursor: not-allowed;
+ background-color: #f6f7f7;
+ opacity: 0.6;
+ }
+
+ &:focus-visible {
+ outline: 2px solid #2271b1;
+ outline-offset: 2px;
+ }
+
+ .drop-zone-icon {
+ font-size: 48px;
+ margin-block-end: 20px;
+ color: #666;
+ }
+
+ p:first-of-type {
+ margin: 0 0 8px;
+ font-size: 16px;
+ font-weight: 500;
+ }
+
+ p:last-of-type {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+ }
+}
+
+.upload-drop-zone-wrapper {
+ position: relative;
+ margin-block-end: 20px;
+
+ &:focus-within .upload-drop-zone:not(.disabled) {
+ box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2271b1;
+ }
+}
+
+.upload-drop-zone-file-input {
+ position: absolute;
+ inline-size: 1px;
+ block-size: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+ clip-path: inset(50%);
+ white-space: nowrap;
+}
+
+.import-result-display-card {
+ > div {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ }
+}
+
+.selected-files {
+ margin-block-end: 20px;
+
+ h3 {
+ margin: 0 0 12px;
+ font-size: 14px;
+ font-weight: 600;
+ }
+}
+
+.selected-files-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ > div {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 12px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ background-color: #f9f9f9;
+ }
+
+ button {
+ background: none;
+ border: none;
+ color: #d63638;
+ cursor: pointer;
+ font-size: 16px;
+ padding: 4px;
+
+ &:focus-visible {
+ outline: 2px solid #2271b1;
+ outline-offset: 2px;
+ }
+ }
+
+ .selected-file-details {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ strong {
+ font-weight: 500;
+ }
+
+ .file-icon {
+ font-size: 16px;
+ }
+
+ .file-size {
+ font-size: 12px;
+ color: #666;
+ }
+ }
+}
+
+.duplicate-action-selector-card {
+ h2 {
+ margin: 0 0 1em;
+ }
+
+ p.description {
+ margin-block-end: 1em;
+ }
+
+ fieldset {
+ > div {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ label {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ cursor: pointer;
+ }
+
+ input {
+ margin-block-start: 2px;
+ }
+ }
+}
+
+.import-result {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+
+ > div:last-child {
+ flex: 1;
+ }
+
+ h2 {
+ margin: 0 0 8px;
+ font-size: 16px;
+ font-weight: 600
+ }
+
+ p.import-result-message {
+ margin: 0 0 8px;
+ color: #666;
+ }
+
+ p.import-result-link {
+ margin: 0;
+ color: #666;
+
+ a {
+ color: #2271b1;
+ text-decoration: none;
+ }
+ }
+
+ .import-result-warnings {
+ margin-block-start: 12px;
+
+ h3 {
+ margin: 0 0 8px;
+ font-size: 14px;
+ color: #d63638;
+ }
+
+ ul {
+ margin: 0;
+ padding-inline-start: 20px;
+ }
+
+ li {
+ color: #666;
+ font-size: 14px;
+ }
+ }
+}
+
+.import-result-icon {
+ border-radius: 50%;
+ inline-size: 24px;
+ block-size: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ margin-block-start: 2px;
+
+ .import-result-success & {
+ background-color: #00a32a;
+ }
+
+ .import-result-failure & {
+ background-color: #d63638;
+ }
+
+ span {
+ color: white;
+ font-size: 14px;
+ font-weight: bold;
+ }
+}
+
+.import-select-card {
+ .return-link {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-block-end: 20px;
+ }
+
+ .tablenav.top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-block-end: 10px;
+
+ h2 {
+ margin: 0;
+ }
+
+ p {
+ margin: 0.5em 0 1em;
+ color: #666;
+ }
+ }
+
+ .table-actions .button {
+ margin-inline-end: 10px;
+ }
+
+ .wp-list-table {
+ table-layout: fixed;
+ }
+
+ th.check-column {
+ inline-size: 40px;
+ }
+
+ th.column-name {
+ inline-size: 200px;
+ }
+
+ td.column-name div {
+ font-size: 12px;
+ color: #666;
+ margin-block-start: 2px;
+ }
+
+ .column-type {
+ inline-size: 90px;
+ text-align: center;
+
+ span {
+ background-color: #1d97c6;
+ color: white;
+ padding: 3px 6px;
+ font-size: 10px;
+ text-transform: uppercase;
+ border-radius: 3px;
+
+ @each $type, $color in $type-colors {
+ @at-root .import-select-card .#{$type}-snippet & {
+ background-color: $color;
+ }
+ }
+ }
+ }
+
+ th.column-desc {
+ inline-size: auto;
+ }
+
+ th.column-tags {
+ inline-size: 120px;
+ }
+}
diff --git a/src/css/manage.scss b/src/css/manage.scss
index f3a4a3737..79c481287 100644
--- a/src/css/manage.scss
+++ b/src/css/manage.scss
@@ -1,169 +1,43 @@
-/**
- * Custom styling for the snippets table
- */
-
-@use 'sass:map';
-@use 'sass:color';
@use 'common/theme';
@use 'common/badges';
@use 'common/switch';
+@use 'common/tooltips';
@use 'common/direction';
@use 'common/select';
-@use 'manage/cloud';
-
-.column-name,
-.column-type {
- .dashicons {
- font-size: 16px;
- inline-size: 16px;
- block-size: 16px;
- vertical-align: middle;
- }
-
- .dashicons-clock {
- vertical-align: middle;
- }
-}
-
-.active-snippet .column-name > .snippet-name {
- font-weight: 600;
-}
-
-.active-snippet {
- td, th {
- background-color: rgba(#78c8e6, 0.06);
- }
-
- th.check-column {
- border-inline-start: 2px solid #2ea2cc;
- }
-}
-
-.column-priority input {
- appearance: none;
- background: none;
- border: none;
- box-shadow: none;
- inline-size: 4em;
- color: #666;
- text-align: center;
-
- &:hover, &:focus, &:active {
- color: #000;
- background-color: #f5f5f5;
- background-color: rgb(0 0 0 / 10%);
- border-radius: 6px;
- }
-
- &:disabled {
- color: inherit;
- }
-}
-
-.clear-filters {
- vertical-align: baseline !important;
-}
-
-.snippets {
- td.column-id {
- text-align: center;
- }
-
- tr {
- background: #fff;
- }
-
- ol, ul {
- margin-block: 0 1.5em;
- margin-inline: 1.5em 0;
- }
-
- ul {
- list-style: disc;
- }
-
- th.sortable a, th.sorted a {
- display: flex;
- flex-direction: row;
- }
-
- .row-actions {
- color: #ddd;
- position: relative;
- inset-inline-start: 0;
- }
-
- .column-activate {
- padding-inline-end: 0 !important;
- }
-
- .clear-filters {
- vertical-align: middle;
- }
-
- tfoot th.check-column {
- padding-block: 13px 0;
- padding-inline: 3px 0;
- }
-
- thead th.check-column,
- tfoot th.check-column,
- .inactive-snippet th.check-column {
- padding-inline-start: 5px;
- }
-
- .active-snippet, .inactive-snippet {
- td, th {
- padding-block: 10px;
- padding-inline: 9px;
- border: none;
- box-shadow: inset 0 -1px 0 rgb(0 0 0 / 10%);
- }
- }
-
- tr.active-snippet + tr.inactive-snippet th,
- tr.active-snippet + tr.inactive-snippet td {
- border-block-start: 1px solid rgb(0 0 0 / 3%);
- box-shadow: inset 0 1px 0 rgb(0 0 0 / 2%), inset 0 -1px 0 #e1e1e1;
- }
+@use 'common/modal';
+@use 'common/notices';
+@use 'common/upsell';
+@use 'common/toolbar';
+@use 'common/subnav';
+@use 'common/wp-admin';
+@use 'common/page-header';
+@use 'common/kebab-menu';
+@use 'manage/snippets-table';
+@use 'manage/cloud-community';
+@use 'manage/cloud-community-cards';
+
+.tablenav .tablenav-pages {
+ margin-block-end: 0;
+}
+
+.create-snippet-button {
+ margin-inline-start: auto;
+ float: inline-end;
+ clear: both;
+ margin-block-start: 1em;
+}
+
+.nav-tab {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ gap: 8px;
- &, #all-snippets-table, #search-snippets-table {
- a.delete:hover {
- border-block-end: 1px solid #f00;
- color: #f00;
+ @media (width <= 1190px) {
+ span:first-child:not(:last-child) {
+ display: none;
}
}
-
- #wpbody-content & .column-name {
- white-space: nowrap; /* prevents wrapping of snippet title */
- }
-}
-
-td.column-description {
- max-inline-size: 700px;
-
- pre {
- white-space: unset;
- }
-}
-
-.inactive-snippet {
- @include theme.link-colors(#579);
-}
-
-@media (width <= 782px) {
- p.search-box {
- float: inline-start;
- position: initial;
- margin-block: 1em 0;
- margin-inline: 0;
- block-size: auto;
- }
-}
-
-.wp-list-table .is-expanded td.column-activate.activate {
- /* fix for mobile layout */
- display: table-cell !important;
}
.nav-tab-wrapper + .subsubsub, p.search-box {
@@ -171,15 +45,19 @@ td.column-description {
margin-inline: 0;
}
-.snippet-type-description {
- border-block-end: 1px solid #ccc;
- margin: 0;
- padding-block: 1em;
- padding-inline: 0;
-}
+.wrap > h2:first-of-type {
+ font-size: 1.5rem;
+ font-weight: 400;
+ line-height: 1.3;
+ padding: 0;
+ margin-block: 50px 1rem;
-.code-snippets-notice a.notice-dismiss {
- text-decoration: none;
+ .subtitle {
+ vertical-align: middle;
+ display: inline-flex;
+ align-items: center;
+ gap: 1em;
+ }
}
.refresh-button-container {
@@ -197,7 +75,7 @@ td.column-description {
line-height: 1.4;
}
-.wrap h2.nav-tab-wrapper {
+.wrap .nav-tab-wrapper {
.nav-tab {
display: flex;
flex-flow: row wrap;
diff --git a/src/css/manage/_cloud-community-cards.scss b/src/css/manage/_cloud-community-cards.scss
new file mode 100644
index 000000000..82ead781a
--- /dev/null
+++ b/src/css/manage/_cloud-community-cards.scss
@@ -0,0 +1,179 @@
+@use '../common/theme';
+@use '../common/cards';
+
+.cloud-search-result {
+ p:last-child {
+ margin-block-end: 0;
+ }
+
+ .card-inner {
+ display: flex;
+ flex-flow: column;
+ gap: 16px;
+ }
+
+ .snippet-card-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ // Keep header content clear of the absolutely-positioned selection checkbox.
+ padding-inline-end: 32px;
+
+ h3 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.5;
+ min-inline-size: 0;
+ }
+
+ .cloud-snippet-title-button {
+ display: block;
+ max-inline-size: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: none;
+ font: inherit;
+ color: theme.$accent;
+ cursor: pointer;
+ text-align: start;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+
+ &:hover, &:focus {
+ text-decoration: underline;
+ }
+
+ &:focus-visible {
+ outline: 2px solid theme.$accent;
+ outline-offset: 2px;
+ border-radius: 2px;
+ }
+ }
+ }
+
+ .snippet-card-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 16px;
+ font-size: 14px;
+ line-height: 1.5;
+
+ .cloud-snippet-tags {
+ color: theme.$accent;
+ }
+
+ .snippet-card-modified {
+ color: #6c7e7e;
+ margin-inline-start: auto;
+ }
+ }
+
+ .snippet-description-content {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+ overflow: hidden;
+ margin: 0;
+ font-size: 14px;
+ line-height: 1.5;
+ color: #646970;
+ border-block-start: 1px solid #dcdcde;
+ padding-block-start: 16px;
+ }
+
+ .cloud-snippet-author {
+ margin: 0;
+ font-size: 14px;
+ color: #646970;
+
+ a {
+ color: theme.$accent;
+ }
+ }
+
+ footer {
+ .dashicons-warning {
+ color: #b32d2e;
+ }
+
+ .cloud-snippet-update svg {
+ display: block;
+ }
+
+ .button {
+ font-size: 14px;
+ line-height: 2.5714;
+ border-radius: 5px;
+ }
+
+ .button:not(.button-primary, .cloud-pro-button) {
+ background: transparent;
+ }
+ }
+}
+
+.cloud-search-results .cloud-search-result footer .components-spinner {
+ margin: 0;
+}
+
+// Pro-only actions use the same quiet outline treatment in card and table views.
+.cloud-pro-button.button {
+ background-color: transparent;
+ border-color: theme.$accent;
+ color: theme.$accent;
+
+ &:hover,
+ &:focus {
+ background-color: #eff5f9;
+ border-color: theme.$accent;
+ color: theme.$accent;
+ }
+}
+
+// Three equal-height snippet cards per row, matching the manage snippets
+// grid. minmax(0, 1fr) lets columns shrink below their content width so
+// the grid never overflows the page.
+ul.cloud-search-results.code-snippets-cards {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+
+ @media (width <= 1100px) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ @media (width <= 782px) {
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .code-snippets-card {
+ min-inline-size: 0;
+
+ footer {
+ flex-wrap: wrap;
+ }
+ }
+
+ // Reveal the bulk-select checkbox on hover or keyboard focus, and keep
+ // every checkbox visible while any card in the grid is selected so an
+ // in-progress selection is never hidden.
+ .snippet-card-select {
+ opacity: 0;
+ transition: opacity 0.15s ease;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
+ }
+
+ .snippet-card-select:checked,
+ &.has-selection .snippet-card-select,
+ .code-snippets-card:hover .snippet-card-select,
+ .code-snippets-card:focus-within .snippet-card-select {
+ opacity: 1;
+ }
+}
diff --git a/src/css/manage/_cloud-community.scss b/src/css/manage/_cloud-community.scss
new file mode 100644
index 000000000..686c52088
--- /dev/null
+++ b/src/css/manage/_cloud-community.scss
@@ -0,0 +1,180 @@
+@use '../common/banners';
+@use '../common/checkbox';
+@use '../common/theme';
+
+.cloud-snippet-status {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ color: #646970;
+ font-size: 12px;
+ font-weight: 500;
+ white-space: nowrap;
+
+ &::before {
+ content: '';
+ block-size: 10px;
+ inline-size: 10px;
+ border-radius: 50%;
+ display: inline-block;
+ }
+}
+
+$status-colors: (
+ public #64baba,
+ private #cc96fb,
+ unverified #ea835e,
+ ai-verified #1cabcf,
+ pro-verified #41a269,
+);
+
+@each $status, $color in $status-colors {
+ .cloud-snippet-status-#{$status}::before {
+ background: $color;
+ }
+}
+
+.bundle-share-code-form,
+.cloud-search-form {
+ display: flex;
+ gap: 8px;
+ margin-block: 0 47px;
+ block-size: 54px;
+
+ > select {
+ flex: 0 0 250px;
+ }
+
+ .button {
+ flex: 0 0 165px;
+ }
+
+ .cloud-search-query {
+ flex: 1;
+ position: relative;
+
+ input {
+ inline-size: 100%;
+ block-size: 100%;
+ }
+
+ > .components-spinner {
+ position: absolute;
+ inset-inline-end: 1.5em;
+ inset-block-start: 25%;
+ }
+ }
+
+ button[type='submit'] {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+
+ .components-spinner {
+ margin: 0;
+ }
+ }
+}
+
+.cloud-search {
+ @include banners.banners;
+
+ padding-block-start: 10px;
+
+ .banner {
+ justify-content: center;
+ }
+
+ .cloud-search-filters select {
+ inline-size: 245px;
+ }
+}
+
+.bundle-share-code-form {
+ max-inline-size: 60%;
+}
+
+.cloud-snippet-action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 8px;
+
+ // Keep the Download / Edit / Pro Only button a consistent width so
+ // rows line up regardless of which state the snippet is in.
+ .button-primary,
+ .cloud-pro-button,
+ .button:not(.button-primary) {
+ inline-size: 103px;
+ min-inline-size: 103px;
+ text-align: center;
+ justify-content: center;
+ }
+}
+
+.cloud-snippets-table {
+ // Body rows inherit the shared table-cell padding so descriptions can grow
+ // naturally instead of being constrained to a fixed row height.
+ tbody td {
+ vertical-align: middle;
+ }
+
+ .check-column {
+ vertical-align: middle;
+
+ input[type='checkbox'] {
+ @include checkbox.canonical;
+ }
+ }
+
+ .column-name {
+ inline-size: 22%;
+ }
+
+ .column-type {
+ inline-size: 90px;
+ }
+
+ .column-status {
+ inline-size: 120px;
+ }
+
+ .column-actions {
+ inline-size: 234px;
+ }
+
+ .cloud-table-name-button {
+ background: none;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ font-size: inherit;
+ font-weight: 600;
+ color: theme.$accent;
+ text-decoration: none;
+ cursor: pointer;
+ text-align: start;
+
+ &:hover,
+ &:focus-visible {
+ color: #135e96;
+ text-decoration: underline;
+ }
+ }
+
+ // Clamp descriptions so every row stays the same height.
+ .cloud-table-description {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+ overflow: hidden;
+ }
+}
+
+// Community bundles tab shares the same top padding as .cloud-search so the
+// two tabs' descriptions and inputs align (scoped so the My Library bundles
+// tab is unaffected).
+.community-bundles {
+ padding-block-start: 10px;
+}
diff --git a/src/css/manage/_cloud.scss b/src/css/manage/_cloud.scss
deleted file mode 100644
index a24a6a8b1..000000000
--- a/src/css/manage/_cloud.scss
+++ /dev/null
@@ -1,383 +0,0 @@
-@use '../common/theme';
-@use '../common/tooltips';
-
-.cloud-legend-tooltip {
- h3 {
- font-size: 16px;
- color: #fff;
- text-align: center;
- }
-
- td {
- vertical-align: top;
- }
-}
-
-.cloud-search-info {
- text-align: justify;
-
- small {
- color: #646970;
- float: inline-end;
- }
-}
-
-.thickbox-code-viewer {
- min-block-size: 250px;
- background-color: hsl(0deg 0% 96.5%);
- padding: 20px;
- border-radius: 10px;
-}
-
-#snippet-code-thickbox {
- display: block;
- inline-size: 100%;
-}
-
-.no-results {
- font-size: 15px;
-}
-
-.dashicons.cloud-synced {
- color: theme.$cloud;
-}
-
-.dashicons.cloud-downloaded {
- color: #e91e63;
-}
-
-.dashicons.cloud-not-downloaded {
- color: theme.$outline;
-}
-
-.dashicons.cloud-update {
- color: theme.$cloud-update;
-}
-
-.cloud_update a {
- color: theme.$cloud-update !important;
- text-decoration: underline;
-}
-
-.updated.column-updated span {
- text-decoration: dotted underline;
-}
-
-td.column-name {
- .cloud-icon {
- margin-inline-end: 3px;
- }
-}
-
-.cloud-snippet-download {
- color: theme.$accent !important;
-}
-
-.cloud-snippet-downloaded, .cloud-snippet-preview-style {
- color: #616161 !important;
-}
-
-.cloud-snippet-update {
- color: theme.$cloud-update !important;
-}
-
-#cloud-search-form {
- margin-block: 30px;
- text-align: center;
-}
-
-.input-group {
- position: relative;
- display: flex;
- flex-wrap: wrap;
- align-items: stretch;
- max-inline-size: 900px;
- margin-block: 0;
- margin-inline: auto;
-}
-
-#cloud_search {
- display: block;
- padding-block: 0.375rem;
- padding-inline: 0.75rem;
- font-size: 1rem;
- color: #495057;
- background-clip: padding-box;
- border-radius: 0;
- transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
- position: relative;
- flex: 1 1 auto;
- inline-size: 1%;
- margin-block-end: 0;
-
- &:focus {
- outline: 0;
- border: 1px solid #8c8f94;
- box-shadow: none;
- }
-}
-
-#cloud-select-prepend {
- margin-inline-end: -3px;
- border-start-end-radius: 0;
- border-end-end-radius: 0;
- position: relative;
- z-index: 2;
- color: theme.$accent;
- border-color: theme.$accent;
- background-color: #f6f7f7;
-
- &:hover {
- background-color: #f0f0f1;
- border-color: #0a4b78;
- color: #0a4b78;
- }
-}
-
-#cloud-search-submit {
- padding-block: 0;
- padding-inline: 15px;
- margin-inline-start: -3px;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.cloud-search {
- margin-inline-start: 5px;
-}
-
-.bundle-group {
- margin-block-start: 10px;
- justify-content: space-between;
- display: flex;
- gap: 5px;
- flex-wrap: nowrap;
-}
-
-#cloud-bundles {
- color: #495057;
- display: flex;
- flex: 1 1 auto;
- font-size: 1rem;
- padding-block: 0.375rem;
- padding-inline: 0.75rem;
- position: relative;
- inline-size: 50%;
-}
-
-#cloud-bundle-show {
- inline-size: 10%;
-}
-
-#cloud-bundle-run {
- inline-size: 15%;
-}
-
-#bundle_share_name {
- color: #495057;
- font-size: 1rem;
- inline-size: 25%;
-}
-
-.heading-box {
- max-inline-size: 900px;
- margin: auto;
- padding-block-end: 1rem;
-}
-
-.cloud-search-heading {
- font-size: 23px;
- font-weight: 400;
- padding-block: 9px 4px;
- padding-inline: 0;
- line-height: 1.3;
- text-align: center;
- margin-block-end: 0;
-}
-
-.cloud-badge.ai-icon {
- font-size: 12px;
- padding: 3px;
- margin-inline-start: 5px;
- color: #b22222;
-}
-
-.cloud-search-card-bottom {
- min-block-size: 40px;
-}
-
-#cloud-search-results .cloud-snippets #the-list {
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
-
- .plugin-card {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-
- .cloud-meta-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-grow: 1;
- }
-
- .column-name {
- display: flex;
- justify-content: space-between;
-
- h3 {
- display: inline-flex;
- flex-shrink: 1;
- }
-
- .title-icon {
- block-size: 90px;
- margin-block-start: -7px;
- }
- }
-
- .column-votes {
- display: inline-flex;
- gap: 3px;
-
- &:hover {
- .thumbs-up {
- stroke: #059669;
- fill: #6ee7b7;
- animation: thumb 1s ease-in-out infinite;
- }
- }
-
- .num-votes {
- display: inline-flex;
- align-items: flex-end;
- }
- }
- }
-
- .action-buttons {
- margin: 0;
- align-items: flex-end;
-
- .button {
- inline-size: 100%;
- text-align: center;
- }
- }
-}
-
-.cloud-snippets #the-list {
- .column-download {
- display: flex;
- flex-flow: column;
- text-align: end;
-
- li {
- list-style: none;
- }
- }
-}
-
-.cloud-connect-wrap {
- display: flex;
- justify-content: space-between;
- align-items: center;
- max-block-size: 35px;
- margin-block: 0;
- margin-inline: 3px;
- float: inline-end;
- gap: 5px;
-}
-
-.cloud-table > tbody > tr {
- block-size: 80px;
- box-shadow: inset 0 -1px 0 rgb(0 0 0 / 10%);
-}
-
-.cloud-table > tbody > tr > td {
- max-inline-size: 250px;
-}
-
-.cloud-table tbody .active-snippet .column-name {
- font-weight: 400;
- max-inline-size: 400px;
- white-space: normal !important;
-}
-
-.cloud-table td .no-results {
- margin-block-start: 15px;
- color: #e32121;
- text-align: center;
-}
-
-.cloud-status-dot {
- block-size: 10px;
- inline-size: 10px;
- background-color: #ce0000;
- border-radius: 50%;
-
-
- .cloud-connect-active & {
- background-color: #25a349;
- }
-}
-
-.cloud-connect-text {
- color: #ce0000;
-
- .cloud-connect-active & {
- color: #2e7d32;
- }
-}
-
-.thumbs-up {
- inline-size: 1.25rem; /* 20px */
- block-size: 1.25rem; /* 20px */
- transform-origin: bottom left;
-
- &:hover {
- stroke: #059669;
- fill: #6ee7b7;
- }
-}
-
-.plugin-card-bottom {
- overflow: visible !important;
- display: flex;
- align-items: center;
-}
-
-.beta-test-notice {
- margin-block-start: 20px;
-}
-
-.highlight-yellow {
- background: #fefdba;
- padding: 3px;
- border-radius: 3px;
-}
-
-@keyframes thumb {
- 0% {
- transform: rotate(0);
- }
-
- 33% {
- transform: rotate(7deg);
- }
-
- 66% {
- transform: rotate(-15deg);
- }
-
- 90% {
- transform: rotate(5deg);
- }
-
- 100% {
- transform: rotate(0);
- }
-}
diff --git a/src/css/manage/_snippets-table.scss b/src/css/manage/_snippets-table.scss
new file mode 100644
index 000000000..5a8e23f28
--- /dev/null
+++ b/src/css/manage/_snippets-table.scss
@@ -0,0 +1,270 @@
+@use '../common/badges';
+@use '../common/list-table';
+@use '../common/theme';
+
+.column-name,
+.column-type {
+ .dashicons {
+ font-size: 16px;
+ inline-size: 16px;
+ block-size: 16px;
+ vertical-align: middle;
+ }
+
+ .dashicons-clock {
+ vertical-align: middle;
+ }
+}
+
+// `.wp-list-table` prefix keeps this above WP 7.0's 40px control height so the boxed
+// priority field stays 30px tall (below the 38px control token, which would stretch the
+// table rows) and vertically aligned with the rest of the row.
+.wp-list-table .priority-column input {
+ appearance: none;
+ background: #fff;
+ border: 1px solid theme.$control-border;
+ border-radius: 5px;
+ box-shadow: none;
+ box-sizing: border-box;
+ min-block-size: 30px;
+ block-size: 30px;
+ inline-size: 4em;
+
+ // Pin the box model so padding/margins/line-height match WP 6.9 regardless of WP 7.0's
+ // taller default control metrics; this keeps the value aligned in both the resting and
+ // hovered (native spinner) states.
+ padding-block: 0;
+ padding-inline: 8px;
+ margin-block: 0;
+ margin-inline: 1px;
+ line-height: 2;
+ color: #2c3337;
+ text-align: center;
+
+ &:disabled {
+ color: inherit;
+ }
+
+ &:hover, &:focus, &:active {
+ color: #000;
+ background-color: rgb(0 0 0 / 10%);
+ border-radius: 6px;
+ appearance: unset;
+
+ &:disabled {
+ color: inherit;
+ background-color: transparent;
+ }
+ }
+}
+
+.wp-list-table .is-expanded td.column-activate.activate {
+ /* fix for mobile layout */
+ display: table-cell !important;
+}
+
+.snippets-card-grid {
+ // Three equal-height cards per row; descriptions are clamped so height
+ // stays uniform regardless of text length. The compound selector wins
+ // over the base .code-snippets-cards auto-fill column rule, which is
+ // emitted later in the compiled stylesheet.
+ // minmax(0, 1fr) lets tracks shrink below their content's min-content
+ // width (wide footer button rows), which plain 1fr tracks cannot,
+ // preventing the grid from overflowing the page horizontally.
+ &.code-snippets-cards {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+
+ @media (width <= 1100px) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ @media (width <= 782px) {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ }
+
+ .code-snippets-card {
+ min-inline-size: 0;
+ }
+
+ // Reveal the bulk-select checkbox on hover or keyboard focus, and keep
+ // every checkbox visible while any card in the grid is selected so an
+ // in-progress selection is never hidden.
+ .snippet-card-select {
+ opacity: 0;
+ transition: opacity 0.15s ease;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
+ }
+
+ .snippet-card-select:checked,
+ &.has-selection .snippet-card-select,
+ .code-snippets-card:hover .snippet-card-select,
+ .code-snippets-card:focus-within .snippet-card-select {
+ opacity: 1;
+ }
+
+ // Card body bands: header row, meta row, then a separated description.
+ .card-inner {
+ display: flex;
+ flex-flow: column;
+ gap: 16px;
+ }
+
+ .snippet-card-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ // Keep header content clear of the absolutely-positioned selection checkbox.
+ padding-inline-end: 32px;
+
+ > a {
+ @include badges.badge-link;
+ }
+
+ h3 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.5;
+
+ // Let the heading shrink inside the flex row so long names
+ // truncate instead of pushing the header icons out of the card.
+ min-inline-size: 0;
+
+ .snippet-name {
+ display: block;
+ color: theme.$accent;
+ text-decoration: none;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .extra-icons:empty {
+ display: none;
+ }
+
+ // The accent pill styling lives in common/_switch.scss; keep the
+ // switch from shrinking inside the flex card header.
+ input[type='checkbox'].switch {
+ flex-shrink: 0;
+ }
+ }
+
+ .snippet-card-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 16px;
+ font-size: 14px;
+ line-height: 1.5;
+
+ .snippet-card-tags-label {
+ color: #646970;
+ }
+
+ .snippet-card-tags a {
+ color: theme.$accent;
+ text-decoration: none;
+ }
+
+ .snippet-card-modified {
+ color: var(--cs-color-text-muted);
+ }
+
+ &.has-tags .snippet-card-modified {
+ margin-inline-start: auto;
+ }
+ }
+
+ .snippet-description-content {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+ overflow: hidden;
+ font-size: 14px;
+ line-height: 1.5;
+ color: #646970;
+ border-block-start: 1px solid #dcdcde;
+ padding-block-start: 16px;
+ }
+
+ // Align the selection checkbox with the taller card header row.
+ .snippet-card-corner {
+ inset-block-start: 28px;
+ }
+
+ .code-snippets-card footer {
+ .button {
+ font-size: 14px;
+ line-height: 2.5714;
+ border-radius: 5px;
+ }
+
+ .button:not(.button-primary) {
+ background: transparent;
+ }
+ }
+
+ .kebab-menu-priority {
+ justify-content: space-between;
+
+ // Balances the shorter input so the row matches the height of the menu
+ // items above and below it.
+ padding-block: 5px;
+
+ input.snippet-priority {
+ inline-size: 52px;
+ block-size: 30px;
+
+ // WordPress sizes number inputs with a min-height, which would
+ // otherwise win over the height set here.
+ min-block-size: 0;
+ box-sizing: border-box;
+ margin: 0;
+ padding-block: 0;
+ padding-inline: 4px;
+ border: 1px solid #e2e2e4;
+ border-radius: 4px;
+ text-align: center;
+ color: #2c3337;
+ }
+ }
+}
+
+// Shared table-navigation layout for both snippet views and both toolbars:
+// bulk actions, select all, tag filter and search on the left, with the item
+// count, pagination and view toggle pinned to the far end of the row. The
+// bottom toolbar repeats only the bulk actions and pagination group.
+.snippets-list-view .tablenav {
+ block-size: auto;
+ margin-block-end: 16px;
+}
+
+.snippets-search-subtitle {
+ margin-block: 0 12px;
+ font-size: 14px;
+ font-style: italic;
+ color: #646970;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 1em;
+}
+
+// Keep every table row the same height by clamping the description cell
+// to two lines, matching the two-line name + row-actions column.
+.snippets-list-view .wp-list-table td.column-desc .snippet-description-content {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ line-clamp: 2;
+ overflow: hidden;
+}
diff --git a/src/css/menu.scss b/src/css/menu.scss
index 8c3717055..c4a9ec286 100644
--- a/src/css/menu.scss
+++ b/src/css/menu.scss
@@ -1,3 +1,4 @@
+
#adminmenu {
.toplevel_page_snippets div.wp-menu-image::before {
content: '';
@@ -11,6 +12,16 @@
color: #fff;
background-color: #d46f4d;
border: none;
+
+ // WP 7.0 restyles `.button`/`.button-primary` with wider padding, a 2px radius and a
+ // 40px min-height; pin these (independent of the plugin control tokens, which are
+ // sized for page content rather than the admin menu) so the pill keeps its compact
+ // dimensions and stays aligned with the surrounding submenu items across WP versions.
+ border-radius: 3px;
+ padding-block: 0;
+ padding-inline: 10px;
+ min-block-size: 30px;
+ block-size: auto;
text-align: center;
font-weight: bold;
transition: background-color .1s linear;
@@ -20,12 +31,23 @@
justify-content: center;
align-items: center;
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0.01s;
+ }
+
&:hover {
- background-color: #08c5d1;
+ background-color: #0ca0a9;
}
.dashicons {
vertical-align: text-bottom;
+ color: inherit;
+ font-size: 16px;
+ padding-block-start: 2px;
+
+ // WP 7.0's taller button line-height (≈38px) leaks onto the icon and pushes it out
+ // of alignment; reset it so the glyph stays centred within the flex pill.
+ line-height: 1;
}
}
diff --git a/src/css/settings.scss b/src/css/settings.scss
index e2a040ae4..d8d931e19 100644
--- a/src/css/settings.scss
+++ b/src/css/settings.scss
@@ -1,10 +1,42 @@
+@use 'common/checkbox';
@use 'common/codemirror';
+@use 'common/subnav';
+@use 'common/theme';
+@use 'common/toolbar';
+@use 'common/wp-admin';
+@use 'common/page-header';
$sections: general, editor, debug, version-switch;
+.wrap.code-snippets-settings {
+ margin-block-start: 0;
+}
+
+.settings-section input[type='checkbox']:not(.switch) {
+ @include checkbox.canonical;
+
+ margin-inline-end: 8px;
+}
+
+.snippets-page-header h1 {
+ color: #2c3337;
+}
+
p.submit {
display: flex;
- justify-content: space-between;
+ justify-content: flex-start;
+ gap: 14px;
+
+ .button-secondary {
+ background: transparent;
+ }
+}
+
+.settings-section .form-table th {
+ inline-size: 261px;
+ font-size: 14px;
+ font-weight: 700;
+ color: #2c3337;
}
.settings-section,
@@ -16,7 +48,7 @@ p.submit {
margin-block-end: 1em;
}
-input[type="number"] {
+input[type='number'] {
inline-size: 4em;
}
@@ -73,7 +105,7 @@ body.js {
display: inline-block;
padding-inline-end: 2em;
line-height: 2;
- color: #aaa;
+ color: #646970;
}
.license-status-valid {
@@ -84,7 +116,7 @@ body.js {
color: #dc3232;
}
-.wrap[data-active-tab="license"] .submit {
+.wrap[data-active-tab='license'] .submit {
display: none;
}
@@ -120,8 +152,8 @@ body.js {
}
.refresh-success {
- background: #2271b1;
- color: #ffeb3b;
+ background: #2e7d4f;
+ color: #fff;
}
.cloud-settings tbody tr:nth-child(n+5) {
@@ -138,7 +170,7 @@ body.js {
background: #f0f6fc;
padding: 2px 8px;
border-radius: 3px;
- border: 1px solid #c3c4c7;
+ border: 1px solid theme.$control-border;
}
#target_version {
@@ -155,7 +187,7 @@ body.js {
border-color: #dcdcde !important;
}
}
-
+
// Warning box styling
#version-switch-warning {
margin-block-start: 20px !important;
@@ -163,11 +195,11 @@ body.js {
border-inline-start: 4px solid #dba617;
background: #fff8e5;
border-radius: 4px;
-
+
p {
margin: 0;
color: #8f6914;
-
+
strong {
color: #8f6914;
}
@@ -176,7 +208,7 @@ body.js {
#version-switch-result {
margin-block-start: 12px;
-
+
&.notice {
padding: 8px 12px;
border-radius: 4px;
@@ -211,3 +243,77 @@ body.js {
}
}
}
+
+#settings-sections-tabs {
+ margin: 0;
+ padding: 0;
+
+ ul {
+ display: flex;
+ align-items: stretch;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+
+ .snippet-type-link {
+ display: flex;
+ align-items: center;
+ min-block-size: 52px;
+ margin: 0;
+ padding-inline: 24px;
+ color: #646970;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.5;
+ border: 0;
+ border-inline-end: 1px solid #e2e2e4;
+ background: transparent;
+
+ &:hover,
+ &:focus {
+ color: #2c3337;
+ background: #f6f7f7;
+ }
+
+ &.active-type,
+ &.active-type:hover,
+ &.active-type:focus {
+ position: relative;
+ z-index: 1;
+ margin-block-end: -1px;
+ color: theme.$accent;
+ background: #f0f0f1;
+ border-color: #f0f0f1;
+ }
+ }
+}
+
+@media (width <= 782px) {
+ body.js {
+ .settings-type-nav-wrapper {
+ display: none;
+ }
+
+ .settings-section-title {
+ position: static;
+ inline-size: auto;
+ block-size: auto;
+ margin: 0 0 16px;
+ overflow: visible;
+ clip: auto;
+ clip-path: none;
+ font-size: 20px;
+ line-height: 1.3;
+ }
+
+ .settings-section {
+ display: block !important;
+ margin-block-end: 40px;
+ }
+
+ .wrap[data-active-tab='license'] .submit {
+ display: flex;
+ }
+ }
+}
diff --git a/src/css/welcome.scss b/src/css/welcome.scss
index 5ebfe850d..591d3a6e8 100644
--- a/src/css/welcome.scss
+++ b/src/css/welcome.scss
@@ -1,369 +1,213 @@
@use 'sass:color';
@use 'common/theme';
@use 'common/badges';
+@use 'common/toolbar';
+@use 'common/wp-admin';
$breakpoint: 1060px;
-.csp-welcome-wrap {
- padding: 25px;
-
- h1, h2, h3 {
- font-weight: 700;
- margin-block: 10px;
-
- .dashicons {
- font-size: 90%;
- line-height: inherit;
- inline-size: auto;
- }
- }
-
+.code-snippets-welcome {
h1 {
- font-size: 1.6rem;
+ font-size: 32px;
+ font-weight: 510;
+ line-height: 1.25;
+ color: #2c3337;
+ padding: 0;
+ margin-block: 50px 1rem;
}
+}
- h2 {
- font-size: 1.4rem;
- }
+.code-snippets-updates {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ gap: 20px;
- .dashicons-external {
- float: inline-end;
- color: #666;
+ > * {
+ background: #fff;
+ border-radius: 8px;
+ padding: 32px;
}
-}
-
-.csp-welcome-header {
- display: flex;
- flex-flow: row wrap;
- justify-content: space-between;
- align-items: center;
- header {
+ .code-snippets-header-wrapper {
display: flex;
- flex-direction: row;
+ flex-flow: row;
+ justify-content: space-between;
align-items: center;
- gap: 10px;
-
- h1 {
- font-size: 1.4rem;
- font-weight: bold;
- line-height: 1;
- margin: 0;
-
- span {
- text-decoration: underline theme.$primary wavy 3px;
- text-decoration-skip-ink: none;
- text-underline-offset: 11px;
- text-transform: capitalize;
- }
- }
+ border-block-end: 1px solid #e2e2e4;
+ margin-block-end: 20px;
+ padding-block-end: 20px;
}
-}
-
-.csp-welcome-header nav {
- column-gap: 15px;
- ul {
- display: flex;
- flex-flow: row wrap;
- justify-content: space-evenly;
+ h2 {
+ font-size: 18px;
margin: 0;
}
+}
- li {
- margin-block-end: 0;
- }
-
- li a {
- margin-block: 10px;
- align-items: center;
- border-width: 1px;
- border-style: solid;
- color: white;
- cursor: pointer;
- display: flex;
- font-weight: 400;
- gap: 3px;
- text-decoration: none;
- transition: all .1s ease-in-out;
- border-radius: 3px;
- padding: 8px;
-
- &:hover {
- background: transparent;
- }
-
- .dashicons, svg {
- text-decoration: none;
- margin-block-start: -1px;
- margin-inline-start: 3px;
- }
-
- svg {
- fill: #fff;
- inline-size: 20px;
- block-size: 20px;
- font-size: 20px;
- vertical-align: top;
- }
-
- &:hover svg {
- fill: currentcolor;
- }
- }
-
- $link-colors: (
- pro: theme.$secondary,
- cloud: #08c5d1,
- resources: #424242,
- discord: theme.$brand-discord,
- facebook: theme.$brand-facebook
- );
+.code-snippets-hero {
+ display: flex;
+ flex-flow: column;
- @each $link-name, $color in $link-colors {
- .csp-link-#{$link-name} {
- background: $color;
- border-color: $color;
+ figure {
+ margin: 1em 0 0;
+ overflow: hidden;
+ border-radius: 0.5rem;
+ position: relative;
+ block-size: auto;
+ background: #efefef;
+ flex: 1;
+ text-align: center;
- &:hover {
- color: $color;
- }
+ img {
+ inline-size: 100%;
+ block-size: 100%;
+ overflow: hidden;
+ object-fit: cover;
}
}
}
-.csp-cards {
- display: grid;
- grid-auto-rows: 1fr;
- grid-template-columns: repeat(4, 1fr);
- gap: 40px 15px;
-
- @media (width <= $breakpoint) {
- grid-template-columns: 1fr !important;
- }
-}
-
-.csp-card {
- border: 1px solid theme.$outline;
- background: white;
- border-radius: 10px;
+.code-snippets-partners,
+.code-snippets-articles {
display: flex;
- flex-flow: column;
-}
+ flex-direction: row;
+ gap: 16px;
+ margin: 0;
-a.csp-card {
- text-decoration: none;
-
- &:hover {
- background: color.adjust(theme.$primary, $lightness: 55%);
- transition: .5s background-color;
- box-shadow: 0 1px 1px rgb(255 255 255 / 50%);
+ figure {
+ margin: 0;
+ padding: 0;
- .dashicons-external {
- color: #000;
+ img {
+ inline-size: 100%;
+ block-size: 220px;
+ overflow: hidden;
+ object-fit: cover;
}
}
}
-.csp-section-changes {
- border: 1px solid theme.$outline;
- border-inline-start: 0;
- border-inline-end: 0;
- padding-block: 40px 50px;
- padding-inline: 0;
+.code-snippets-card {
+ flex: 1;
+ background: #fff;
+ border-radius: 8px;
display: flex;
- flex-direction: column;
- row-gap: 20px;
- margin-block-start: 30px;
-
- .csp-cards {
- grid-template-columns: 2fr 1fr;
- gap: 20px;
- }
-
- .csp-card {
- padding: 20px;
- box-shadow: 0 1px 1px rgb(0 0 0 / 5%);
+ margin: 0;
+ flex-flow: column;
+ color: #2c3337;
- h2 {
- color: theme.$primary;
- }
+ figure img {
+ border-start-start-radius: 8px;
+ border-start-end-radius: 8px;
}
+}
- .csp-changelog-wrapper {
- overflow-y: scroll;
+.code-snippets-partners {
+ .code-snippets-header-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 24px;
}
+}
- .csp-section-changelog {
- font-size: 0.9rem;
- line-height: 1.5;
- color: #333;
- block-size: 400px;
-
- h3 {
- float: inline-end;
- color: #666;
- }
-
- h4 {
- margin-block: 30px 10px;
- margin-inline: 0;
- }
-
- ul {
- margin-block-start: 5px;
- }
-
- li {
- display: grid;
- grid-template-columns: 40px 1fr;
- grid-template-rows: 1fr;
- align-items: baseline;
- gap: 7px;
- }
-
- li .badge {
- text-align: center;
- }
-
- > article::after {
- border-block-end: 1px solid #666;
- content: ' ';
- display: block;
- inline-size: 50%;
- margin-block: 3em 0;
- margin-inline: auto;
- }
-
- > article:last-child {
- padding-block-end: 1px;
-
- &::after {
- border: 0;
- }
- }
+.code-snippets-articles {
+ .code-snippets-header-wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ padding: 24px;
+ gap: 14px;
+ block-size: 100%;
}
- figure {
- margin-block: 1em 0;
- margin-inline: 0;
- overflow: hidden;
- border-radius: 0.5rem;
- border: 1px solid grey;
- position: relative;
- block-size: auto;
- background: #646970;
-
- img {
- inline-size: 100%;
- block-size: 100%;
- overflow: hidden;
- object-fit: cover;
- }
+ .button {
+ margin-block-start: auto;
}
- .dashicons-lightbulb {
- color: #f1c40f;
+ h3 {
+ font-size: 18px;
+ margin: 0;
}
- .dashicons-chart-line {
- color: #85144b;
+ p {
+ margin: 0;
}
- .dashicons-buddicons-replies {
- color: #3d9970;
+ .item-category {
+ color: white;
+ background: color.adjust(theme.$secondary, $lightness: -15%);
+ display: block;
+ font-size: 12px;
+ letter-spacing: 1px;
+ margin-block: 0;
+ text-transform: uppercase;
+ inline-size: fit-content;
+ padding: 3px 10px;
+ border-radius: 3px;
+ font-weight: bold;
}
}
-.csp-section-links {
- padding-block: 40px 50px;
- padding-inline: 0;
-
- .csp-card {
- margin-block-start: 20px;
- justify-content: flex-start;
- color: black;
- position: relative;
- overflow: hidden;
- row-gap: 10px;
- padding: 1rem;
- inline-size: 85%;
-
- header {
- flex: 1;
- }
-
- figure {
- margin-block: 1em 0;
- margin-inline: 0;
-
- img {
- border-radius: 5px;
- inline-size: 100%;
- block-size: 100%;
- max-block-size: 300px;
- overflow: hidden;
- object-fit: cover;
- }
- }
+.code-snippets-changelog-entries {
+ font-size: 0.9rem;
+ line-height: 1.5;
+ overflow-y: scroll;
+ max-block-size: 500px;
+ color: #2c3337;
+ padding-inline-end: 1em;
- .csp-card-item-category {
- color: white;
- background: theme.$secondary;
- display: block;
- font-size: .9rem;
- letter-spacing: 1px;
- margin-block: 0;
- text-transform: uppercase;
- inline-size: fit-content;
- padding-block: 5px;
- padding-inline: 15px;
- border-radius: 50px;
- }
+ h3 {
+ font-size: 16px;
+ display: flex;
- h3 {
- font-size: 1.7rem;
- color: theme.$primary;
- line-height: normal;
+ &:not(:first-of-type) {
+ margin-block-start: 100px;
}
- .csp-card-item-description {
- color: #51525c;
- font-size: 1rem;
- font-weight: 300;
+ span {
+ font-size: 14px;
+ font-weight: normal;
+ margin-inline-start: auto;
}
+ }
- footer {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- }
+ h4 {
+ font-size: 14px;
+ margin: 1em 0;
}
- &.csp-section-partners {
- border-block-start: 1px solid theme.$outline;
+ ul {
+ margin-block-start: 5px;
+ }
- header {
- display: flex;
- flex-direction: row-reverse;
- justify-content: space-between;
- align-items: center;
- }
+ li {
+ display: grid;
+ grid-template-columns: 40px 1fr;
+ grid-template-rows: 1fr;
+ align-items: baseline;
+ gap: 10px;
}
- &.csp-section-articles {
- h2 {
- font-size: 1.1rem;
- }
+ $icon-colors: (
+ lightbulb #f1c40f,
+ chart-line #85144b,
+ buddicons-replies #3d9970,
+ remove #ffbf00,
+ trash #c0c0c0,
+ shield #0074d9,
+ open-folder #5d2f27
+ );
- figure img {
- aspect-ratio: 1;
+ @each $icon, $color in $icon-colors {
+ .dashicons-#{$icon} {
+ color: $color;
}
}
}
-.csp-loading-spinner {
+.code-snippets-loading-spinner {
block-size: 0;
inline-size: 0;
padding: 15px;
@@ -374,6 +218,22 @@ a.csp-card {
position: absolute;
inset-inline-start: 47%;
inset-block-start: 45%;
+
+ @media (prefers-reduced-motion: reduce) {
+ animation: none;
+ }
+}
+
+.csp-welcome-header nav {
+ column-gap: 15px;
+
+ ul {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: space-evenly;
+ gap: 5px;
+ margin: 0;
+ }
}
@keyframes loading-rotate {
diff --git a/src/js/components/ConditionModal/ConditionModalButton.tsx b/src/js/components/EditMenu/ConditionModal/ConditionModalButton.tsx
similarity index 62%
rename from src/js/components/ConditionModal/ConditionModalButton.tsx
rename to src/js/components/EditMenu/ConditionModal/ConditionModalButton.tsx
index 270d3e38f..38c3faab9 100644
--- a/src/js/components/ConditionModal/ConditionModalButton.tsx
+++ b/src/js/components/EditMenu/ConditionModal/ConditionModalButton.tsx
@@ -1,11 +1,11 @@
import React from 'react'
import classnames from 'classnames'
import { __ } from '@wordpress/i18n'
-import { isLicensed } from '../../utils/screen'
-import { isCondition } from '../../utils/snippets/snippets'
-import { Badge } from '../common/Badge'
-import { Button } from '../common/Button'
-import { useSnippetForm } from '../../hooks/useSnippetForm'
+import { isLicensed } from '../../../utils/screen'
+import { isCondition } from '../../../utils/snippets/snippets'
+import { Badge } from '../../common/Badge'
+import { Button } from '../../common/Button'
+import { useSnippetForm } from '../SnippetForm/WithSnippetFormContext'
import type { Dispatch, SetStateAction } from 'react'
export interface ConditionModalButtonProps {
@@ -18,14 +18,16 @@ export const ConditionModalButton: React.FC = ({ setI
const hasCondition = 0 !== snippet.conditionId
return (
-
+
{isCondition(snippet) ? null
: <>
-
- {__('Conditions', 'code-snippets')}
- {__('beta', 'code-snippets')}
+
+ {__('Conditions', 'code-snippets')}
{!isLicensed() && {__('Pro', 'code-snippets')} }
-
+
setIsDialogOpen(true)}>
diff --git a/src/js/components/EditMenu/EditMenu.tsx b/src/js/components/EditMenu/EditMenu.tsx
new file mode 100644
index 000000000..e657adebe
--- /dev/null
+++ b/src/js/components/EditMenu/EditMenu.tsx
@@ -0,0 +1,118 @@
+import React, { useEffect } from 'react'
+import { SnippetForm } from './SnippetForm'
+
+const EVENT_NAME = 'code_snippets_focus_editor'
+
+interface EditMenuLinkBinding {
+ menuLink: HTMLAnchorElement
+ originalHref: string | undefined
+ originalRole: string | undefined
+ originalTabIndex: string | undefined
+ handleClick: (event: MouseEvent) => void
+ handleKeyDown: (event: KeyboardEvent) => void
+}
+
+const focusCodeEditor = () => {
+ window.dispatchEvent(new CustomEvent(EVENT_NAME))
+}
+
+const restoreAttribute = (menuLink: HTMLAnchorElement, name: string, value: string | undefined) => {
+ if (undefined !== value) {
+ menuLink.setAttribute(name, value)
+ return
+ }
+
+ menuLink.removeAttribute(name)
+}
+
+const getEditPage = (): string | undefined => {
+ const editUrl = window.CODE_SNIPPETS?.urls.edit
+
+ return editUrl ? new URL(editUrl, window.location.origin).searchParams.get('page') ?? undefined : undefined
+}
+
+const bindEditMenuLink = (menuLink: HTMLAnchorElement, page: string): EditMenuLinkBinding | undefined => {
+ const menuUrl = new URL(menuLink.href, window.location.origin)
+
+ if (page !== menuUrl.searchParams.get('page')) {
+ return undefined
+ }
+
+ const handleClick = (event: MouseEvent) => {
+ event.preventDefault()
+ focusCodeEditor()
+ }
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if ('Enter' !== event.key && ' ' !== event.key) {
+ return
+ }
+
+ event.preventDefault()
+ focusCodeEditor()
+ }
+
+ const binding = {
+ menuLink,
+ originalHref: menuLink.getAttribute('href') ?? undefined,
+ originalRole: menuLink.getAttribute('role') ?? undefined,
+ originalTabIndex: menuLink.getAttribute('tabindex') ?? undefined,
+ handleClick,
+ handleKeyDown
+ }
+
+ menuLink.dataset.codeSnippetsDisabled = 'true'
+ menuLink.setAttribute('role', 'button')
+ menuLink.setAttribute('tabindex', '0')
+ menuLink.classList.add('code-snippets-edit-menu-link')
+ menuLink.removeAttribute('href')
+ menuLink.addEventListener('click', handleClick)
+ menuLink.addEventListener('keydown', handleKeyDown)
+
+ return binding
+}
+
+const unbindEditMenuLink = ({
+ menuLink,
+ originalHref,
+ originalRole,
+ originalTabIndex,
+ handleClick,
+ handleKeyDown
+}: EditMenuLinkBinding) => {
+ menuLink.removeEventListener('click', handleClick)
+ menuLink.removeEventListener('keydown', handleKeyDown)
+ menuLink.classList.remove('code-snippets-edit-menu-link')
+ delete menuLink.dataset.codeSnippetsDisabled
+
+ restoreAttribute(menuLink, 'href', originalHref)
+ restoreAttribute(menuLink, 'role', originalRole)
+ restoreAttribute(menuLink, 'tabindex', originalTabIndex)
+}
+
+const useEditMenuLinkFocus = () => {
+ useEffect(() => {
+ const page = getEditPage()
+
+ if (!page) {
+ return
+ }
+
+ const editMenuLinks = Array.from(document.querySelectorAll('#adminmenu a[href]'))
+ .flatMap(menuLink => {
+ const binding = bindEditMenuLink(menuLink, page)
+
+ return binding ? [binding] : []
+ })
+
+ return () => {
+ editMenuLinks.forEach(unbindEditMenuLink)
+ }
+ }, [])
+}
+
+export const EditMenu = () => {
+ useEditMenuLinkFocus()
+
+ return
+}
diff --git a/src/js/components/EditorSidebar/EditorSidebar.tsx b/src/js/components/EditMenu/EditorSidebar/EditorSidebar.tsx
similarity index 50%
rename from src/js/components/EditorSidebar/EditorSidebar.tsx
rename to src/js/components/EditMenu/EditorSidebar/EditorSidebar.tsx
index f9808cd41..2a5a4ab86 100644
--- a/src/js/components/EditorSidebar/EditorSidebar.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/EditorSidebar.tsx
@@ -1,9 +1,12 @@
import React from 'react'
import { Spinner } from '@wordpress/components'
-import { isRTL } from '@wordpress/i18n'
-import { useSnippetForm } from '../../hooks/useSnippetForm'
-import { isNetworkAdmin } from '../../utils/screen'
-import { isCondition } from '../../utils/snippets/snippets'
+import { __, isRTL } from '@wordpress/i18n'
+import { useDeleteSnippet } from '../../../hooks/useDeleteSnippet'
+import { buildUrl } from '../../../utils/urls'
+import { Button } from '../../common/Button'
+import { useSnippetForm } from '../SnippetForm/WithSnippetFormContext'
+import { isNetworkAdmin } from '../../../utils/screen'
+import { isCondition } from '../../../utils/snippets/snippets'
import { ConditionModalButton } from '../ConditionModal/ConditionModalButton'
import { SnippetLocationInput } from '../SnippetForm/fields/SnippetLocationInput'
import { Notices } from '../SnippetForm/page/Notices'
@@ -12,7 +15,7 @@ import { MultisiteSharingSettings } from './controls/MultisiteSharingSettings'
import { ExportButtons } from './actions/ExportButtons'
import { SubmitButtons } from './actions/SubmitButtons'
import { ActivationSwitch } from './controls/ActivationSwitch'
-import { DeleteButton } from './actions/DeleteButton'
+import { LockControl } from './controls/LockControl'
import { PriorityInput } from './controls/PriorityInput'
import { RTLControl } from './controls/RTLControl'
import type { Dispatch, SetStateAction } from 'react'
@@ -22,27 +25,43 @@ export interface EditorSidebarProps {
}
export const EditorSidebar: React.FC = ({ setIsUpgradeDialogOpen }) => {
- const { snippet, isWorking } = useSnippetForm()
+ const { snippet, isWorking, setIsWorking, handleRequestError } = useSnippetForm()
+
+ const { requestDelete, ConfirmDeleteDialog } = useDeleteSnippet({
+ snippet,
+ setIsWorking,
+ onSuccess: () => {
+ window.location.replace(buildUrl(window.CODE_SNIPPETS?.urls.manage, { result: 'deleted' }))
+ },
+ onError: error => {
+ handleRequestError(error, __('Could not delete snippet.', 'code-snippets'))
+ }
+ })
return (
{snippet.id && !isCondition(snippet) ?
: null}
- {isNetworkAdmin() ?
: null}
+ {isNetworkAdmin() &&
}
- {isRTL() ?
: null}
+ {isRTL() &&
}
- {snippet.id
- ?
+ {snippet.id && (
+
-
-
: null}
+
+
void requestDelete()} disabled={isWorking || snippet.locked}>
+ {snippet.trashed ? __('Delete Permanently', 'code-snippets') : __('Trash', 'code-snippets')}
+
+
+
+
)}
@@ -50,7 +69,8 @@ export const EditorSidebar: React.FC = ({ setIsUpgradeDialog
{isWorking ? : ''}
-
+
+
)
}
diff --git a/src/js/components/EditorSidebar/actions/ExportButtons.tsx b/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx
similarity index 57%
rename from src/js/components/EditorSidebar/actions/ExportButtons.tsx
rename to src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx
index d4b08d818..82d84aa71 100644
--- a/src/js/components/EditorSidebar/actions/ExportButtons.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/actions/ExportButtons.tsx
@@ -1,11 +1,12 @@
import React from 'react'
import { __ } from '@wordpress/i18n'
-import { useRestAPI } from '../../../hooks/useRestAPI'
-import { Button } from '../../common/Button'
-import { downloadSnippetExportFile } from '../../../utils/files'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
-import type { Snippet } from '../../../types/Snippet'
-import type { SnippetsExport } from '../../../types/schema/SnippetsExport'
+import { useSnippetsAPI } from '../../../../hooks/useSnippetsAPI'
+import { getSnippetType } from '../../../../utils/snippets/snippets'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { downloadSnippetExportFile } from '../../../../utils/files'
+import { Button } from '../../../common/Button'
+import type { SnippetsExport } from '../../../../types/schema/SnippetsExport'
+import type { Snippet } from '../../../../types/Snippet'
interface ExportButtonProps {
name: string
@@ -34,23 +35,23 @@ const ExportButton: React.FC = ({ name, label, makeRequest })
}
export const ExportButtons: React.FC = () => {
- const { snippetsAPI } = useRestAPI()
+ const api = useSnippetsAPI()
+ const { snippet } = useSnippetForm()
return (
- {window.CODE_SNIPPETS_EDIT?.enableDownloads
- ?
- : null}
+ label={__('Download Code', 'code-snippets')}
+ makeRequest={api.exportCode}
+ />)}
)
}
diff --git a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx b/src/js/components/EditMenu/EditorSidebar/actions/ShortcodeInfo.tsx
similarity index 86%
rename from src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx
rename to src/js/components/EditMenu/EditorSidebar/actions/ShortcodeInfo.tsx
index c5128ada0..5bf243390 100644
--- a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/actions/ShortcodeInfo.tsx
@@ -1,10 +1,10 @@
import React, { useState } from 'react'
import { CheckboxControl, ExternalLink, Modal } from '@wordpress/components'
import { __ } from '@wordpress/i18n'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
-import { Button } from '../../common/Button'
-import { CopyToClipboardButton } from '../../common/CopyToClipboardButton'
-import type { Dispatch, SetStateAction } from 'react'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { Button } from '../../../common/Button'
+import { CopyToClipboardButton } from '../../../common/CopyToClipboardButton'
+import type { Dispatch, SetStateAction} from 'react'
type ShortcodeAtts = Record
@@ -107,18 +107,18 @@ const ModalContent = () => {
-
-
{__('Shortcode Options', 'code-snippets')}
+ {__('Shortcode Options', 'code-snippets')}
-
-
+
+
+
>
)
}
@@ -129,7 +129,8 @@ export const ShortcodeInfo: React.FC = () => {
return 'content' === snippet.scope && snippet.id
?
-
{__('Shortcode', 'code-snippets')}
+
{__('Shortcode', 'code-snippets')}
+
setIsModalOpen(true)} disabled={isReadOnly}>
{__('See options', 'code-snippets')}
diff --git a/src/js/components/EditorSidebar/actions/SubmitButtons.tsx b/src/js/components/EditMenu/EditorSidebar/actions/SubmitButtons.tsx
similarity index 82%
rename from src/js/components/EditorSidebar/actions/SubmitButtons.tsx
rename to src/js/components/EditMenu/EditorSidebar/actions/SubmitButtons.tsx
index 01167a09b..5da32c960 100644
--- a/src/js/components/EditorSidebar/actions/SubmitButtons.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/actions/SubmitButtons.tsx
@@ -1,11 +1,11 @@
import React from 'react'
import { __ } from '@wordpress/i18n'
-import { SubmitSnippetAction } from '../../../hooks/useSubmitSnippet'
-import { isCondition } from '../../../utils/snippets/snippets'
-import { isNetworkAdmin } from '../../../utils/screen'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
-import { SubmitButton } from '../../common/SubmitButton'
-import type { SubmitButtonProps } from '../../common/SubmitButton'
+import { SubmitSnippetAction } from '../../../../hooks/useSubmitSnippet'
+import { isCondition } from '../../../../utils/snippets/snippets'
+import { isNetworkAdmin } from '../../../../utils/screen'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { SubmitButton } from '../../../common/SubmitButton'
+import type { SubmitButtonProps } from '../../../common/SubmitButton'
const SaveButton = (props: SubmitButtonProps) => {
const { snippet } = useSnippetForm()
diff --git a/src/js/components/EditMenu/EditorSidebar/controls/ActivationSwitch.tsx b/src/js/components/EditMenu/EditorSidebar/controls/ActivationSwitch.tsx
new file mode 100644
index 000000000..aee563f3b
--- /dev/null
+++ b/src/js/components/EditMenu/EditorSidebar/controls/ActivationSwitch.tsx
@@ -0,0 +1,42 @@
+import React, { useId } from 'react'
+import { __ } from '@wordpress/i18n'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { SubmitSnippetAction, useSubmitSnippet } from '../../../../hooks/useSubmitSnippet'
+import { handleUnknownError } from '../../../../utils/errors'
+
+export const ActivationSwitch = () => {
+ const { snippet, isWorking } = useSnippetForm()
+ const { submitSnippet } = useSubmitSnippet()
+ const activationSwitchId = useId()
+
+ return (
+
+
+ {__('Status', 'code-snippets')}
+
+
+
+ {snippet.active
+ ? __('Active', 'code-snippets')
+ : __('Inactive', 'code-snippets')}
+
+
+ {
+ submitSnippet(
+ { id: snippet.id, network: snippet.network, active: !snippet.active },
+ snippet.active ? SubmitSnippetAction.SAVE_AND_DEACTIVATE : SubmitSnippetAction.SAVE_AND_ACTIVATE
+ )
+ .then(() => undefined)
+ .catch(handleUnknownError)
+ }}
+ />
+
+ )
+}
diff --git a/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx b/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx
new file mode 100644
index 000000000..84df9dcf1
--- /dev/null
+++ b/src/js/components/EditMenu/EditorSidebar/controls/LockControl.tsx
@@ -0,0 +1,47 @@
+import React from 'react'
+import classnames from 'classnames'
+import { __ } from '@wordpress/i18n'
+import { useSnippetsAPI } from '../../../../hooks/useSnippetsAPI'
+import { TooltipButton } from '../../../common/TooltipButton'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+
+export const LockControl: React.FC = () => {
+ const { update } = useSnippetsAPI()
+ const { acceptSnippet, snippet, setSnippet, isWorking, setIsWorking, setCurrentNotice } = useSnippetForm()
+
+ const handleToggle = () => {
+ setIsWorking(true)
+ setSnippet(previous => ({ ...previous, locked: !previous.locked }))
+
+ update({ id: snippet.id, network: snippet.network, locked: !snippet.locked })
+ .then(result => {
+ acceptSnippet(result)
+
+ setCurrentNotice(['updated', result.locked
+ ? __('Snippet
locked .', 'code-snippets')
+ : __('Snippet
unlocked .', 'code-snippets')])
+ })
+ .catch(() => setCurrentNotice(['error', __('Unable to lock snippet.', 'code-snippets')]))
+ .finally(() => setIsWorking(false))
+ }
+
+ return (
+
+
+ {snippet.locked
+ ?
+ : }
+
+
+ )
+}
diff --git a/src/js/components/EditMenu/EditorSidebar/controls/MultisiteSharingSettings.tsx b/src/js/components/EditMenu/EditorSidebar/controls/MultisiteSharingSettings.tsx
new file mode 100644
index 000000000..a56bf7933
--- /dev/null
+++ b/src/js/components/EditMenu/EditorSidebar/controls/MultisiteSharingSettings.tsx
@@ -0,0 +1,43 @@
+import React, { useId } from 'react'
+import { __ } from '@wordpress/i18n'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { Tooltip } from '../../../common/Tooltip'
+
+export const MultisiteSharingSettings: React.FC = () => {
+ const { snippet, setSnippet, isReadOnly } = useSnippetForm()
+ const sharingId = useId()
+
+ return (
+
+
+ {__('Share with Subsites', 'code-snippets')}
+
+
+
+ {__('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')}
+
+
+
+ {snippet.shared_network
+ ? __('Enabled', 'code-snippets')
+ : __('Disabled', 'code-snippets')}
+
+
+
+ setSnippet(previous => ({
+ ...previous,
+ active: false,
+ shared_network: event.target.checked
+ }))}
+ />
+
+ )
+}
diff --git a/src/js/components/EditorSidebar/controls/PriorityInput.tsx b/src/js/components/EditMenu/EditorSidebar/controls/PriorityInput.tsx
similarity index 58%
rename from src/js/components/EditorSidebar/controls/PriorityInput.tsx
rename to src/js/components/EditMenu/EditorSidebar/controls/PriorityInput.tsx
index 2f8128df0..844716c67 100644
--- a/src/js/components/EditorSidebar/controls/PriorityInput.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/controls/PriorityInput.tsx
@@ -1,29 +1,29 @@
-import React from 'react'
+import React, { useId } from 'react'
import { __ } from '@wordpress/i18n'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
-import { Tooltip } from '../../common/Tooltip'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
+import { Tooltip } from '../../../common/Tooltip'
export const PriorityInput = () => {
const { snippet, isReadOnly, setSnippet } = useSnippetForm()
+ const priorityId = useId()
return (
-
-
- {__('Priority', 'code-snippets')}
-
-
+
+ {__('Priority', 'code-snippets')}
+
-
+
{__('Snippets with a lower priority number will run before those with a higher number.', 'code-snippets')}
setSnippet(previous => ({
...previous,
priority: parseInt(event.target.value, 10)
diff --git a/src/js/components/EditorSidebar/controls/RTLControl.tsx b/src/js/components/EditMenu/EditorSidebar/controls/RTLControl.tsx
similarity index 61%
rename from src/js/components/EditorSidebar/controls/RTLControl.tsx
rename to src/js/components/EditMenu/EditorSidebar/controls/RTLControl.tsx
index 2dce2b256..42b8d7d03 100644
--- a/src/js/components/EditorSidebar/controls/RTLControl.tsx
+++ b/src/js/components/EditMenu/EditorSidebar/controls/RTLControl.tsx
@@ -1,19 +1,18 @@
-import React from 'react'
+import React, { useId } from 'react'
import { __ } from '@wordpress/i18n'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
+import { useSnippetForm } from '../../SnippetForm/WithSnippetFormContext'
export const RTLControl: React.FC = () => {
const { codeEditorInstance } = useSnippetForm()
+ const directionId = useId()
return (
-
-
- {__('Code Direction', 'code-snippets')}
-
-
+
+ {__('Code Direction', 'code-snippets')}
+
-
+
codeEditorInstance?.codemirror.setOption('direction', 'rtl' === event.target.value ? 'rtl' : 'ltr')
}>
{__('LTR', 'code-snippets')}
diff --git a/src/js/components/EditorSidebar/index.ts b/src/js/components/EditMenu/EditorSidebar/index.ts
similarity index 100%
rename from src/js/components/EditorSidebar/index.ts
rename to src/js/components/EditMenu/EditorSidebar/index.ts
diff --git a/src/js/components/SnippetForm/SnippetForm.tsx b/src/js/components/EditMenu/SnippetForm/SnippetForm.tsx
similarity index 58%
rename from src/js/components/SnippetForm/SnippetForm.tsx
rename to src/js/components/EditMenu/SnippetForm/SnippetForm.tsx
index 10ac3ff68..aa9b8398e 100644
--- a/src/js/components/SnippetForm/SnippetForm.tsx
+++ b/src/js/components/EditMenu/SnippetForm/SnippetForm.tsx
@@ -1,25 +1,28 @@
-import React, { useState } from 'react'
+import React, { useEffect, useRef, useState } from 'react'
import classnames from 'classnames'
import { __ } from '@wordpress/i18n'
-import { addQueryArgs } from '@wordpress/url'
-import { WithRestAPIContext } from '../../hooks/useRestAPI'
-import { WithSnippetsListContext, useSnippetsList } from '../../hooks/useSnippetsList'
-import { SubmitSnippetAction, useSubmitSnippet } from '../../hooks/useSubmitSnippet'
-import { handleUnknownError } from '../../utils/errors'
-import { createSnippetObject, getSnippetType, isCondition, validateSnippet } from '../../utils/snippets/snippets'
-import { WithSnippetFormContext, useSnippetForm } from '../../hooks/useSnippetForm'
-import { ConfirmDialog } from '../common/ConfirmDialog'
-import { UpsellDialog } from '../common/UpsellDialog'
+import { WithRestAPIContext } from '../../../hooks/useRestAPI'
+import { WithSnippetsAPIContext } from '../../../hooks/useSnippetsAPI'
+import { WithSnippetsListContext, useSnippetsList } from '../../../hooks/useSnippetsList'
+import { SubmitSnippetAction, useSubmitSnippet } from '../../../hooks/useSubmitSnippet'
+import { handleUnknownError } from '../../../utils/errors'
+import { createSnippetObject, getSnippetType, isCondition, validateSnippet } from '../../../utils/snippets/snippets'
+import { buildUrl } from '../../../utils/urls'
+import { ConfirmDialog } from '../../common/ConfirmDialog'
+import { Toolbar } from '../../common/Toolbar'
+import { UpsellBanner } from '../../common/UpsellBanner'
+import { UpsellDialog } from '../../common/UpsellDialog'
import { EditorSidebar } from '../EditorSidebar'
-import { UpsellBanner } from '../common/UpsellBanner'
+import { WithSnippetFormContext, useSnippetForm } from './WithSnippetFormContext'
import { SnippetTypeInput } from './fields/SnippetTypeInput'
import { TagsEditor } from './fields/TagsEditor'
import { CodeEditor } from './fields/CodeEditor'
import { DescriptionEditor } from './fields/DescriptionEditor'
import { NameInput } from './fields/NameInput'
+import { Notices } from './page/Notices'
import { PageHeading } from './page/PageHeading'
import type { PropsWithChildren } from 'react'
-import type { Snippet } from '../../types/Snippet'
+import type { Snippet } from '../../../types/Snippet'
const editFormClassName = ({ snippet, isReadOnly, isExpanded }: {
snippet: Snippet,
@@ -84,16 +87,16 @@ const EditForm: React.FC = ({ children, className }) => {
const [submitAction, setSubmitAction] = useState()
const doSubmit = (action?: SubmitSnippetAction) => {
- submitSnippet(action)
+ submitSnippet(snippet, action)
.then(response => {
if (response && 0 !== response.id && window.CODE_SNIPPETS) {
- if (window.location.href.toString().includes(window.CODE_SNIPPETS.urls.addNew)) {
+ if (window.location.href.includes(window.CODE_SNIPPETS.urls.addNew)) {
document.title = document.title
- .replace(__('Add New Snippet', 'code-snippets'), __('Edit Snippet', 'code-snippets'))
- .replace(__('Add New Condition', 'code-snippets'), __('Edit Condition', 'code-snippets'))
+ .replace(__('Create New Snippet', 'code-snippets'), __('Edit Snippet', 'code-snippets'))
+ .replace(__('Create New Condition', 'code-snippets'), __('Edit Condition', 'code-snippets'))
- const newUrl = addQueryArgs(window.CODE_SNIPPETS.urls.edit, { id: response.id })
- window.history.pushState({}, document.title, newUrl)
+ const newUrl = buildUrl(window.CODE_SNIPPETS.urls.edit, { id: response.id })
+ window.history.replaceState({}, document.title, newUrl)
}
}
})
@@ -123,7 +126,9 @@ const EditForm: React.FC = ({ children, className }) => {
{children}
-
+
>
)
}
@@ -138,27 +143,66 @@ const ConditionsEditor: React.FC = () => {
: null
}
+const useReloadOnPopState = (isDirty: boolean) => {
+ const currentUrl = useRef(window.location.href)
+ const skipNextUnloadPrompt = useRef(false)
+
+ useEffect(() => {
+ currentUrl.current = window.location.href
+ })
+
+ useEffect(() => {
+ const handleBeforeUnload = (event: BeforeUnloadEvent) => {
+ if (skipNextUnloadPrompt.current) {
+ skipNextUnloadPrompt.current = false
+ return
+ }
+
+ event.preventDefault()
+ // Required by Chrome and Edge versions before 119.
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
+ event.returnValue = true
+ }
+
+ if (isDirty) {
+ window.addEventListener('beforeunload', handleBeforeUnload)
+ }
+
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload)
+ }, [isDirty])
+
+ useEffect(() => {
+ const handlePopState = () => {
+ if (isDirty && !window.confirm(
+ __('You have unsaved changes. Leave this page and discard them?', 'code-snippets')
+ )) {
+ window.history.pushState({}, document.title, currentUrl.current)
+ return
+ }
+
+ skipNextUnloadPrompt.current = isDirty
+ window.location.reload()
+ }
+
+ window.addEventListener('popstate', handlePopState)
+ return () => window.removeEventListener('popstate', handlePopState)
+ }, [isDirty])
+}
+
const EditFormWrap: React.FC = () => {
- const { snippet, isReadOnly } = useSnippetForm()
+ const { snippet, isReadOnly, isDirty } = useSnippetForm()
const [isExpanded, setIsExpanded] = useState(false)
const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false)
- return (
-
-
- {isCondition(snippet)
- ?
- {__('Back to all conditions', 'code-snippets')}
-
- :
- {__('Back to all snippets', 'code-snippets')}
- }
-
+ useReloadOnPopState(isDirty)
+ return (
+ <>
+
-
+
@@ -166,7 +210,7 @@ const EditFormWrap: React.FC = () => {
-
+
@@ -178,15 +222,20 @@ const EditFormWrap: React.FC = () => {
-
+ >
)
}
export const SnippetForm: React.FC = () =>
-
- createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}>
-
-
-
+
+
+ createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}
+ >
+
+
+
+
+
diff --git a/src/js/hooks/useSnippetForm.tsx b/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx
similarity index 53%
rename from src/js/hooks/useSnippetForm.tsx
rename to src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx
index 458b9f390..a006df03f 100644
--- a/src/js/hooks/useSnippetForm.tsx
+++ b/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx
@@ -1,18 +1,20 @@
import { isAxiosError } from 'axios'
import React, { useCallback, useMemo, useState } from 'react'
-import { createContextHook } from '../utils/hooks'
-import { isLicensed } from '../utils/screen'
-import { isProSnippet } from '../utils/snippets/snippets'
+import { createContextHook } from '../../../utils/bootstrap'
+import { isLicensed } from '../../../utils/screen'
+import { isProSnippet } from '../../../utils/snippets/snippets'
import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'
-import type { ScreenNotice } from '../types/ScreenNotice'
-import type { Snippet } from '../types/Snippet'
-import type { CodeEditorInstance } from '../types/WordPressCodeEditor'
+import type { ScreenNotice } from '../../../types/ScreenNotice'
+import type { Snippet } from '../../../types/Snippet'
+import type { CodeEditorInstance } from '../../../types/vendor/WordPressCodeEditor'
export interface SnippetFormContext {
snippet: Snippet
isWorking: boolean
isReadOnly: boolean
+ isDirty: boolean
setSnippet: Dispatch
>
+ acceptSnippet: (snippet: Snippet) => void
updateSnippet: Dispatch>
setIsWorking: Dispatch>
currentNotice: ScreenNotice | undefined
@@ -22,19 +24,52 @@ export interface SnippetFormContext {
setCodeEditorInstance: Dispatch>
}
-export const [SnippetFormContext, useSnippetForm] = createContextHook('SnippetForm')
+const [Context, useSnippetForm] = createContextHook('useSnippetForm')
export interface WithSnippetFormContextProps extends PropsWithChildren {
initialSnippet: () => Snippet
}
+const getSnippetDraftState = (snippet: Snippet) => ({
+ name: snippet.name,
+ desc: snippet.desc,
+ code: snippet.code,
+ tags: snippet.tags,
+ scope: snippet.scope,
+ priority: snippet.priority,
+ active: snippet.active,
+ locked: snippet.locked,
+ network: snippet.network,
+ sharedNetwork: snippet.shared_network,
+ conditionId: snippet.conditionId
+})
+
+const isSnippetDraftDirty = (snippet: Snippet, savedSnippet: Snippet): boolean => {
+ const draftState = JSON.stringify(getSnippetDraftState(snippet))
+ const savedDraftState = JSON.stringify(getSnippetDraftState(savedSnippet))
+ return draftState !== savedDraftState
+}
+
export const WithSnippetFormContext: React.FC = ({ children, initialSnippet }) => {
- const [snippet, setSnippet] = useState(initialSnippet)
+ const [initialValue] = useState(initialSnippet)
+ const [snippet, setSnippet] = useState(initialValue)
+ const [savedSnippet, setSavedSnippet] = useState(initialValue)
const [isWorking, setIsWorking] = useState(false)
const [currentNotice, setCurrentNotice] = useState()
const [codeEditorInstance, setCodeEditorInstance] = useState()
- const isReadOnly = useMemo(() => !isLicensed() && isProSnippet({ scope: snippet.scope }), [snippet.scope])
+ const isReadOnly = useMemo(
+ () => snippet.locked || !isLicensed() && isProSnippet({ scope: snippet.scope }),
+ [snippet.locked, snippet.scope]
+ )
+ const isDirty = useMemo(
+ () => isSnippetDraftDirty(snippet, savedSnippet),
+ [snippet, savedSnippet]
+ )
+ const acceptSnippet = useCallback((value: Snippet) => {
+ setSnippet(value)
+ setSavedSnippet(value)
+ }, [])
const handleRequestError = useCallback((error: unknown, message?: string) => {
console.error('Request failed', error)
@@ -55,7 +90,9 @@ export const WithSnippetFormContext: React.FC = ({
snippet,
isWorking,
isReadOnly,
+ isDirty,
setSnippet,
+ acceptSnippet,
setIsWorking,
updateSnippet,
currentNotice,
@@ -65,5 +102,7 @@ export const WithSnippetFormContext: React.FC = ({
setCodeEditorInstance
}
- return {children}
+ return {children}
}
+
+export { useSnippetForm }
diff --git a/src/js/components/SnippetForm/fields/CodeEditor.tsx b/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx
similarity index 54%
rename from src/js/components/SnippetForm/fields/CodeEditor.tsx
rename to src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx
index 076e5d0cf..7a3deadde 100644
--- a/src/js/components/SnippetForm/fields/CodeEditor.tsx
+++ b/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx
@@ -1,29 +1,63 @@
-import React, { useEffect, useRef } from 'react'
+import React, { useEffect, useId, useRef } from 'react'
import { __ } from '@wordpress/i18n'
-import { useSubmitSnippet } from '../../../hooks/useSubmitSnippet'
-import { handleUnknownError } from '../../../utils/errors'
-import { isMacOS } from '../../../utils/screen'
-import { useSnippetForm } from '../../../hooks/useSnippetForm'
-import { Button } from '../../common/Button'
-import { ExpandIcon } from '../../common/icons/ExpandIcon'
-import { MinimiseIcon } from '../../common/icons/MinimiseIcon'
+import { useSubmitSnippet } from '../../../../hooks/useSubmitSnippet'
+import { handleUnknownError } from '../../../../utils/errors'
+import { isMacOS } from '../../../../utils/screen'
+import { useSnippetForm } from '../WithSnippetFormContext'
+import { Button } from '../../../common/Button'
+import { ExpandIcon } from '../../../common/icons/ExpandIcon'
+import { MinimiseIcon } from '../../../common/icons/MinimiseIcon'
import { CodeEditorShortcuts } from './CodeEditorShortcuts'
import type { Dispatch, RefObject, SetStateAction } from 'react'
interface EditorTextareaProps {
textareaRef: RefObject
+ snippetCodeId: string
}
-const EditorTextarea: React.FC = ({ textareaRef }) => {
+const useFocusEditorShortcut = (
+ textareaRef: RefObject
+) => {
+ const { codeEditorInstance } = useSnippetForm()
+
+ useEffect(() => {
+ const focusEditor = () => {
+ if (codeEditorInstance) {
+ codeEditorInstance.codemirror.focus()
+ return
+ }
+
+ textareaRef.current?.focus()
+ }
+
+ window.addEventListener('code_snippets_focus_editor', focusEditor)
+
+ return () => {
+ window.removeEventListener('code_snippets_focus_editor', focusEditor)
+ }
+ }, [codeEditorInstance, textareaRef])
+}
+
+const EditorTextarea: React.FC = ({ textareaRef, snippetCodeId }) => {
+ const descriptionId = useId()
const { snippet, setSnippet } = useSnippetForm()
return (
-
+
+
+ {__('In the editing area, the Tab key enters a tab character. To exit the code editor, press the Escape key and then the Tab key.', 'code-snippets')}
+