diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..e9397ad1e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,90 @@ +name: Bug report +description: Report a reproducible problem or unexpected behavior in Huabu +title: '[Bug]: ' +body: + - type: markdown + attributes: + value: | + Thanks for helping improve Huabu. Please search existing issues before submitting a new report. + + Do not include API keys, access tokens, private documents, personal information, or other sensitive data. + + - type: checkboxes + id: existing-issues + attributes: + label: Existing issues + options: + - label: I searched the existing issues and did not find a report of the same problem. + required: true + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Describe the behavior you observed, including any error message. + placeholder: Tell us what went wrong. + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: What did you expect? + description: Describe what you were trying to do and what you expected Huabu to do instead. + placeholder: Tell us what should have happened. + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: List the shortest sequence of steps that triggers the problem. + placeholder: | + 1. Open Huabu and ... + 2. Select ... + 3. Observe ... + validations: + required: true + + - type: dropdown + id: frequency + attributes: + label: How often does it happen? + options: + - Every time + - Sometimes + - Only once + - Not sure + validations: + required: true + + - type: textarea + id: system-information + attributes: + label: System information + description: In the desktop app, choose Help → Troubleshooting → Copy System Information, then paste the result here. + render: shell + validations: + required: true + + - type: textarea + id: ai-details + attributes: + label: AI-related details + description: Complete this only if the problem involves an AI response or edit. + placeholder: Include the interaction mode, agent, provider and model, selected context, relevant output, and whether proposed changes were applied. Never include an API key. + + - type: textarea + id: screenshots-and-logs + attributes: + label: Screenshots and logs + description: Drag screenshots, recordings, or the relevant part of server.log here after removing sensitive information. + + - type: checkboxes + id: sensitive-information + attributes: + label: Sensitive information + options: + - label: I reviewed this report and its attachments and removed sensitive information. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..fabf850db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Huabu Handbook + url: https://microsoft.github.io/Huabu/docs/ + about: Read the user handbook before reporting a usage question. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..f7a1799d4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + quality: + name: Lint / Format / Typecheck / Test / Build / Headers + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.34.3 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Run quality checks + run: pnpm check + env: + DOCS_BASE_PATH: /Huabu/ + DOCS_CANONICAL_ORIGIN: https://microsoft.github.io diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 000000000..1bb2ab772 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,66 @@ +name: Deploy user handbook + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'apps/docs/**' + - '.github/workflows/deploy-docs.yml' + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out Huabu + uses: actions/checkout@v4 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.34.3 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Build handbook + run: pnpm --filter @huabu/docs build + env: + DOCS_BASE_PATH: /Huabu/ + DOCS_CANONICAL_ORIGIN: https://microsoft.github.io + + - name: Configure GitHub Pages + uses: actions/configure-pages@v5 + + - name: Upload handbook artifact + uses: actions/upload-pages-artifact@v3 + with: + path: apps/docs/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/sync-release.yml b/.github/workflows/sync-release.yml new file mode 100644 index 000000000..f754fb0b4 --- /dev/null +++ b/.github/workflows/sync-release.yml @@ -0,0 +1,148 @@ +name: Sync upstream release + +on: + workflow_dispatch: + inputs: + destination_tag: + description: 'Destination tag; leave empty to use source tag' + required: false + type: string + +permissions: + contents: write + +concurrency: + group: sync-release-${{ inputs.destination_tag }} + cancel-in-progress: false + +jobs: + sync-release: + runs-on: ubuntu-latest + + steps: + - name: Read latest source release + shell: bash + env: + SOURCE_REPO: ${{ secrets.SOURCE_REPOSITORY }} + SOURCE_TOKEN: ${{ secrets.SOURCE_REPO_TOKEN }} + REQUESTED_DEST_TAG: ${{ inputs.destination_tag }} + run: | + set -euo pipefail + + GH_TOKEN="$SOURCE_TOKEN" gh release view \ + --repo "$SOURCE_REPO" \ + --json tagName,body,isPrerelease \ + > source-release.json + + SOURCE_TAG="$(jq -r '.tagName' source-release.json)" + IS_PRERELEASE="$(jq -r '.isPrerelease' source-release.json)" + + DEST_TAG="$REQUESTED_DEST_TAG" + if [[ -z "$DEST_TAG" ]]; then + DEST_TAG="$SOURCE_TAG" + fi + + for tag in "$SOURCE_TAG" "$DEST_TAG"; do + if [[ "$tag" == -* ]] || ! git check-ref-format "refs/tags/$tag"; then + echo "Invalid release tag: $tag" >&2 + exit 1 + fi + done + + jq -r '.body // ""' source-release.json > release-notes.md + + { + echo "source_tag=$SOURCE_TAG" + echo "dest_tag=$DEST_TAG" + echo "is_prerelease=$IS_PRERELEASE" + } >> "$GITHUB_OUTPUT" + id: release + + - name: Download source assets + id: download + shell: bash + env: + SOURCE_REPO: ${{ secrets.SOURCE_REPOSITORY }} + SOURCE_TOKEN: ${{ secrets.SOURCE_REPO_TOKEN }} + SOURCE_TAG: ${{ steps.release.outputs.source_tag }} + run: | + set -euo pipefail + + mkdir -p assets + + if ! GH_TOKEN="$SOURCE_TOKEN" gh release download \ + "$SOURCE_TAG" \ + --repo "$SOURCE_REPO" \ + --dir assets; then + echo "No downloadable assets for $SOURCE_TAG." + fi + + asset_count="$(find assets -maxdepth 1 -type f | wc -l)" + if (( asset_count == 0 )); then + echo "Source release has no assets; skipping publish." + echo "has_assets=false" >> "$GITHUB_OUTPUT" + else + echo "has_assets=true" >> "$GITHUB_OUTPUT" + fi + + - name: Create or update destination release + if: steps.download.outputs.has_assets == 'true' + shell: bash + env: + DEST_REPO: ${{ github.repository }} + DEST_TOKEN: ${{ github.token }} + SOURCE_TAG: ${{ steps.release.outputs.source_tag }} + DEST_TAG: ${{ steps.release.outputs.dest_tag }} + IS_PRERELEASE: ${{ steps.release.outputs.is_prerelease }} + run: | + set -euo pipefail + + TITLE="Huabu $SOURCE_TAG" + + prerelease_args=() + if [[ "$IS_PRERELEASE" == "true" ]]; then + prerelease_args+=(--prerelease) + fi + + if GH_TOKEN="$DEST_TOKEN" gh release view \ + "$DEST_TAG" \ + --repo "$DEST_REPO" >/dev/null 2>&1; then + + echo "Updating existing release $DEST_TAG" + + GH_TOKEN="$DEST_TOKEN" gh release edit \ + "$DEST_TAG" \ + --repo "$DEST_REPO" \ + --title "$TITLE" \ + --notes-file release-notes.md \ + --prerelease="$IS_PRERELEASE" + else + echo "Creating release $DEST_TAG" + + GH_TOKEN="$DEST_TOKEN" gh release create \ + "$DEST_TAG" \ + --repo "$DEST_REPO" \ + --title "$TITLE" \ + --notes-file release-notes.md \ + "${prerelease_args[@]}" + fi + + - name: Upload destination assets + if: steps.download.outputs.has_assets == 'true' + shell: bash + env: + DEST_REPO: ${{ github.repository }} + DEST_TOKEN: ${{ github.token }} + DEST_TAG: ${{ steps.release.outputs.dest_tag }} + run: | + set -euo pipefail + + mapfile -d '' assets < <( + find assets -maxdepth 1 -type f -print0 + ) + + GH_TOKEN="$DEST_TOKEN" gh release upload \ + "$DEST_TAG" \ + "${assets[@]}" \ + --repo "$DEST_REPO" \ + --clobber diff --git a/.gitignore b/.gitignore index d5a18deed..aa0b14d44 100644 --- a/.gitignore +++ b/.gitignore @@ -315,6 +315,11 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ +# Huabu handbook build output +apps/docs/.ssr/ +apps/docs/dist/ +apps/docs/tsconfig.node.tsbuildinfo + # Visual Studio 6 build log *.plg diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..4c6ba3818 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +**/dist/ +**/node_modules/ +**/.ssr/ +**/*.tsbuildinfo diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..8beea539b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf", + "arrowParens": "always", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..02e6376c5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing to Huabu + +Thank you for your interest in Huabu. + +## Current contribution scope + +The current contribution scope focuses on bug reports, documentation feedback, and research feedback submitted through the [Huabu issue tracker](https://github.com/microsoft/Huabu/issues). + +Before opening an issue, search existing issues, use the latest available release, and follow the guidance in [SUPPORT.md](SUPPORT.md). Do not include API keys, access tokens, private documents, personal information, or other sensitive data in public issues, logs, or screenshots. + +Security vulnerabilities must not be reported through public GitHub issues. Follow [SECURITY.md](SECURITY.md) to report them privately. + +## Contributor License Agreement + +Most contributions require agreement to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use the contribution. For details, visit [Contributor License Agreements](https://cla.opensource.microsoft.com). When a pull request is submitted, a CLA bot will determine whether the agreement is required and provide instructions. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), review the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/), or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with additional questions or comments. + +## License + +By contributing to this repository, you agree that your contributions will be licensed under the [MIT License](LICENSE), unless explicitly stated otherwise. diff --git a/RAI_README.md b/RAI_README.md new file mode 100644 index 000000000..7338a4949 --- /dev/null +++ b/RAI_README.md @@ -0,0 +1,105 @@ +# Microsoft Responsible AI Transparency Documentation for Research - Huabu + +## Overview + +Huabu is a canvas-based interaction framework for human–AI collaboration. It provides a shared two-dimensional workspace where humans and AI agents co-work on the same materials — documents, web pages, notes, and AI conversations — arranged as persistent, spatially organized nodes. + +Huabu is designed around three principles: externalizing thinking, so that the intermediate structure of work becomes visible and manipulable; sharing a cognitive space with AI, so that agents can follow broader intent rather than only the latest instruction; and supporting natural interaction — handwriting, touch, and speech alongside typing — so that users can stay in flow while ideas are still forming. + +### WHAT CAN Huabu do + +Huabu was developed to support the early, exploratory stages of complex work — where the central challenge is deciding what to do, rather than executing well-formed instruction. On the canvas, users can externalize ideas, notes, references, and AI outputs as persistent nodes; arrange them spatially to reflect relationships, priorities, and uncertainty; and let AI agents observe the evolving workspace to help organize materials, synthesize across nodes, identify what remains unresolved, and hand off mature ideas to downstream execution agents. + + + +### INTENDED USES + +Huabu is best suited for open-ended, exploratory knowledge work — including research, planning, ideation, and early-stage design — where intent is still forming and benefits from being externalized and discussed in a shared visual space rather than a linear chat thread. + +Huabu is being shared with the research community to facilitate reproduction of our results and foster further research in this area. + +Huabu is intended to be used by domain experts who are independently capable of evaluating the quality of outputs before acting on them. + +### OUT-OF-SCOPE USES + +Huabu is not well suited for tasks that require a single authoritative answer, time-critical decision making, or workflows where the AI’s outputs would be acted on without human review. It is also not suited for users who lack the domain expertise needed to evaluate AI-generated content on the canvas, since responsibility for assessing quality and correctness rests with the user. + +We do not recommend using Huabu in commercial or real-world applications without further testing and development. It is being released for research purposes. + +Huabu was not designed or evaluated for all possible downstream purposes. Developers should consider its inherent limitations as they select use cases, and evaluate and mitigate for accuracy, safety, and fairness concerns specific to each intended downstream use. + +Without further testing and development, Huabu should not be used in sensitive domains where inaccurate outputs could suggest actions that lead to injury or negatively impact an individual's legal, financial, or life opportunities. + +We do not recommend using Huabu in the context of high-risk decision making (e.g. in law enforcement, legal, finance, or healthcare). + +## HOW TO GET STARTED + +To begin using Huabu, download the latest installer for your operating system from the [GitHub Releases page](https://github.com/microsoft/Huabu/releases), then run the installer and launch the application. See the repository README for instructions on connecting a base LLM/MLLM and configuring example workflows. + +## Evaluation + +Huabu was evaluated on its ability to support exploratory knowledge work scenarios — including research synthesis, planning, and ideation — through internal dogfooding and qualitative studies with researchers. + + + +### EVALUATION METHODS + +We used qualitative observation, task walk-throughs, and structured user feedback from internal dogfooding sessions to measure Huabu’s performance. + +We compared the performance of Huabu against the linear chat baseline that is the dominant paradigm today using participant-reported feedback across research, planning, and ideation scenarios. + +The model used for evaluation was GPT 5.5. For more on this specific model, please see [Introducing GPT-5.5 | OpenAI](https://openai.com/index/introducing-gpt-5-5/). + +Results may vary if Huabu is used with a different model based on its unique design, configuration and training. + +### EVALUATION RESULTS + +At a high level, we found that Huabu performed well in helping users externalize and organize early-stage thinking, reduced the cognitive load of holding intermediate ideas in working memory, and made it easier for AI agents to act on the broader context of a task rather than only the latest message. + +## LIMITATIONS + +Huabu was developed for research and experimental purposes. Further testing and validation are needed before considering its application in commercial or real-world scenarios. It does not control any downstream agents, and users are responsible for configuring them safely. + +Huabu was designed and tested using the English language. Performance in other languages may vary and should be assessed by someone who is both an expert in the expected outputs and a native speaker of that language. + +Outputs generated by AI may include factual errors, fabrication, or speculation. Users are responsible for assessing the accuracy of generated content. All decisions leveraging outputs of the system should be made with human oversight and not be based solely on system outputs. + +Huabu inherits any biases, errors, or omissions produced by the model you choose to use with it. Developers are advised to choose an appropriate MLLM carefully, depending on the intended use case. + +There has not been a systematic effort to ensure that systems using Huabu are protected from security vulnerabilities such as indirect prompt injection attacks. Any systems using it should take proactive measures to harden their systems as appropriate. + +## BEST PRACTICES + +Better performance can be achieved by curating canvas content carefully, keeping nodes concise and well-labeled, and grouping related materials spatially so that the AI agent can interpret structure from the layout. Reviewing AI suggestions before acting on them — particularly before handing canvas content off to downstream execution agents — is strongly recommended. + +We strongly encourage users to use LLMs/MLLMs that support robust Responsible AI mitigations, such as Azure Open AI (AOAI) services. Such services continually update their safety and RAI mitigations with the latest industry standards for responsible use. For more on AOAI’s best practices when employing foundations models for scripts and applications: + +- [What is Azure AI Content Safety?](https://learn.microsoft.com/en-us/azure/ai-services/content-safety/overview) +- [Overview of Responsible AI practices for Azure OpenAI models](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/overview) +- [Azure OpenAI Transparency Note](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/transparency-note) +- [OpenAI’s Usage policies](https://openai.com/policies/usage-policies) +- [Azure OpenAI’s Code of Conduct](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/code-of-conduct) + +Users are responsible for sourcing their datasets legally and ethically. This could include securing appropriate rights, ensuring consent for use of audio/images, and/or the anonymization of data prior to use in research. + +Users are reminded to be mindful of data privacy concerns and are encouraged to review the privacy policies associated with any models and data storage solutions interfacing with Huabu. + +It is the user’s responsibility to ensure that the use of Huabu complies with relevant data protection regulations and organizational guidelines. + +Developers should follow transparency best practices and inform end-users they are interacting with an AI system. + +## LICENSE + +MIT License + +Nothing disclosed here, including the Out of Scope Uses section, should be interpreted as or deemed a restriction or modification to the license the code is released under. + +## TRADEMARKS + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft’s Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. + +## CONTACT + +This research was conducted by members of [Microsoft Research](https://www.microsoft.com/en-us/research/). We welcome feedback and collaboration from our audience. If you have suggestions, questions, or observe unexpected/offensive behavior in our technology, please contact us at nanchen@microsoft.com. + +If the team receives reports of undesired behavior or identifies issues independently, we will update this repository with appropriate mitigations. diff --git a/README.md b/README.md index ffd0cdd5c..dc6e0535e 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,68 @@ -# Project +

+ Huabu logo + Huabu +

-> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. +

Where you and your agents think together.

-As the maintainer of this project, please make a few updates: +

+ User Handbook · + Download · + Feedback +

-- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README +Huabu is a spatial workspace for thinking with AI. It keeps ideas and AI work persistent, visible, and connected, so collaboration can continue across sessions instead of disappearing into isolated chat threads. -## Contributing +## Core Features + +- **Bring scattered information together** — drag ideas, notes, documents, and outputs from different chat sessions and sources into one Space, so everything relevant stays visible and connected. +- **Organize your thinking with AI** — work with AI to arrange information, uncover relationships, and clarify ideas while reviewing and controlling every proposed change. +- **Let your agents work with richer context** — bring your own agents into a Space, where they can understand not only the materials you provide, but also how those materials relate to one another—helping them better grasp your intent and do more relevant work. + + > Source code will be released in a future update. + +## Download and Install + +This initial release distributes packaged desktop applications through [GitHub Releases](https://github.com/microsoft/Huabu/releases/latest). + +1. Download the latest package for your platform from the Releases page. +2. On macOS, open the `.dmg`; on Windows, run the `.exe` installer. +3. Launch Huabu and choose a local folder as your **Home**. + +The current desktop packages target macOS on Apple silicon and Windows on x64. Available packages may vary by release; consult the release notes before installing. + +## Quick Start + +Follow the [Huabu User Handbook](https://microsoft.github.io/Huabu/docs/) for setup instructions, model configuration, core concepts, and guidance on creating and working in your first Space. The handbook is the canonical source for product usage instructions. + +Huabu does not provide an LLM service as part of the application. Use of a model or external capability may require a separate account, credentials, subscription, or usage charges from its provider. -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit [Contributor License Agreements](https://cla.opensource.microsoft.com). +## Data, Credentials, and Connected Services -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. +Huabu stores Spaces and their materials in the Home folder selected by the user. Credentials saved in the packaged desktop application are encrypted at rest using operating-system-protected storage. + +Huabu does not send telemetry, crash reports, diagnostic logs, or usage data to Microsoft. + +When an AI feature is used, the configured model provider or connected service may receive the prompt and relevant Space content needed to fulfill the request. External agents may also read or modify files within the working directory configured for them. Review the terms, privacy practices, data-handling controls, and permissions of every provider or agent before connecting it, and do not provide sensitive material unless its use is authorized. + +## Research Status and Responsible Use + +Huabu is released for research and experimental use. AI-generated output may be inaccurate, incomplete, biased, or unsafe, and Huabu has not been evaluated for every language, model, or downstream scenario. Do not use it for high-risk or time-critical decisions, or in workflows where output is acted upon without qualified human review. + +For the complete intended-use statement, evaluation summary, limitations, and responsible-use guidance, read [RAI_README.md](RAI_README.md). + +## Support and Feedback + +Community support is available on a best-effort basis through the [Huabu issue tracker](https://github.com/microsoft/Huabu/issues). See [SUPPORT.md](SUPPORT.md) for support guidance. Report security vulnerabilities privately by following [SECURITY.md](SECURITY.md). + +## Contributing -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +We welcome bug reports, documentation feedback, and research feedback. See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidance and Contributor License Agreement requirements. This project follows the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). ## Trademarks -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos is subject to those third parties' policies. +## License +The materials included directly in this repository are licensed under the [MIT License](LICENSE), except where otherwise noted. The packaged Huabu application distributed through GitHub Releases is provided under the license terms included with that release. diff --git a/SECURITY.md b/SECURITY.md index e751608fc..656f79188 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,4 +11,4 @@ For security reporting information, locations, contact information, and policies please review the latest guidance for Microsoft repositories at [https://aka.ms/SECURITY.md](https://aka.ms/SECURITY.md). - \ No newline at end of file + diff --git a/SUPPORT.md b/SUPPORT.md index eaf439aec..b93ce8636 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,25 +1,23 @@ -# TODO: The maintainer of this repo has not yet edited this file +# Support -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? +## How to get help -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. -- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. +Community support is available through the [Huabu GitHub issue tracker](https://github.com/microsoft/Huabu/issues) on a best-effort basis. -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* +Before opening an issue: -# Support +1. Update Huabu to the latest available version and confirm that the problem still occurs. +2. Search [existing issues](https://github.com/microsoft/Huabu/issues) to avoid duplicates. +3. Follow the [Report an Issue guide](https://microsoft.github.io/Huabu/docs/reference/issues) to collect reproduction steps, system information, and relevant screenshots or logs. + +If an existing issue describes the same problem, add any new details there instead of opening a duplicate. Otherwise, [create a new issue](https://github.com/microsoft/Huabu/issues/new/choose). -## How to file issues and get help +Never include API keys, access tokens, private documents, personal information, or other sensitive data in a public issue. Review and redact screenshots and logs before uploading them. -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +## Security issues -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. +Do not report security vulnerabilities through public GitHub issues. Follow the instructions in [SECURITY.md](SECURITY.md) instead. -## Microsoft Support Policy +## Microsoft Support Policy -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. +Support for Huabu is limited to the community resources listed above. diff --git a/apps/docs/.npmrc b/apps/docs/.npmrc new file mode 100644 index 000000000..538f0f031 --- /dev/null +++ b/apps/docs/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmjs.org/ diff --git a/apps/docs/README.md b/apps/docs/README.md new file mode 100644 index 000000000..0b759be42 --- /dev/null +++ b/apps/docs/README.md @@ -0,0 +1,77 @@ +# Huabu User Handbook + +This package contains the source for the public Huabu User Handbook at [https://microsoft.github.io/Huabu/docs/](https://microsoft.github.io/Huabu/docs/). It is an independent Vite application maintained directly in the Huabu repository. + +## Requirements + +- Node.js 22 +- pnpm 10.34.3 + +## Install dependencies + +From the repository root, run: + +```sh +pnpm install +``` + +## Local development + +Start the Vite development server: + +```sh +pnpm dev +``` + +The development server defaults to `http://localhost:43127`. Set `DOCS_PORT` to use another port. Search is unavailable in development because Pagefind indexes are generated during the production build. + +To preview the complete generated site, including search: + +```sh +pnpm build +pnpm preview +``` + +The preview server defaults to `http://localhost:43128`. + +## Quality checks + +Run the complete repository quality gate from the repository root before opening a pull request: + +```sh +pnpm check +``` + +The root command runs these checks across every workspace package in order: + +- `pnpm lint` — applies the repository ESLint rules to all supported source files. +- `pnpm format:check` — verifies repository-wide Prettier formatting without modifying files. +- `pnpm typecheck` — runs each workspace package's type checker when present. +- `pnpm test` — runs each workspace package's test suite when present. +- `pnpm build` — builds each workspace package when present; for this package it also prerenders, indexes, and validates the deployable static artifact. +- `node scripts/check-headers.mjs` — verifies Microsoft MIT headers across tracked source files. + +To apply safe automatic lint and formatting fixes: + +```sh +pnpm lint:fix +pnpm format +``` + +Review automatic changes before committing them. + +## Tests + +Run all tests once: + +```sh +pnpm test +``` + +Run tests in watch mode while developing: + +```sh +pnpm test:watch +``` + +Tests use Vitest with Happy DOM. Add or update tests when changing routing, navigation, base-path behavior, or documentation data with executable invariants. diff --git a/apps/docs/index.html b/apps/docs/index.html new file mode 100644 index 000000000..97403eb91 --- /dev/null +++ b/apps/docs/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + Huabu Handbook + + +
+ + + diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 000000000..298b00ff5 --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,38 @@ +{ + "name": "@huabu/docs", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "pnpm run typecheck && vite build && vite build --ssr src/entry-server.tsx --outDir .ssr && node scripts/prerender.mjs && pagefind --site dist && node scripts/validate-build.mjs", + "preview": "vite preview --host 0.0.0.0", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit && tsc -p tsconfig.node.json --noEmit" + }, + "dependencies": { + "@pagefind/default-ui": "^1.4.0", + "clsx": "^2.1.1", + "lucide-react": "^0.577.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router": "^7.13.1", + "react-router-dom": "^7.13.1", + "tailwind-merge": "^3.5.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@types/node": "^22.15.0", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.3.4", + "happy-dom": "^15.11.7", + "pagefind": "^1.4.0", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3", + "vite": "^6.0.0", + "vitest": "^4.0.18" + } +} diff --git a/apps/docs/postcss.config.js b/apps/docs/postcss.config.js new file mode 100644 index 000000000..6a3193b0c --- /dev/null +++ b/apps/docs/postcss.config.js @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/apps/docs/public/docs/quick-start/add-content.png b/apps/docs/public/docs/quick-start/add-content.png new file mode 100644 index 000000000..379927664 Binary files /dev/null and b/apps/docs/public/docs/quick-start/add-content.png differ diff --git a/apps/docs/public/docs/quick-start/configure-llm.png b/apps/docs/public/docs/quick-start/configure-llm.png new file mode 100644 index 000000000..ebacd7464 Binary files /dev/null and b/apps/docs/public/docs/quick-start/configure-llm.png differ diff --git a/apps/docs/public/docs/quick-start/create-canvas.png b/apps/docs/public/docs/quick-start/create-canvas.png new file mode 100644 index 000000000..d11d969a8 Binary files /dev/null and b/apps/docs/public/docs/quick-start/create-canvas.png differ diff --git a/apps/docs/public/docs/work-in-a-space/quick-create-arrows-full.png b/apps/docs/public/docs/work-in-a-space/quick-create-arrows-full.png new file mode 100644 index 000000000..49cdd2ff4 Binary files /dev/null and b/apps/docs/public/docs/work-in-a-space/quick-create-arrows-full.png differ diff --git a/apps/docs/public/favicon.png b/apps/docs/public/favicon.png new file mode 100644 index 000000000..737d97da6 Binary files /dev/null and b/apps/docs/public/favicon.png differ diff --git a/apps/docs/public/favicon.svg b/apps/docs/public/favicon.svg new file mode 100644 index 000000000..9162bab3d --- /dev/null +++ b/apps/docs/public/favicon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/docs/scripts/prerender.mjs b/apps/docs/scripts/prerender.mjs new file mode 100644 index 000000000..2a938c81b --- /dev/null +++ b/apps/docs/scripts/prerender.mjs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { rm, mkdir, readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const appRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +); +const distDir = path.join(appRoot, 'dist'); +const ssrDir = path.join(appRoot, '.ssr'); +const serverEntry = path.join(ssrDir, 'entry-server.js'); +const template = await readFile(path.join(distDir, 'index.html'), 'utf8'); +const { docsBasePath, renderRoute, routeManifest } = await import( + `${pathToFileURL(serverEntry).href}?build=${Date.now()}` +); +const canonicalOrigin = process.env.DOCS_CANONICAL_ORIGIN?.replace(/\/$/, ''); + +function escapeAttribute(value) { + return value + .replaceAll('&', '&') + .replaceAll('"', '"') + .replaceAll('<', '<') + .replaceAll('>', '>'); +} + +for (const route of routeManifest) { + const markup = await renderRoute(route.path); + const canonical = canonicalOrigin + ? `` + : ''; + const html = template + .replace( + /.*?<\/title>/s, + `<title>${escapeAttribute(route.title)} · Huabu Handbook`, + ) + .replace( + //s, + ``, + ) + .replace('', ` ${canonical}\n `) + .replace('
', `
${markup}
`); + const routeDir = path.join(distDir, ...route.path.split('/').filter(Boolean)); + await mkdir(routeDir, { recursive: true }); + await writeFile(path.join(routeDir, 'index.html'), html); +} + +const redirectTarget = `${docsBasePath}docs/`; +const redirectHtml = `\n\n \n \n \n \n Huabu Handbook\n \n \n

Open the Huabu Handbook

\n \n\n`; +await writeFile(path.join(distDir, 'index.html'), redirectHtml); +await rm(ssrDir, { recursive: true, force: true }); diff --git a/apps/docs/scripts/validate-build.mjs b/apps/docs/scripts/validate-build.mjs new file mode 100644 index 000000000..80bf0cecf --- /dev/null +++ b/apps/docs/scripts/validate-build.mjs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { access, readdir, readFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const appRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +); +const distDir = path.join(appRoot, 'dist'); +const source = await readFile( + path.join(appRoot, 'src', 'navigation.ts'), + 'utf8', +); +const routes = [...source.matchAll(/to: '(\/docs[^']*)'/g)].map( + (match) => match[1], +); +const uniqueRoutes = [...new Set(routes)]; +const failures = []; +const baseSegments = (process.env.DOCS_BASE_PATH ?? '/') + .split('/') + .filter(Boolean); +const basePath = + baseSegments.length === 0 ? '/' : `/${baseSegments.join('/')}/`; + +async function exists(file) { + try { + await access(file); + return true; + } catch { + return false; + } +} + +async function validateLocalReference(reference, sourceFile) { + if (reference.startsWith('#')) return; + + const url = new URL(reference, 'https://handbook.invalid'); + if (url.origin !== 'https://handbook.invalid') return; + + let pathname = decodeURIComponent(url.pathname); + if (basePath !== '/') { + if (!pathname.startsWith(basePath)) { + failures.push( + `Reference escapes DOCS_BASE_PATH in ${sourceFile}: ${reference}`, + ); + return; + } + pathname = `/${pathname.slice(basePath.length)}`; + } + + const relativePath = pathname.replace(/^\//, ''); + const target = pathname.endsWith('/') + ? path.join(distDir, relativePath, 'index.html') + : path.join(distDir, relativePath); + if (!(await exists(target))) { + failures.push(`Missing local reference in ${sourceFile}: ${reference}`); + } +} + +for (const route of uniqueRoutes) { + const htmlPath = path.join( + distDir, + ...route.split('/').filter(Boolean), + 'index.html', + ); + if (!(await exists(htmlPath))) { + failures.push(`Missing prerendered route: ${route}`); + continue; + } + const html = await readFile(htmlPath, 'utf8'); + if (!html.includes(']/.test(html)) failures.push(`Missing H1: ${route}`); + if (html.includes('data-docs-loading')) + failures.push(`Suspense fallback was prerendered: ${route}`); + if (!/[^<]+ · Huabu Handbook<\/title>/.test(html)) + failures.push(`Missing route title: ${route}`); + if (!html.includes('<script type="module"')) + failures.push(`Missing hydration entry: ${route}`); + if (/localhost|[A-Z]:\\|VITE_/.test(html)) + failures.push(`Forbidden build value in: ${route}`); + + const references = [ + ...html.matchAll(/<(?:a|link)\b[^>]*\bhref="([^"]+)"/g), + ...html.matchAll(/<(?:img|script)\b[^>]*\bsrc="([^"]+)"/g), + ].map((match) => match[1]); + for (const reference of references) { + await validateLocalReference(reference, route); + } +} + +const root = await readFile(path.join(distDir, 'index.html'), 'utf8'); +if (!root.includes('http-equiv="refresh"') || !root.includes('docs/')) + failures.push('Missing root handbook redirect'); +const pagefindDir = path.join(distDir, 'pagefind'); +if (!(await exists(path.join(pagefindDir, 'pagefind-ui.js')))) + failures.push('Missing Pagefind UI runtime'); +if (!(await exists(path.join(pagefindDir, 'pagefind.js')))) + failures.push('Missing Pagefind search runtime'); +if ((await readdir(pagefindDir).catch(() => [])).length < 3) + failures.push('Pagefind index is incomplete'); + +const htmlFiles = []; +async function collectHtml(directory) { + for (const entry of await readdir(directory, { withFileTypes: true })) { + const target = path.join(directory, entry.name); + if (entry.isDirectory()) await collectHtml(target); + else if (entry.name === 'index.html') htmlFiles.push(target); + } +} +await collectHtml(path.join(distDir, 'docs')); +if (htmlFiles.length !== uniqueRoutes.length) + failures.push( + `Expected ${uniqueRoutes.length} route pages, found ${htmlFiles.length}`, + ); + +if (failures.length > 0) { + console.error(failures.join('\n')); + process.exit(1); +} +console.log( + `Validated ${uniqueRoutes.length} prerendered handbook routes and Pagefind output.`, +); diff --git a/apps/docs/src/DocsApp.tsx b/apps/docs/src/DocsApp.tsx new file mode 100644 index 000000000..70ed882fb --- /dev/null +++ b/apps/docs/src/DocsApp.tsx @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Suspense, useEffect } from 'react'; +import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; + +import { DocsLayout } from './DocsLayout'; +import { allRoutes } from './navigation'; +import { NotFound } from './NotFound'; + +export function DocsApp() { + return ( + <DocsLayout> + <RouteTitle /> + <Suspense fallback={<DocsFallback />}> + <Routes> + <Route path="/" element={<Navigate to="/docs" replace />} /> + {allRoutes.map(({ to, Component }) => ( + <Route key={to} path={to} element={<Component />} /> + ))} + <Route path="*" element={<NotFound />} /> + </Routes> + </Suspense> + </DocsLayout> + ); +} + +function RouteTitle() { + const { pathname } = useLocation(); + + useEffect(() => { + const route = allRoutes.find((candidate) => candidate.to === pathname); + document.title = route + ? `${route.label} · Huabu Handbook` + : 'Huabu Handbook'; + }, [pathname]); + + return null; +} + +function DocsFallback() { + return ( + <div + data-docs-loading + className="flex items-center gap-3 px-8 py-12 text-sm text-gray-500" + > + <span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-transparent" /> + Loading… + </div> + ); +} diff --git a/apps/docs/src/DocsLayout.tsx b/apps/docs/src/DocsLayout.tsx new file mode 100644 index 000000000..b3d6fdd5f --- /dev/null +++ b/apps/docs/src/DocsLayout.tsx @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Link, NavLink } from 'react-router-dom'; + +import { withBasePath } from './basePath'; +import { cn } from './components/cn'; +import { Search } from './components/Search'; +import { groups, pinnedItems, type DocsItem } from './navigation'; + +import type { CSSProperties, ReactNode } from 'react'; + +/** + * Shell for every page on the standalone handbook site. + * + * Layout: + * - Floating sidebar pinned to the viewport on the left (logo + + * pinned shortcuts always visible; groups scroll below). + * - Main content area on the right owns the article content. + * + * Horizontal spacing (kept consistent across every section page): + * - Sidebar occupies `left-4 + w-60` = 256px from the viewport's + * left edge to its right edge. + * - `--docs-gutter` (= 1/5 of the space remaining after the sidebar) + * is applied as both the left padding (sidebar → content) and the + * right padding (toc → viewport edge). + * - Content-to-TOC gap is `4vw` (set inside `PageLayout`). + * + * The module has no product-application dependency. + */ +export function DocsLayout({ children }: { children: ReactNode }) { + return ( + <div + className="relative h-full w-full overflow-y-auto bg-white" + style={ + { + // Single source of truth for the side gutters. Tweak here + // and both PageLayout's padding and the TOC offset move + // together. + '--docs-sidebar-right-edge': '256px', + '--docs-gutter': + 'calc((100vw - var(--docs-sidebar-right-edge)) / 10)', + } as CSSProperties + } + > + <DocsSidebar /> + <main + className="min-h-full" + style={{ + paddingLeft: + 'calc(var(--docs-sidebar-right-edge) + var(--docs-gutter))', + paddingRight: 'var(--docs-gutter)', + }} + > + {children} + </main> + </div> + ); +} + +const sidebarLinkClass = ({ isActive }: { isActive: boolean }) => + cn( + 'block rounded-md px-3 py-1.5 text-[13.5px] transition-colors', + isActive + ? 'bg-gray-100 font-medium text-gray-900' + : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900', + ); + +function DocsSidebar() { + return ( + <aside className="fixed top-4 bottom-4 left-4 z-20 flex w-60 flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-[0_2px_12px_rgba(0,0,0,0.04)]"> + {/* Pinned header: logo + brand + the two universal entry points + (Overview / Quick Start). Stays put when the lower list + scrolls. */} + <div className="shrink-0 border-b border-gray-100 px-4 pt-4 pb-3"> + <Link + to="/docs" + className="mb-3 flex items-center gap-2 text-[15px] font-semibold text-gray-900" + > + <img src={withBasePath('favicon.svg')} alt="" className="h-5 w-5" /> + <span>Huabu Handbook</span> + </Link> + <Search /> + <ul className="space-y-0.5"> + {pinnedItems.map((item) => ( + <li key={item.to}> + <NavLink to={item.to} end className={sidebarLinkClass}> + {item.label} + </NavLink> + </li> + ))} + </ul> + </div> + + {/* Scrollable group list: flat (no collapse), each group has a + bold title above its items. */} + <nav className="min-h-0 flex-1 overflow-y-auto px-4 py-4"> + <ul className="space-y-5"> + {groups.map((group) => ( + <li key={group.label}> + <div className="mb-1.5 px-3 text-[13px] font-semibold tracking-wide text-gray-700 uppercase"> + {group.label} + </div> + <ul className="space-y-0.5"> + {group.items.map((item: DocsItem) => ( + <li key={item.to}> + <NavLink to={item.to} end className={sidebarLinkClass}> + {item.label} + </NavLink> + </li> + ))} + </ul> + </li> + ))} + </ul> + </nav> + </aside> + ); +} diff --git a/apps/docs/src/NotFound.tsx b/apps/docs/src/NotFound.tsx new file mode 100644 index 000000000..e642c42a2 --- /dev/null +++ b/apps/docs/src/NotFound.tsx @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Link } from 'react-router-dom'; + +export function NotFound() { + return ( + <article + data-pagefind-ignore + className="mx-auto max-w-2xl px-8 py-24 text-center" + > + <h1 className="text-3xl font-semibold text-gray-900">Page not found</h1> + <p className="mt-4 text-gray-600"> + The requested handbook page does not exist. + </p> + <Link + className="mt-6 inline-block font-medium text-gray-900 underline" + to="/docs" + > + Return to the handbook overview + </Link> + </article> + ); +} diff --git a/apps/docs/src/basePath.test.ts b/apps/docs/src/basePath.test.ts new file mode 100644 index 000000000..705412d21 --- /dev/null +++ b/apps/docs/src/basePath.test.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { describe, expect, it } from 'vitest'; + +import { resolveWithBasePath } from './basePath'; +import { normalizeBasePath } from './normalizeBasePath'; + +describe('normalizeBasePath', () => { + it.each([ + [undefined, '/'], + ['', '/'], + ['/', '/'], + ['ExampleProject', '/ExampleProject/'], + ['/ExampleProject/', '/ExampleProject/'], + ])('normalizes %s', (input, expected) => { + expect(normalizeBasePath(input)).toBe(expected); + }); +}); + +describe('resolveWithBasePath', () => { + it('prefixes a root-relative path with the configured base', () => { + expect(resolveWithBasePath('/ExampleProject/', '/docs/quickstart')).toBe( + '/ExampleProject/docs/quickstart', + ); + }); + + it('does not prefix an already resolved path again', () => { + expect( + resolveWithBasePath( + '/ExampleProject/', + '/ExampleProject/docs/quickstart', + ), + ).toBe('/ExampleProject/docs/quickstart'); + }); + + it('keeps root deployments root-relative', () => { + expect(resolveWithBasePath('/', 'docs/quickstart')).toBe( + '/docs/quickstart', + ); + }); +}); diff --git a/apps/docs/src/basePath.ts b/apps/docs/src/basePath.ts new file mode 100644 index 000000000..7f697f998 --- /dev/null +++ b/apps/docs/src/basePath.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export const docsBasePath = import.meta.env.BASE_URL; + +export const routerBasename = + docsBasePath === '/' ? '/' : docsBasePath.replace(/\/$/, ''); + +export function resolveWithBasePath( + basePath: string, + pathname: string, +): string { + const normalizedPathname = `/${pathname.replace(/^\/+/, '')}`; + if (basePath === '/') return normalizedPathname; + + const baseWithoutTrailingSlash = basePath.replace(/\/$/, ''); + if ( + normalizedPathname === baseWithoutTrailingSlash || + normalizedPathname.startsWith(basePath) + ) { + return normalizedPathname; + } + + return `${baseWithoutTrailingSlash}${normalizedPathname}`; +} + +export function withBasePath(pathname: string): string { + return resolveWithBasePath(docsBasePath, pathname); +} diff --git a/apps/docs/src/components/Callout.tsx b/apps/docs/src/components/Callout.tsx new file mode 100644 index 000000000..cb4497d40 --- /dev/null +++ b/apps/docs/src/components/Callout.tsx @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Long-format callout strip — used for tips, warnings and asides + * inside the body of a section. Spans the full content width. + */ + +import { AlertTriangle, Info, Lightbulb } from 'lucide-react'; + +import { cn } from './cn'; + +import type { ReactNode } from 'react'; + +type CalloutTone = 'info' | 'tip' | 'warning'; + +const toneStyles: Record<CalloutTone, { wrapper: string; icon: typeof Info }> = + { + info: { + wrapper: 'border-blue-200 bg-blue-50/60 text-blue-900', + icon: Info, + }, + tip: { + wrapper: 'border-emerald-200 bg-emerald-50/60 text-emerald-900', + icon: Lightbulb, + }, + warning: { + wrapper: 'border-amber-200 bg-amber-50/60 text-amber-900', + icon: AlertTriangle, + }, + }; + +export function Callout({ + tone = 'info', + title, + children, +}: { + tone?: CalloutTone; + title?: string; + children: ReactNode; +}) { + const { wrapper, icon: Icon } = toneStyles[tone]; + return ( + <div + className={cn( + 'flex items-start gap-3 rounded-xl border px-4 py-3 text-[14px] leading-relaxed', + wrapper, + )} + > + <Icon className="mt-0.5 h-4 w-4 shrink-0 opacity-80" /> + <div className="min-w-0 flex-1"> + {title && ( + <div className="mb-0.5 text-[13px] font-semibold">{title}</div> + )} + <div>{children}</div> + </div> + </div> + ); +} diff --git a/apps/docs/src/components/Card.tsx b/apps/docs/src/components/Card.tsx new file mode 100644 index 000000000..899009208 --- /dev/null +++ b/apps/docs/src/components/Card.tsx @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Navigation card — a one-third-width hover card used on hub pages + * (e.g. Overview) to surface key sections. + * + * Render a `<CardGrid>` and place 1–3 `<NavCard>`s inside per row. + */ + +import { ArrowUpRight } from 'lucide-react'; +import { useRef } from 'react'; +import { Link } from 'react-router-dom'; + +import { withBasePath } from '../basePath'; +import { cn } from './cn'; + +import type { LucideIcon } from 'lucide-react'; +import type { MouseEvent, ReactNode } from 'react'; + +export function CardGrid({ children }: { children: ReactNode }) { + return ( + <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> + {children} + </div> + ); +} + +type NavCardProps = { + to: string; + icon?: LucideIcon; + title: string; + description?: ReactNode; + /** Optional small label like "Core" / "Reference". */ + eyebrow?: string; + /** Optional banner image rendered above the text. */ + image?: string; + /** Alt text for `image`. Falls back to `title`. */ + imageAlt?: string; + className?: string; +}; + +/** + * A clickable card with a subtle hover lift. Designed to occupy + * 1/3 of the content row inside a `<CardGrid>`. + */ +export function NavCard({ + to, + icon: Icon, + title, + description, + eyebrow, + image, + imageAlt, + className, +}: NavCardProps) { + const isInternal = to.startsWith('/') && !to.startsWith('//'); + const bannerRef = useRef<HTMLDivElement | null>(null); + + const handleMove = (event: MouseEvent<HTMLDivElement>) => { + const el = bannerRef.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + const x = ((event.clientX - rect.left) / rect.width) * 100; + const y = ((event.clientY - rect.top) / rect.height) * 100; + el.style.setProperty('--mx', `${x}%`); + el.style.setProperty('--my', `${y}%`); + }; + + const banner = ( + <div + ref={bannerRef} + onMouseMove={handleMove} + className="relative aspect-[16/9] w-full overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100" + style={{ '--mx': '50%', '--my': '50%' } as React.CSSProperties} + > + {image ? ( + <img + src={image.startsWith('/') ? withBasePath(image) : image} + alt={imageAlt ?? title} + className="h-full w-full object-cover transition-transform duration-500 ease-out group-hover:scale-105" + /> + ) : ( + Icon && ( + <div className="relative z-10 flex h-full w-full items-center justify-center text-gray-400 transition-transform duration-500 ease-out group-hover:scale-105"> + <Icon className="h-10 w-10" strokeWidth={1.25} /> + </div> + ) + )} + {/* Soft blue/purple wash that fills the whole banner on hover. + The two cursor-driven CSS vars (--mx/--my) only shift the + colour balance slightly so the wash feels alive without + becoming a tight spotlight. */} + <div + aria-hidden + className="pointer-events-none absolute inset-0 opacity-0 blur-2xl transition-opacity duration-500 ease-out group-hover:opacity-100" + style={{ + background: + 'radial-gradient(120% 120% at var(--mx) var(--my), rgba(176, 200, 255, 0.45), rgba(208, 188, 255, 0.3) 55%, rgba(230, 215, 255, 0.18) 100%)', + }} + /> + </div> + ); + + const inner = ( + <> + {banner} + <div className="p-4"> + <div className="flex items-start justify-between gap-2"> + {eyebrow && ( + <span className="text-[10.5px] font-medium tracking-wide text-gray-500 uppercase"> + {eyebrow} + </span> + )} + <ArrowUpRight className="ml-auto h-4 w-4 text-gray-400 transition-transform duration-300 ease-out group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-gray-700" /> + </div> + <div className="mt-1.5 text-[14px] font-semibold text-gray-900"> + {title} + </div> + {description && ( + <div className="mt-1 line-clamp-2 text-[12.5px] leading-relaxed text-gray-600"> + {description} + </div> + )} + </div> + </> + ); + + const classes = cn( + 'group relative block overflow-hidden rounded-2xl border border-gray-200 bg-white transition-all duration-300 ease-out hover:-translate-y-0.5 hover:border-gray-300 hover:shadow-md', + className, + ); + + return isInternal ? ( + <Link to={to} className={classes}> + {inner} + </Link> + ) : ( + <a href={to} target="_blank" rel="noopener noreferrer" className={classes}> + {inner} + </a> + ); +} diff --git a/apps/docs/src/components/Code.tsx b/apps/docs/src/components/Code.tsx new file mode 100644 index 000000000..fb1eba433 --- /dev/null +++ b/apps/docs/src/components/Code.tsx @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Inline `<Code>` for short identifiers, and block `<CodeBlock>` for + * snippets. Both are stylistic only — no syntax highlighting — which + * keeps the docs module dependency-free. + */ + +import { cn } from './cn'; + +import type { ReactNode } from 'react'; + +export function Code({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + <code + className={cn( + 'rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[0.85em] text-gray-800', + className, + )} + > + {children} + </code> + ); +} + +export function CodeBlock({ + children, + language, + className, +}: { + children: ReactNode; + language?: string; + className?: string; +}) { + return ( + <div + className={cn( + 'group relative overflow-hidden rounded-lg border border-gray-200 bg-gray-50', + className, + )} + > + {language && ( + <div className="border-b border-gray-200 bg-white px-4 py-1.5 font-mono text-[11px] tracking-wide text-gray-500 uppercase"> + {language} + </div> + )} + <pre className="overflow-x-auto px-4 py-3 font-mono text-[13px] leading-relaxed text-gray-800"> + <code>{children}</code> + </pre> + </div> + ); +} + +export function Kbd({ children }: { children: ReactNode }) { + return ( + <kbd className="inline-flex h-5 min-w-5 items-center justify-center rounded border border-gray-300 bg-white px-1.5 font-mono text-[11px] text-gray-700 shadow-[0_1px_0_rgba(0,0,0,0.04)]"> + {children} + </kbd> + ); +} diff --git a/apps/docs/src/components/Heading.tsx b/apps/docs/src/components/Heading.tsx new file mode 100644 index 000000000..fa6f59a67 --- /dev/null +++ b/apps/docs/src/components/Heading.tsx @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Typography primitives. + * + * Every heading auto-generates an `id` from its text so the + * right-side table-of-contents links resolve via plain `#anchor` + * navigation — no extra wiring required at the call site. + */ + +import { cn } from './cn'; + +import type { ReactNode } from 'react'; + +export function slugify(text: string): string { + return text + .toLowerCase() + .trim() + .replace(/[^\w\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); +} + +function headingId(children: ReactNode): string | undefined { + return typeof children === 'string' ? slugify(children) : undefined; +} + +export function H1({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + <h1 + id={headingId(children)} + className={cn( + 'text-3xl font-semibold tracking-tight text-gray-900', + className, + )} + > + {children} + </h1> + ); +} + +export function H2({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + <h2 + id={headingId(children)} + className={cn( + 'mt-10 scroll-mt-8 text-xl font-semibold text-gray-900', + className, + )} + > + {children} + </h2> + ); +} + +export function H3({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + <h3 + id={headingId(children)} + className={cn( + 'mt-6 scroll-mt-8 text-base font-semibold text-gray-900', + className, + )} + > + {children} + </h3> + ); +} + +export function P({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + <p className={cn('text-[15px] leading-relaxed text-gray-700', className)}> + {children} + </p> + ); +} diff --git a/apps/docs/src/components/Image.tsx b/apps/docs/src/components/Image.tsx new file mode 100644 index 000000000..d9f794d49 --- /dev/null +++ b/apps/docs/src/components/Image.tsx @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Image / GIF embed with optional caption. + * + * Accepts the same `src` types as a plain `<img>` (URL string or a + * bundler-imported asset) and wraps the result in a soft, bordered + * frame so screenshots feel intentional rather than dropped in. + */ + +import { cn } from './cn'; +import { withBasePath } from '../basePath'; + +type DocImageProps = { + src: string; + alt: string; + caption?: string; + className?: string; +}; + +export function DocImage({ src, alt, caption, className }: DocImageProps) { + const resolvedSrc = src.startsWith('/') ? withBasePath(src) : src; + return ( + <figure + className={cn( + 'overflow-hidden rounded-xl border border-gray-200 bg-white', + className, + )} + > + <img src={resolvedSrc} alt={alt} className="block h-auto w-full" /> + {caption && ( + <figcaption className="border-t border-gray-200 bg-gray-50 px-4 py-2 text-[12px] text-gray-600"> + {caption} + </figcaption> + )} + </figure> + ); +} diff --git a/apps/docs/src/components/Link.tsx b/apps/docs/src/components/Link.tsx new file mode 100644 index 000000000..89f77e9f0 --- /dev/null +++ b/apps/docs/src/components/Link.tsx @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Styled link. + * + * - Internal links (starting with `/`) use react-router's `Link` so + * they don't trigger a full page reload. + * - Same-page anchors (starting with `#`) use a regular anchor. + * - External links open in a new tab with `rel="noopener"`. + */ + +import { Link as RouterLink } from 'react-router-dom'; + +import { cn } from './cn'; + +import type { AnchorHTMLAttributes, ReactNode } from 'react'; + +type DocLinkProps = { + href: string; + children: ReactNode; + className?: string; +} & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'children'>; + +export function DocLink({ href, children, className, ...rest }: DocLinkProps) { + const isInternal = href.startsWith('/') && !href.startsWith('//'); + const isSamePageAnchor = href.startsWith('#'); + const classes = cn( + 'font-medium text-gray-900 underline decoration-gray-400 underline-offset-2 transition-colors hover:decoration-gray-900', + className, + ); + + if (isInternal) { + return ( + <RouterLink to={href} className={classes}> + {children} + </RouterLink> + ); + } + + if (isSamePageAnchor) { + return ( + <a href={href} className={classes} {...rest}> + {children} + </a> + ); + } + + return ( + <a + href={href} + target="_blank" + rel="noopener noreferrer" + className={classes} + {...rest} + > + {children} + </a> + ); +} diff --git a/apps/docs/src/components/PageLayout.tsx b/apps/docs/src/components/PageLayout.tsx new file mode 100644 index 000000000..36bf5b075 --- /dev/null +++ b/apps/docs/src/components/PageLayout.tsx @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Per-section page chrome. + * + * Handles the three pieces every page needs: + * - The page title block (with optional description). + * - The main scrollable content column. + * - An optional right-side table of contents that sticks while + * scrolling. + */ + +import { H1, P } from './Heading'; +import { Toc, type TocEntry } from './Toc'; + +import type { ReactNode } from 'react'; + +type PageLayoutProps = { + title: string; + description?: ReactNode; + /** Right-side anchor list. Omit to render full-width. */ + toc?: TocEntry[]; + children: ReactNode; +}; + +export function PageLayout({ + title, + description, + toc, + children, +}: PageLayoutProps) { + return ( + // Horizontal padding comes from `DocsLayout`'s gutter variables + // so every page lines up identically against the sidebar and + // viewport edge. The gap between the content column and the + // sticky TOC is fixed at 4vw per the docs spacing spec. + <div className="flex gap-x-[4vw] py-12"> + <article data-pagefind-body className="min-w-0 flex-1 space-y-6"> + <header className="space-y-3"> + <H1>{title}</H1> + {description && <P className="text-base">{description}</P>} + </header> + <div className="space-y-5">{children}</div> + </article> + + {toc && toc.length > 0 && ( + <aside className="sticky top-12 hidden h-fit w-56 shrink-0 self-start rounded-xl bg-gray-50 p-4 lg:block"> + <Toc entries={toc} /> + </aside> + )} + </div> + ); +} diff --git a/apps/docs/src/components/Search.tsx b/apps/docs/src/components/Search.tsx new file mode 100644 index 000000000..c75d5e2c7 --- /dev/null +++ b/apps/docs/src/components/Search.tsx @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Search as SearchIcon } from 'lucide-react'; +import { useCallback, useEffect, useId, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; + +import { withBasePath } from '../basePath'; +import { Kbd } from './Code'; +import { Shortcut } from './Shortcut'; + +import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; + +export function Search() { + const id = useId().replace(/:/g, ''); + const triggerRef = useRef<HTMLButtonElement>(null); + const closeRef = useRef<HTMLButtonElement>(null); + const hasBeenOpen = useRef(false); + const isInitializing = useRef(false); + const isReady = useRef(false); + const [hasOpened, setHasOpened] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [error, setError] = useState<string | null>(null); + + const focusSearchInput = useCallback(() => { + document + .querySelector<HTMLInputElement>(`#${id} .pagefind-ui__search-input`) + ?.focus(); + }, [id]); + + const initialize = useCallback(async () => { + if (import.meta.env.DEV || isInitializing.current) return; + if (isReady.current) { + focusSearchInput(); + return; + } + isInitializing.current = true; + try { + const { PagefindUI } = await import('@pagefind/default-ui'); + new PagefindUI({ + element: `#${id}`, + bundlePath: withBasePath('pagefind/'), + baseUrl: withBasePath(''), + showSubResults: true, + pageSize: 10, + }); + isReady.current = true; + focusSearchInput(); + } catch { + setError('Search could not be loaded. Please try again.'); + } finally { + isInitializing.current = false; + } + }, [focusSearchInput, id]); + + const openSearch = useCallback(() => { + setHasOpened(true); + setIsOpen(true); + }, []); + + const closeSearch = useCallback(() => { + setIsOpen(false); + }, []); + + useEffect(() => { + if (!isOpen) return; + + hasBeenOpen.current = true; + const previousOverflow = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + closeRef.current?.focus(); + void initialize(); + + return () => { + document.body.style.overflow = previousOverflow; + }; + }, [initialize, isOpen]); + + useEffect(() => { + if (isOpen || !hasBeenOpen.current) return; + triggerRef.current?.focus(); + }, [isOpen]); + + useEffect(() => { + const handleShortcut = (event: KeyboardEvent) => { + if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'k') { + event.preventDefault(); + openSearch(); + } else if (event.key === 'Escape' && isOpen) { + event.preventDefault(); + closeSearch(); + } + }; + + document.addEventListener('keydown', handleShortcut); + return () => document.removeEventListener('keydown', handleShortcut); + }, [closeSearch, isOpen, openSearch]); + + const trapFocus = (event: ReactKeyboardEvent<HTMLDivElement>) => { + if (event.key !== 'Tab') return; + const focusable = Array.from( + event.currentTarget.querySelectorAll<HTMLElement>( + 'button:not([disabled]), input:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])', + ), + ).filter((element) => !element.hidden); + if (focusable.length === 0) return; + + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last?.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first?.focus(); + } + }; + + return ( + <> + <button + ref={triggerRef} + type="button" + onClick={openSearch} + className="mb-2 flex w-full items-center gap-2 rounded-lg border border-gray-200 px-3 py-2 text-left text-sm text-gray-600 transition-colors hover:border-gray-300 hover:bg-gray-50" + aria-expanded={isOpen} + aria-haspopup="dialog" + > + <SearchIcon className="h-4 w-4" /> + <span className="flex-1">Search</span> + <Shortcut combo="mod+k" /> + </button> + {hasOpened && + createPortal( + <div + className={ + isOpen + ? 'fixed inset-0 z-[100] flex items-start justify-center bg-black/45 px-4 pt-[10vh] backdrop-blur-[2px]' + : 'hidden' + } + onMouseDown={(event) => { + if (event.target === event.currentTarget) closeSearch(); + }} + > + <div + role="dialog" + aria-modal="true" + aria-labelledby={`${id}-title`} + className="docs-search-modal relative flex max-h-[78vh] w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-2xl" + onKeyDown={trapFocus} + > + <h2 id={`${id}-title`} className="sr-only"> + Search + </h2> + <div className="min-h-0 overflow-x-hidden overflow-y-auto p-3 sm:p-4"> + {import.meta.env.DEV ? ( + <div className="flex min-h-32 items-center justify-center rounded-xl border border-dashed border-gray-200 px-6 text-center text-sm text-gray-500"> + Search is available in a built preview. + </div> + ) : error ? ( + <div className="flex min-h-32 items-center justify-center px-6 text-center text-sm text-red-600"> + {error} + </div> + ) : ( + <div id={id} /> + )} + </div> + <div className="flex items-center justify-end border-t border-gray-100 bg-gray-50 px-4 py-1.5 text-xs text-gray-500"> + <button + ref={closeRef} + type="button" + onClick={closeSearch} + className="inline-flex items-center gap-1.5 rounded-md px-2 py-1 leading-none transition-colors hover:bg-gray-200 hover:text-gray-900" + > + <Kbd>Esc</Kbd> + Close + </button> + </div> + </div> + </div>, + document.body, + )} + </> + ); +} diff --git a/apps/docs/src/components/Shortcut.tsx b/apps/docs/src/components/Shortcut.tsx new file mode 100644 index 000000000..4dda96ce2 --- /dev/null +++ b/apps/docs/src/components/Shortcut.tsx @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * OS-aware keyboard-shortcut chip. Renders a shortcut in the same boxed + * {@link Kbd} style used across the docs, picking the right notation for the + * reader's platform — `⌘,` / `⇧⌘Z` on macOS, `Ctrl+,` / `Ctrl+Shift+Z` + * elsewhere — so every surface (body copy, the Search trigger) shows one + * consistent representation instead of the old hand-written + * `⌘, (Ctrl+, on Windows)`. + * + * The pages are prerendered in Node and hydrated in the browser, so platform + * detection must run *after* mount to avoid a hydration mismatch: the first + * client render matches the server (non-mac), then {@link useIsMac} flips it. + */ + +import { useEffect, useState } from 'react'; + +import { Kbd } from './Code'; + +/** Canonical modifier tokens accepted in a {@link Shortcut} `combo`. */ +type Modifier = 'mod' | 'shift' | 'alt' | 'meta'; + +const MAC_SYMBOL: Record<Modifier, string> = { + mod: '⌘', + shift: '⇧', + alt: '⌥', + meta: '⌘', +}; + +const OTHER_LABEL: Record<Modifier, string> = { + mod: 'Ctrl', + shift: 'Shift', + alt: 'Alt', + meta: 'Win', +}; + +const isModifier = (part: string): part is Modifier => part in MAC_SYMBOL; + +/** + * `true` when the reader is on macOS. Defaults to `false` during SSR and the + * first hydration render, then resolves on the client to keep markup stable. + */ +function useIsMac(): boolean { + const [isMac, setIsMac] = useState(false); + useEffect(() => { + const ua = navigator.userAgent; + setIsMac(/Mac|iPhone|iPad|iPod/.test(navigator.platform || ua)); + }, []); + return isMac; +} + +/** + * Render `combo` (a `+`-separated string like `mod+,` or `mod+shift+z`) as a + * platform-appropriate shortcut chip. Modifiers are mapped per OS; any other + * token is treated as a literal key (upper-cased when a single letter). macOS + * joins parts with no separator (`⇧⌘Z`); other platforms use `+` + * (`Ctrl+Shift+Z`). + */ +export function Shortcut({ combo }: { combo: string }) { + const isMac = useIsMac(); + + const parts = combo.split('+').map((raw) => { + const part = raw.trim(); + if (isModifier(part)) return isMac ? MAC_SYMBOL[part] : OTHER_LABEL[part]; + return part.length === 1 ? part.toUpperCase() : part; + }); + + return <Kbd>{parts.join(isMac ? '' : '+')}</Kbd>; +} diff --git a/apps/docs/src/components/Table.tsx b/apps/docs/src/components/Table.tsx new file mode 100644 index 000000000..87fff11b9 --- /dev/null +++ b/apps/docs/src/components/Table.tsx @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Small data table component for reference-style pages. + * + * Designed for short, two- or three-column lookup tables (shortcuts, + * settings, comparisons). Long-form prose still belongs in `P`. + */ + +import { cn } from './cn'; + +import type { ReactNode } from 'react'; + +export function Table({ + headers, + rows, + className, +}: { + headers: string[]; + rows: ReactNode[][]; + className?: string; +}) { + return ( + <div + className={cn( + 'overflow-hidden rounded-xl border border-gray-200', + className, + )} + > + <table className="w-full border-collapse text-[13.5px]"> + <thead> + <tr className="bg-gray-50 text-left text-[12px] font-semibold tracking-wide text-gray-600 uppercase"> + {headers.map((header) => ( + <th key={header} className="px-4 py-2.5"> + {header} + </th> + ))} + </tr> + </thead> + <tbody className="divide-y divide-gray-100 bg-white text-gray-700"> + {rows.map((row, rowIndex) => ( + <tr key={rowIndex} className="align-top"> + {row.map((cell, cellIndex) => ( + <td key={cellIndex} className="px-4 py-2.5"> + {cell} + </td> + ))} + </tr> + ))} + </tbody> + </table> + </div> + ); +} diff --git a/apps/docs/src/components/Toc.tsx b/apps/docs/src/components/Toc.tsx new file mode 100644 index 000000000..2cfa90530 --- /dev/null +++ b/apps/docs/src/components/Toc.tsx @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Right-side table of contents for a section page. + * + * The page passes a list of `{ id, label, level }` entries that map + * to heading anchors emitted by the typography components in + * `Heading.tsx`. Levels 2 and 3 are indented to mirror the document + * hierarchy without becoming visually noisy. + */ + +import { cn } from './cn'; + +export type TocEntry = { + /** The `id` of the heading to jump to. */ + id: string; + /** Sidebar label — usually the heading text. */ + label: string; + /** 2 for `<H2>`, 3 for `<H3>`. Defaults to 2. */ + level?: 2 | 3; +}; + +export function Toc({ entries }: { entries: TocEntry[] }) { + if (!entries.length) return null; + + return ( + <nav aria-label="On this page" className="text-[13px]"> + <div className="mb-3 text-[11px] font-semibold tracking-wide text-gray-500 uppercase"> + On this page + </div> + <ul className="space-y-1.5 border-l border-gray-200"> + {entries.map((entry) => ( + <li key={entry.id}> + <a + href={`#${entry.id}`} + className={cn( + '-ml-px block border-l border-transparent py-0.5 pl-3 text-gray-600 transition-colors hover:border-gray-400 hover:text-gray-900', + entry.level === 3 && 'pl-6', + )} + > + {entry.label} + </a> + </li> + ))} + </ul> + </nav> + ); +} diff --git a/apps/docs/src/components/cn.ts b/apps/docs/src/components/cn.ts new file mode 100644 index 000000000..6c5539ff2 --- /dev/null +++ b/apps/docs/src/components/cn.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Local class-name combinator for the docs module. + * + * The docs folder is intentionally decoupled from the rest of the + * web app so it can be lifted out into a standalone site later. We + * keep our own `cn` here instead of reaching into + * `apps/web/src/components/Common/cn`. + */ +export function cn(...args: (string | false | null | undefined)[]): string { + return args.filter(Boolean).join(' '); +} diff --git a/apps/docs/src/components/index.ts b/apps/docs/src/components/index.ts new file mode 100644 index 000000000..05be8b5d9 --- /dev/null +++ b/apps/docs/src/components/index.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Barrel export for the docs component library. + * + * Every section page imports from `'../components'` (or + * `'../../components'` for nested folders) instead of reaching into + * individual files, so future renames don't ripple across the docs. + */ + +export { Callout } from './Callout'; +export { CardGrid, NavCard } from './Card'; +export { Code, CodeBlock, Kbd } from './Code'; +export { H1, H2, H3, P, slugify } from './Heading'; +export { DocImage } from './Image'; +export { DocLink } from './Link'; +export { PageLayout } from './PageLayout'; +export { Shortcut } from './Shortcut'; +export { Table } from './Table'; +export { Toc, type TocEntry } from './Toc'; +export { cn } from './cn'; diff --git a/apps/docs/src/config/nodeIcons.ts b/apps/docs/src/config/nodeIcons.ts new file mode 100644 index 000000000..031b4b75a --- /dev/null +++ b/apps/docs/src/config/nodeIcons.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + BookOpen, + Clipboard, + Film, + Frame, + Globe, + Image as ImageIcon, + MessageCircleQuestionMark, + Mic, + Pencil, + Type, +} from 'lucide-react'; + +import type { LucideIcon } from 'lucide-react'; + +type HandbookNodeType = + | 'note' + | 'text' + | 'image' + | 'pdf' + | 'video' + | 'audio' + | 'web' + | 'frame' + | 'sketch' + | 'question'; + +export const NODE_ICON: Record<HandbookNodeType, LucideIcon> = { + note: Clipboard, + text: Type, + image: ImageIcon, + pdf: BookOpen, + video: Film, + audio: Mic, + web: Globe, + frame: Frame, + sketch: Pencil, + question: MessageCircleQuestionMark, +}; diff --git a/apps/docs/src/config/shortcuts.test.ts b/apps/docs/src/config/shortcuts.test.ts new file mode 100644 index 000000000..883f78d88 --- /dev/null +++ b/apps/docs/src/config/shortcuts.test.ts @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { describe, expect, it } from 'vitest'; + +import { keyboardShortcutSections } from './shortcuts'; + +describe('handbook keyboard shortcut catalog', () => { + it('documents the public shortcuts in canonical section order', () => { + expect(keyboardShortcutSections.map((section) => section.title)).toEqual([ + 'General', + 'Editing', + 'Layout', + 'Toolbar', + 'Layering & Grouping', + 'Drag and drop', + 'Search', + 'Help', + ]); + expect( + keyboardShortcutSections.flatMap((section) => section.items), + ).toHaveLength(31); + }); + + it('uses the toolbar bindings registered by the app', () => { + const toolbar = keyboardShortcutSections.find( + (section) => section.title === 'Toolbar', + ); + expect(toolbar?.items).toEqual( + expect.arrayContaining([ + { keys: '1', description: 'Note placement mode' }, + { keys: '2', description: 'Text placement mode' }, + { keys: '3', description: 'Frame placement mode' }, + { keys: '4', description: 'Sketch mode' }, + { keys: '5', description: 'Audio placement mode' }, + { keys: 'A', description: 'Create Agent Node' }, + ]), + ); + }); +}); diff --git a/apps/docs/src/config/shortcuts.ts b/apps/docs/src/config/shortcuts.ts new file mode 100644 index 000000000..3beef7734 --- /dev/null +++ b/apps/docs/src/config/shortcuts.ts @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export type ShortcutSection = { + title: string; + items: Array<{ keys: string; description: string }>; +}; + +/** Public English subset of the app's user-facing shortcut catalog. */ +export const keyboardShortcutSections: ShortcutSection[] = [ + { + title: 'General', + items: [ + { keys: 'Ctrl/Cmd+N', description: 'New Space (desktop app only)' }, + { + keys: 'Ctrl/Cmd+,', + description: 'Open settings (desktop app only)', + }, + ], + }, + { + title: 'Editing', + items: [ + { keys: 'Ctrl/Cmd+Z', description: 'Undo' }, + { keys: 'Ctrl/Cmd+Shift+Z', description: 'Redo' }, + { keys: 'Ctrl/Cmd+C', description: 'Copy selected nodes' }, + { + keys: 'Ctrl/Cmd+V', + description: 'Paste nodes, files, URLs, or plain text', + }, + { + keys: 'Delete / Backspace', + description: 'Delete selected nodes or edges', + }, + ], + }, + { + title: 'Layout', + items: [ + { keys: 'Ctrl/Cmd+Plus', description: 'Zoom in' }, + { keys: 'Ctrl/Cmd+Minus', description: 'Zoom out' }, + ], + }, + { + title: 'Toolbar', + items: [ + { keys: 'Space (hold)', description: 'Temporarily switch to pan tool' }, + { + keys: 'Space (hold while dragging)', + description: + 'Move a node without entering or leaving any frame (opt out of auto-reparent)', + }, + { keys: 'S', description: 'Select tool' }, + { keys: 'P', description: 'Pan tool' }, + { keys: 'L', description: 'Lasso tool' }, + { keys: '3', description: 'Frame placement mode' }, + { keys: '1', description: 'Note placement mode' }, + { keys: '2', description: 'Text placement mode' }, + { keys: '4', description: 'Sketch mode' }, + { keys: '5', description: 'Audio placement mode' }, + { keys: 'A', description: 'Create Agent Node' }, + ], + }, + { + title: 'Layering & Grouping', + items: [ + { keys: '[', description: 'Send selected nodes to back' }, + { keys: ']', description: 'Bring selected nodes to front' }, + { keys: 'Ctrl/Cmd+G', description: 'Group selected nodes into a frame' }, + ], + }, + { + title: 'Drag and drop', + items: [ + { + keys: 'Option (hold while dragging) / Ctrl (hold while dragging)', + description: + 'Copy a Note block to the Space instead of moving it (the default is move — the source loses the block)', + }, + ], + }, + { + title: 'Search', + items: [ + { + keys: 'Ctrl/Cmd+F', + description: + 'Search the Space (or find inside the active preview when focus is in an expanded node)', + }, + { keys: 'Enter', description: 'Jump to active result / next match' }, + { keys: 'Shift+Enter', description: 'Previous match (in preview)' }, + { + keys: '↑ / ↓', + description: 'Move between results (in Space overlay)', + }, + { keys: 'Esc', description: 'Close the search bar' }, + ], + }, + { + title: 'Help', + items: [ + { keys: '?', description: 'Show keyboard shortcuts' }, + { keys: 'Esc', description: 'Close the shortcuts dialog' }, + ], + }, +]; diff --git a/apps/docs/src/entry-server.tsx b/apps/docs/src/entry-server.tsx new file mode 100644 index 000000000..82b819400 --- /dev/null +++ b/apps/docs/src/entry-server.tsx @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { PassThrough } from 'node:stream'; + +import { renderToPipeableStream } from 'react-dom/server'; +import { StaticRouter } from 'react-router'; + +import { docsBasePath, routerBasename } from './basePath'; +import { DocsApp } from './DocsApp'; +import { routeManifest } from './navigation'; + +export { docsBasePath, routeManifest }; + +export function renderRoute(pathname: string): Promise<string> { + const location = `${routerBasename === '/' ? '' : routerBasename}${pathname}`; + + return new Promise((resolve, reject) => { + let settled = false; + const output = new PassThrough(); + const chunks: Buffer[] = []; + + output.on('data', (chunk: Buffer) => chunks.push(chunk)); + output.on('end', () => { + settled = true; + resolve(Buffer.concat(chunks).toString('utf8')); + }); + output.on('error', reject); + + const stream = renderToPipeableStream( + <StaticRouter basename={routerBasename} location={location}> + <DocsApp /> + </StaticRouter>, + { + onAllReady() { + stream.pipe(output); + }, + onShellError(error) { + reject(error); + }, + onError(error) { + if (!settled) console.error(error); + }, + }, + ); + }); +} diff --git a/apps/docs/src/index.css b/apps/docs/src/index.css new file mode 100644 index 000000000..002b3d1a7 --- /dev/null +++ b/apps/docs/src/index.css @@ -0,0 +1,177 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + */ + +@import 'tailwindcss'; +@import '@pagefind/default-ui/css/ui.css'; + +:root { + color: #191919; + background: #ffffff; + font-family: + Inter, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; + font-synthesis: none; + text-rendering: optimizeLegibility; + --fg-default: #191919; + --fg-muted: #6b6b6b; + --fg-subtle: #ababab; +} + +@theme inline { + --color-fg-default: var(--fg-default); + --color-fg-muted: var(--fg-muted); + --color-fg-subtle: var(--fg-subtle); +} + +html, +body, +#root { + min-height: 100%; + margin: 0; +} + +body { + min-width: 320px; +} + +* { + box-sizing: border-box; +} + +:focus-visible { + outline: 2px solid #191919; + outline-offset: 2px; +} + +.docs-search-modal { + --pagefind-ui-scale: 1; + --pagefind-ui-primary: #191919; + --pagefind-ui-text: #353535; + --pagefind-ui-background: #ffffff; + --pagefind-ui-border: #e5e7eb; + --pagefind-ui-tag: #f3f4f6; + --pagefind-ui-border-width: 1px; + --pagefind-ui-border-radius: 12px; + --pagefind-ui-font: inherit; +} + +.docs-search-modal .pagefind-ui__search-input { + height: 56px; + padding-right: 20px; + border-color: #d1d5db; + font-size: 18px; + box-shadow: 0 1px 2px rgb(0 0 0 / 4%); +} + +.docs-search-modal .pagefind-ui__form::before { + top: 28px; + transform: translateY(-50%); +} + +.docs-search-modal .pagefind-ui__search-clear { + display: none; +} + +.docs-search-modal .pagefind-ui__search-input:focus { + border-color: #9ca3af; + outline: none; + box-shadow: 0 1px 2px rgb(0 0 0 / 4%); +} + +.docs-search-modal .pagefind-ui__drawer { + padding-top: 4px; +} + +.docs-search-modal .pagefind-ui__message { + display: none; +} + +.docs-search-modal .pagefind-ui__results-area { + margin-top: 4px; +} + +.docs-search-modal .pagefind-ui__result.pagefind-ui__result { + gap: 0; + padding: 10px 12px; + border: 1px solid #e5e7eb; + border-radius: 10px; +} + +.docs-search-modal .pagefind-ui__result.pagefind-ui__result:last-of-type { + border-bottom: 1px solid #e5e7eb; +} + +@media (min-width: 640px) { + .docs-search-modal .pagefind-ui__results { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + } +} + +.docs-search-modal .pagefind-ui__result-thumb.pagefind-ui__result-thumb { + display: none; +} + +.docs-search-modal .pagefind-ui__result-inner.pagefind-ui__result-inner { + min-width: 0; + margin-top: 0; +} + +.docs-search-modal .pagefind-ui__result-link { + font-size: 17px; + line-height: 1.35; + overflow-wrap: anywhere; +} + +.docs-search-modal .pagefind-ui__result-excerpt.pagefind-ui__result-excerpt { + display: none; +} + +.docs-search-modal .pagefind-ui__result-nested.pagefind-ui__result-nested { + padding-left: 14px; +} + +.docs-search-modal + .pagefind-ui__result-nested.pagefind-ui__result-nested:first-of-type { + padding-top: 4px; +} + +.docs-search-modal + .pagefind-ui__result-nested + .pagefind-ui__result-link.pagefind-ui__result-link { + color: #6b7280; + font-size: 14px; + font-weight: 400; +} + +.docs-search-modal .pagefind-ui__button { + display: block; + width: fit-content; + height: 32px; + margin: 12px auto 2px; + padding: 0 14px; + border-color: #e5e7eb; + border-radius: 9999px; + background: #f9fafb; + color: #6b7280; + font-size: 13px; + font-weight: 500; + transition: + color 150ms ease, + background-color 150ms ease, + border-color 150ms ease; +} + +.docs-search-modal .pagefind-ui__button:hover { + border-color: #d1d5db; + background: #f3f4f6; + color: #191919; +} diff --git a/apps/docs/src/main.tsx b/apps/docs/src/main.tsx new file mode 100644 index 000000000..2d4548f5a --- /dev/null +++ b/apps/docs/src/main.tsx @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { StrictMode } from 'react'; +import { hydrateRoot } from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; + +import { routerBasename } from './basePath'; +import { DocsApp } from './DocsApp'; +import './index.css'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error('Docs root element was not found.'); +} + +hydrateRoot( + rootElement, + <StrictMode> + <BrowserRouter basename={routerBasename}> + <DocsApp /> + </BrowserRouter> + </StrictMode>, +); diff --git a/apps/docs/src/navigation.test.ts b/apps/docs/src/navigation.test.ts new file mode 100644 index 000000000..55c759e37 --- /dev/null +++ b/apps/docs/src/navigation.test.ts @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { describe, expect, it } from 'vitest'; + +import { allRoutes, groups, pinnedItems, routeManifest } from './navigation'; + +describe('handbook route registry', () => { + it('contains unique /docs routes', () => { + const paths = allRoutes.map((route) => route.to); + expect(new Set(paths).size).toBe(paths.length); + expect( + paths.every((path) => path === '/docs' || path.startsWith('/docs/')), + ).toBe(true); + }); + + it('provides metadata for every route', () => { + expect(routeManifest).toHaveLength(allRoutes.length); + expect( + routeManifest.every((route) => route.title && route.description), + ).toBe(true); + }); + + it('starts with the task-focused Quick Start', () => { + expect(pinnedItems).toEqual([]); + expect(groups[0]?.label).toBe('Getting Started'); + expect(groups[0]?.items.map(({ to, label }) => ({ to, label }))).toEqual([ + { to: '/docs', label: 'Quick Start' }, + ]); + expect(allRoutes.some((route) => route.to === '/docs/showcase')).toBe( + false, + ); + }); + + it('does not expose product-positioning Core pages', () => { + expect(groups.some((group) => group.label === 'Core')).toBe(false); + expect(allRoutes.some((route) => route.to.startsWith('/docs/core/'))).toBe( + false, + ); + }); + + it('presents Space fundamentals as one task-focused page', () => { + const spaces = groups.find((group) => group.label === 'Spaces'); + expect(spaces?.items.map(({ to, label }) => ({ to, label }))).toEqual([ + { to: '/docs/work-in-a-space', label: 'Work in a Space' }, + { to: '/docs/space/data-and-backup', label: 'Data & Files' }, + ]); + expect( + allRoutes.some( + (route) => + route.to.startsWith('/docs/concepts/') || + route.to.startsWith('/docs/nodes/'), + ), + ).toBe(false); + }); + + it('does not expose unreleased AI features', () => { + expect(allRoutes.some((route) => route.to === '/docs/ai/intent')).toBe( + false, + ); + }); + + it('presents everyday AI work in one task-focused page', () => { + const ai = groups.find((group) => group.label === 'AI'); + expect(ai?.items.map(({ to, label }) => ({ to, label }))).toEqual([ + { to: '/docs/work-with-ai', label: 'Work with AI' }, + { + to: '/docs/ai/models-and-capabilities', + label: 'Models & Capabilities', + }, + { to: '/docs/ai/external-agents', label: 'External Agents' }, + { to: '/docs/ai/memory-and-skills', label: 'Memory & Skills' }, + ]); + expect( + allRoutes.some((route) => + [ + '/docs/ai/chat-mode', + '/docs/ai/agent-mode', + '/docs/ai/question-mode', + '/docs/ai/digest', + '/docs/ai/models-and-credentials', + ].includes(route.to), + ), + ).toBe(false); + }); + + it('keeps shortcuts and issue reporting in Help', () => { + const help = groups.find((group) => group.label === 'Help'); + expect(help?.items.map(({ to, label }) => ({ to, label }))).toEqual([ + { to: '/docs/reference/shortcuts', label: 'Keyboard Shortcuts' }, + { to: '/docs/reference/issues', label: 'Report an Issue' }, + ]); + expect(groups.map((group) => group.label)).toEqual([ + 'Getting Started', + 'Spaces', + 'AI', + 'Help', + ]); + expect( + allRoutes.some((route) => + ['/docs/reference/settings', '/docs/reference/storage'].includes( + route.to, + ), + ), + ).toBe(false); + }); +}); diff --git a/apps/docs/src/navigation.ts b/apps/docs/src/navigation.ts new file mode 100644 index 000000000..1c6c76cc0 --- /dev/null +++ b/apps/docs/src/navigation.ts @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Single source of truth for the User Handbook. + * + * Authoring rules + * - `pinned` entries, when present, sit below the logo and search + * control in the static top portion of the sidebar and never scroll. + * - `groups` are rendered as bold-titled flat lists below the pinned + * block, all expanded all the time (no collapse). + * - Each item declares its full absolute `to` (under `/docs`). This + * lets a single sidebar group pull pages from any folder, so we + * can regroup the menu without breaking existing URLs. + * - Section modules are loaded with `React.lazy` so the handbook + * never ships in the main bundle. + */ + +import { lazy } from 'react'; + +import type { ComponentType } from 'react'; + +type SectionLoader = () => Promise<{ default: ComponentType }>; + +type RawItem = { + /** Absolute path under `/docs`, with leading slash. */ + to: string; + label: string; + description?: string; + load: SectionLoader; +}; + +type RawGroup = { + label: string; + items: RawItem[]; +}; + +const pinnedRaw: RawItem[] = []; + +const groupsRaw: RawGroup[] = [ + { + label: 'Getting Started', + items: [ + { + to: '/docs', + label: 'Quick Start', + description: + 'Install Huabu, create your first Space, add material, and complete your first AI conversation.', + load: () => import('./sections/QuickStart'), + }, + ], + }, + { + label: 'Spaces', + items: [ + { + to: '/docs/work-in-a-space', + label: 'Work in a Space', + description: + 'Create, arrange, connect, and inspect the materials and ideas in a Huabu Space.', + load: () => import('./sections/space/WorkInASpace'), + }, + { + to: '/docs/space/data-and-backup', + label: 'Data & Files', + description: + 'Understand how Huabu stores each Space as plain files inside your Home folder.', + load: () => import('./sections/space/DataAndBackup'), + }, + ], + }, + { + label: 'AI', + items: [ + { + to: '/docs/work-with-ai', + label: 'Work with AI', + description: + 'Choose Chat, Agent, or an Agent Node; provide context; and review AI changes.', + load: () => import('./sections/ai/WorkWithAI'), + }, + { + to: '/docs/ai/models-and-capabilities', + label: 'Models & Capabilities', + description: + 'Configure the Chat and Utility Models and optional web, image, and video capabilities.', + load: () => import('./sections/ai/ModelsAndCapabilities'), + }, + { + to: '/docs/ai/external-agents', + label: 'External Agents', + load: () => import('./sections/ai/ExternalAgents'), + }, + { + to: '/docs/ai/memory-and-skills', + label: 'Memory & Skills', + load: () => import('./sections/ai/MemoryAndSkills'), + }, + ], + }, + { + label: 'Help', + items: [ + { + to: '/docs/reference/shortcuts', + label: 'Keyboard Shortcuts', + load: () => import('./sections/reference/Shortcuts'), + }, + { + to: '/docs/reference/issues', + label: 'Report an Issue', + description: + 'Describe a problem, collect useful diagnostic information, and report it through GitHub Issues.', + load: () => import('./sections/reference/IssueReporting'), + }, + ], + }, +]; + +export type DocsItem = { + /** Absolute path including the `/docs` prefix. */ + to: string; + label: string; + description: string; + Component: ComponentType; +}; + +export type DocsGroup = { + label: string; + items: DocsItem[]; +}; + +function buildItem(item: RawItem): DocsItem { + return { + to: item.to, + label: item.label, + description: + item.description ?? `Learn about ${item.label.toLowerCase()} in Huabu.`, + Component: lazy(item.load), + }; +} + +export const pinnedItems: DocsItem[] = pinnedRaw.map(buildItem); + +export const groups: DocsGroup[] = groupsRaw.map((group) => ({ + label: group.label, + items: group.items.map(buildItem), +})); + +/** + * Flat list consumed by `<Routes>` in `DocsPage`. + */ +const allItems: DocsItem[] = [ + ...pinnedItems, + ...groups.flatMap((g) => g.items), +]; + +const seen = new Set<string>(); +export const allRoutes: DocsItem[] = allItems.filter((item) => { + if (seen.has(item.to)) { + throw new Error(`Duplicate handbook route: ${item.to}`); + } + seen.add(item.to); + return true; +}); + +export const routeManifest = allRoutes.map(({ to, label, description }) => ({ + path: to, + title: label, + description, +})); diff --git a/apps/docs/src/normalizeBasePath.ts b/apps/docs/src/normalizeBasePath.ts new file mode 100644 index 000000000..37211e57f --- /dev/null +++ b/apps/docs/src/normalizeBasePath.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export function normalizeBasePath(value: string | undefined): string { + const segments = (value ?? '/').split('/').filter(Boolean); + return segments.length === 0 ? '/' : `/${segments.join('/')}/`; +} diff --git a/apps/docs/src/sections/QuickStart.tsx b/apps/docs/src/sections/QuickStart.tsx new file mode 100644 index 000000000..2a51f1781 --- /dev/null +++ b/apps/docs/src/sections/QuickStart.tsx @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Bot, Settings as SettingsIcon } from 'lucide-react'; + +import { + Callout, + DocImage, + DocLink, + H2, + P, + PageLayout, + Shortcut, + type TocEntry, +} from '../components'; +import { NODE_ICON } from '../config/nodeIcons'; + +const toc: TocEntry[] = [ + { id: '1-download-and-install-huabu', label: 'Download and install Huabu' }, + { id: '2-choose-a-home-folder', label: 'Choose a Home Folder' }, + { id: '3-configure-models-and-keys', label: 'Configure models and keys' }, + { id: '4-create-a-space', label: 'Create a Space' }, + { id: '5-add-content-to-your-space', label: 'Add content to your Space' }, + { id: '6-think-and-work-with-ai', label: 'Think and work with AI' }, +]; + +export default function QuickStart() { + return ( + <PageLayout + title="Quick Start" + description="Install Huabu, create your first Space, add one piece of material, and have your first conversation with AI." + toc={toc} + > + <H2>1. Download and install Huabu</H2> + <P> + Download the latest version from{' '} + <DocLink href="https://github.com/microsoft/Huabu/releases/latest"> + GitHub Releases + </DocLink> + . Choose the <code>.dmg</code> package on macOS or the <code>.exe</code>{' '} + package on Windows, then install and launch Huabu. + </P> + + <H2>2. Choose a Home Folder</H2> + <P> + On first launch, select a local folder and open it as your{' '} + <strong>Home</strong>. Huabu stores your Spaces and their materials in + this folder so you can choose where your work lives. + </P> + + <H2>3. Configure models and keys</H2> + <P> + Open <strong>Settings</strong> ( + <SettingsIcon + aria-label="Settings" + className="inline-block size-[1em] align-[-0.15em]" + /> + ) from the app header (or press <Shortcut combo="mod+," /> + ), then select <strong>Huabu Agent</strong>. The{' '} + <strong>Chat Model</strong> is the only model you must set up to get + started. + </P> + <P> + The same page also holds optional settings — a{' '} + <strong>Utility Model</strong> for lightweight background tasks,{' '} + <strong>Image Generation</strong>, and capabilities such as{' '} + <strong>Web Search</strong> — that you can configure later. + </P> + <Callout tone="info"> + API keys and sign-in credentials saved in the Huabu desktop app are + encrypted at rest using your operating system's protected storage. + Huabu never stores them as plain text. + </Callout> + <DocImage + src="/docs/quick-start/configure-llm.png" + alt="Huabu Agent settings with the Chat Model provider, endpoint, and API key fields" + caption="Configure models and keys in Huabu Agent settings — only the Chat Model is required." + className="mx-auto max-w-2xl" + /> + + <H2>4. Create a Space</H2> + <P> + A <strong>Space</strong> is an independent place to think and work on a + topic, question, or project. Inside your Home, select{' '} + <strong>New Space</strong> to create a Space and open it immediately. + </P> + <DocImage + src="/docs/quick-start/create-canvas.png" + alt="Huabu Space list with the New Space button" + caption="Create a Space for the topic or project you want to work on." + className="mx-auto max-w-2xl" + /> + + <H2>5. Add content to your Space</H2> + <P>There are several ways to bring content into a Space:</P> + <ul className="text-fg-muted list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed"> + <li> + <strong>Paste</strong> — copy text or an image anywhere and paste it + straight into your Space. + </li> + <li> + <strong>Drag and drop</strong> — drop files from your computer into + your Space. + </li> + <li> + <strong>Upload or Link</strong> — from the toolbar, choose{' '} + <strong>Upload Files</strong> to add a PDF or image, or{' '} + <strong>Add Links</strong> to bring in a web page. + </li> + <li> + <strong>Create a node</strong> — start from scratch with a{' '} + <NODE_ICON.note + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Note</strong>,{' '} + <NODE_ICON.text + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Text</strong>,{' '} + <NODE_ICON.frame + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Frame</strong>, or{' '} + <NODE_ICON.sketch + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Sketch</strong> from the toolbar. + </li> + </ul> + <DocImage + src="/docs/quick-start/add-content.png" + alt="Huabu toolbar with the Upload or Link menu open showing Upload Files and Add Links, alongside the Note, Text, Frame, and Sketch tools" + caption="Add content by pasting, dragging, uploading — or create a Note, Text, Frame, or Sketch from the toolbar." + className="mx-auto max-w-2xl" + /> + + <H2>6. Think and work with AI</H2> + <P> + Every conversation lives in the{' '} + <DocLink href="/docs/work-with-ai">Chat Panel</DocLink>. Open it with + the top-right button ( + <Bot + aria-label="Open chat panel" + className="inline-block size-[1em] align-[-0.15em]" + /> + ), start a new conversation, and choose who you are talking to: + </P> + <ul className="text-fg-muted list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed"> + <li> + <strong>Huabu Chat</strong> — a plain conversation. Select the nodes + you want as context, ask questions, and drag useful parts of a reply + back into your Space to keep organizing your thinking. + </li> + <li> + <strong>Huabu Agent</strong> — a conversation in which the agent can + also act on your Space. It works with your materials and structure and + records its changes so you can keep or revert them. + </li> + </ul> + <P> + You can add more agents too. Pair an{' '} + <DocLink href="/docs/ai/external-agents">External Agent</DocLink> in{' '} + <strong>Settings → External Agents</strong>; afterwards it appears in + the Chat Panel as an option when you start a new conversation, bringing + its own specialized context or tools, such as understanding a repository + or producing a presentation. + </P> + <P> + Prefer to ask right beside your material? Add an{' '} + <NODE_ICON.question + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <DocLink href="/docs/work-with-ai">Agent Node</DocLink> in your Space to + start a conversation in place, so it stays in context with the ideas and + sources around it. + </P> + <Callout tone="tip" title="Where to next"> + Continue with{' '} + <DocLink href="/docs/work-in-a-space">Work in a Space</DocLink> to learn + how to select, arrange, connect, and frame materials in a Space. For + deeper AI workflows, continue with{' '} + <DocLink href="/docs/work-with-ai">Work with AI</DocLink> or configure{' '} + <DocLink href="/docs/ai/external-agents">External Agents</DocLink>. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/ai/ExternalAgents.tsx b/apps/docs/src/sections/ai/ExternalAgents.tsx new file mode 100644 index 000000000..fc85117e0 --- /dev/null +++ b/apps/docs/src/sections/ai/ExternalAgents.tsx @@ -0,0 +1,308 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Pencil, Settings as SettingsIcon, Trash2 } from 'lucide-react'; + +import { + Callout, + Code, + DocLink, + H2, + Kbd, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; + +const toc: TocEntry[] = [ + { id: 'what-is-an-external-agent', label: 'What is an external agent?' }, + { id: 'before-you-start', label: 'Before you start' }, + { id: 'create-a-profile', label: 'Create a Profile' }, + { id: 'understand-presets', label: 'Understand Presets' }, + { id: 'use-an-external-agent', label: 'Use an external agent' }, + { id: 'manage-and-troubleshoot', label: 'Manage & troubleshoot' }, +]; + +export default function ExternalAgents() { + return ( + <PageLayout + title="External Agents" + description="Connect different AI agents, configure them as Profiles in Huabu." + toc={toc} + > + <H2>What is an external agent?</H2> + <P> + External agents are AI agents outside Huabu's built-in agent + system. Huabu currently starts local{' '} + <DocLink href="https://agentclientprotocol.com/get-started/agents"> + ACP-compatible agents + </DocLink> + , connects to them over ACP, and associates each with a working + directory and chat thread. Model access, authentication, tools, + permissions, and usage charges remain with the agent or its provider. + </P> + + <H2>Before you start</H2> + <P>Prepare the agent on the same computer that runs Huabu:</P> + <ol className="list-decimal space-y-2 pl-5 text-[15px] leading-relaxed text-gray-700"> + <li> + <strong>Install an ACP-compatible agent.</strong> Huabu automatically + detects these eight ACP-capable agent commands or adapters after + installation:{' '} + <DocLink href="https://www.npmjs.com/package/@github/copilot"> + GitHub Copilot + </DocLink> + ,{' '} + <DocLink href="https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp"> + Claude Agent + </DocLink> + ,{' '} + <DocLink href="https://geminicli.com/docs/get-started/installation"> + Gemini + </DocLink> + ,{' '} + <DocLink href="https://www.npmjs.com/package/@agentclientprotocol/codex-acp"> + Codex + </DocLink> + ,{' '} + <DocLink href="https://github.com/QwenLM/qwen-code"> + Qwen Code + </DocLink> + ,{' '} + <DocLink href="https://github.com/MoonshotAI/kimi-cli"> + Kimi Code + </DocLink> + , <DocLink href="https://opencode.ai/docs/">OpenCode</DocLink>, and{' '} + <DocLink href="https://cursor.com/docs/cli/installation"> + Cursor + </DocLink> + . For any other ACP-compatible agent, select{' '} + <strong>Custom command</strong> and enter its complete ACP launch + command, such as <Code>/path/to/my-agent --acp</Code>. Use the command + documented by that agent. + </li> + <li> + <strong>Sign in and accept any first-run prompts.</strong> Open a + normal terminal, run the agent once, and complete its authentication + or provider setup. Huabu cannot complete an interactive sign-in for a + background agent process. + </li> + <li> + <strong>Ensure Huabu can find the agent.</strong> The agent command + must be on the operating system's <Code>PATH</Code> environment + variable when Huabu starts. Restart Huabu after installing the agent + or changing <Code>PATH</Code>. If the command is still unavailable, + use <strong>Custom command</strong> with its absolute executable path. + </li> + </ol> + + <H2>Create a Profile</H2> + <P> + A <strong>Profile</strong> is Huabu's saved connection to one + external agent in one working directory. Open Settings ( + <SettingsIcon + aria-label="Settings" + className="inline-block size-[1em] align-[-0.15em]" + /> + ) > <strong>External Agents</strong>, select{' '} + <strong>Add agent</strong>, and complete these fields: + </P> + <Table + headers={['Field', 'What to choose']} + rows={[ + [ + <strong>Preset</strong>, + <> + Choose a Preset to give the agent a ready-made role and workflow, + including any required tools, configuration, and setup. Leave it + set to <strong>None</strong> to use the agent as installed. See{' '} + <DocLink href="#understand-presets">Understand Presets</DocLink>. + </>, + ], + [ + <strong>Agent</strong>, + 'Installed agents appear first. Agents that are not installed appear in a disabled Not installed section, followed by Custom command. Selecting a Preset filters the list to agents supported by that Preset.', + ], + [ + <strong>Auto-approve all tool calls</strong>, + 'Optional. With no Preset, this appears when Huabu knows the selected agent’s auto-approval option. Enable it only when you trust the agent and the working directory.', + ], + [ + <strong>Working directory</strong>, + 'The local project folder used as the agent process working directory. Each Profile can point to a different folder.', + ], + [ + <strong>Display name</strong>, + 'Optional. Huabu derives a name from the agent or Preset and the working-directory folder when you leave it empty.', + ], + ]} + /> + <P> + With no Preset, select <strong>Create profile</strong>. The Profile is + ready to use immediately. Choose <strong>Custom command</strong> at the + end of the Agent menu when you need an absolute path, extra flags, or an + ACP agent Huabu does not detect. Enter the complete command Huabu should + launch. + </P> + + <H2>Understand Presets</H2> + <P> + A <strong>Preset</strong> is a ready-made workflow for a specific task. + It gives an external agent a specialized role, instructions, and tools, + and may configure the working directory for that workflow. A Preset is + not a model and does not install the external agent itself. + </P> + <P> + The first release includes the following Presets. Their additional + requirements apply alongside the installed and authenticated external + agent described above. + </P> + <Table + headers={['Preset', 'What it does', 'Additional requirements']} + rows={[ + [ + <strong>paper-reviewer</strong>, + 'Provides a paper-review role and workflow for discussing academic papers and drafting review responses.', + 'None. It does not install npm tools or skills.', + ], + [ + <strong>html-slides-maker</strong>, + 'Creates static HTML presentations and optional technical diagrams.', + <> + Node.js with <Code>npx</Code> available on the <Code>PATH</Code>, + plus network access to GitHub during setup so Huabu can install + the required skills. + </>, + ], + [ + <strong>hackmd-publisher</strong>, + 'Turns selected Space material into a HackMD document and writes the published URL back to Huabu.', + <> + A HackMD API token, Node.js with <Code>npm</Code> and{' '} + <Code>npx</Code> available on the <Code>PATH</Code>, access to a + working npm registry, and network access to GitHub during setup. + </>, + ], + ]} + /> + <Callout tone="tip"> + On macOS, Windows, and Linux, <Code>npm</Code> and <Code>npx</Code> are + normally installed with Node.js. Verify that <Code>node --version</Code> + , <Code>npm --version</Code>, and <Code>npx --version</Code> work in a + normal terminal, then restart Huabu after installing Node.js or changing + the <Code>PATH</Code>. A company npm mirror is supported as long as it + can provide the packages required by the Preset. + </Callout> + <Table + headers={['Without a Preset', 'With a Preset']} + rows={[ + [ + 'Huabu launches the selected agent with its standard ACP command.', + "Huabu launches the selected agent with the Preset's managed instructions and tools.", + ], + [ + 'You can select any installed recognized agent or enter a Custom command.', + 'You can select only an installed agent supported by that Preset. Custom command is unavailable.', + ], + [ + 'The Profile is ready as soon as it is created.', + 'Huabu prepares the working directory before the Profile becomes available.', + ], + [ + 'No Preset-specific configuration is required.', + 'The Preset may request tokens or other configuration. Secret values are stored securely and are never displayed again.', + ], + ]} + /> + <P> + After selecting a Preset, complete any fields shown directly below it, + choose an available Agent and a working directory, then select{' '} + <strong>Create and set up</strong>. Setup continues from the External + Agents list. When setup finishes successfully, the setup status and + action disappear and the Profile becomes available in Chat. If setup + fails, the Profile remains in the list so you can fix its configuration + and retry. + </P> + <Callout tone="info"> + Preset configuration belongs to the Preset and is shared by Profiles + created from the same Preset. The working directory and display name + still belong to each individual Profile. + </Callout> + + <H2>Use an external agent</H2> + <P> + Once a Profile is available, you can choose it from the agent menus in + Chat. A Profile that uses a Preset appears after setup finishes + successfully. + </P> + <ul className="list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed text-gray-700"> + <li> + To start a new conversation, open the dropdown beside the{' '} + <strong>New chat</strong> button and select a Profile under{' '} + <strong>External Agents</strong>. + </li> + <li> + Before sending the first message in an empty conversation, you can + also use the Agent menu in the message box to choose a Profile. After + you send the message, you cannot change the Agent for that + conversation. + </li> + </ul> + <P> + The <strong>Add agent</strong> entry in either menu opens Settings + directly on the External Agents page. Send a message to begin the + conversation. Huabu opens the selected Agent when needed and continues + using it for that conversation. Type <Kbd>/</Kbd> in the message box to + see any commands offered by that Agent. + </P> + <Callout tone="info"> + A conversation keeps using the Profile it started with. Start a new + conversation to use a different Agent or updated Profile settings. + </Callout> + + <H2>Manage & troubleshoot</H2> + <P> + In <strong>Settings > External Agents</strong>, use{' '} + <Pencil + aria-label="Edit" + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + to rename a Profile or update shared Preset configuration, and{' '} + <Trash2 + aria-label="Delete" + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + to remove it from future agent selectors. The launch command, selected + agent, Preset, and working directory are fixed after creation; create a + new Profile to change them. Existing chat threads keep the connection + settings captured when they were created. + </P> + <Table + headers={['Symptom', 'What to check']} + rows={[ + [ + 'An Agent is marked not installed', + 'Install the indicated CLI or adapter, run it once in a terminal, and reopen the Add agent editor. Confirm that Huabu inherits the PATH containing the executable.', + ], + [ + 'The Agent menu has no suitable installed agent', + 'For a Profile without a Preset, use Custom command with the full ACP launch command. A Preset requires one of its supported agents and cannot use a custom command.', + ], + [ + 'A Preset Profile is not available in Chat', + 'Return to External Agents and check its status. Complete the required Preset configuration, then use Set up or Retry. When setup succeeds, the status badge disappears and the Profile becomes available in Chat.', + ], + [ + 'Worker offline appears', + 'Select Restart worker. If the error names a missing executable or path, correct the installation or Profile command first.', + ], + [ + 'The agent starts but does not reply', + 'Run the same agent command in a terminal and check authentication, provider access, first-run prompts, and ACP support. Also confirm that the working directory still exists and is accessible.', + ], + ]} + /> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/ai/MemoryAndSkills.tsx b/apps/docs/src/sections/ai/MemoryAndSkills.tsx new file mode 100644 index 000000000..d8511e62e --- /dev/null +++ b/apps/docs/src/sections/ai/MemoryAndSkills.tsx @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Callout, + Code, + H2, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; + +const toc: TocEntry[] = [ + { id: 'what-they-remember', label: 'What Memory and Skills remember' }, + { id: 'two-memory-tiers', label: 'Two Memory tiers' }, + { id: 'how-memory-updates', label: 'How Memory updates' }, + { id: 'review-memory', label: 'Review and edit Memory' }, + { id: 'run-a-skill', label: 'Run a Skill' }, + { id: 'save-a-skill', label: 'Save a Skill' }, +]; + +const orderedListClassName = + 'text-fg-muted list-decimal space-y-1.5 pl-5 text-[15px] leading-relaxed'; + +export default function MemoryAndSkills() { + return ( + <PageLayout + title="Memory & Skills" + description="Memory carries durable context between conversations, while Skills preserve procedures an Agent can run again." + toc={toc} + > + <H2>What Memory and Skills remember</H2> + <P> + Memory and Skills help the AI carry useful knowledge into later work, + but they preserve different things. + </P> + <Table + headers={['Save', 'When to use it', 'Example']} + rows={[ + [ + <strong>Memory</strong>, + 'For a preference, constraint, decision, or durable fact.', + 'I prefer concise summaries.', + ], + [ + <strong>Skill</strong>, + 'For a repeatable procedure with a consistent outcome.', + 'Turn selected research notes into a comparison structure.', + ], + ]} + /> + <Callout tone="info"> + Huabu curates Memory automatically. Use <Code>/create-skill</Code> when + you want Agent to preserve a successful repeatable workflow. + </Callout> + <Callout tone="info"> + Memory curation and Skill creation or updates use the Utility Model. + Running a Skill to complete a task uses the Chat Model. If Utility is + not configured, Huabu uses Chat for authoring too. + </Callout> + + <H2>Two Memory tiers</H2> + <Table + headers={['Tier', 'Scope', 'Where it lives', 'Typical contents']} + rows={[ + [ + <strong>User</strong>, + 'Applies to every Space in this Home.', + <Code>setting/user.md</Code>, + 'Preferences, constraints, and recurring vocabulary.', + ], + [ + <strong>Space</strong>, + 'Applies only to the current Space.', + <Code>.memory/space.md</Code>, + 'The topic, decisions, and things to revisit.', + ], + ]} + /> + <P> + User Memory is available to every built-in Chat and Agent turn. Space + Memory is read on demand when the current topic needs it. Both tiers are + kept small, about 4 KB or 80 lines each, so they remain focused. + </P> + + <H2>How Memory updates</H2> + <P> + After enough Space activity, a background process reviews recent + conversations and operations, then keeps only details worth carrying + forward. It runs without interrupting your work. + </P> + <P> + Telling the AI to remember a preference or Space decision makes the + request a strong candidate for that review. It does not update Memory + immediately, and the curator still decides whether the detail is worth + keeping. + </P> + + <H2>Review and edit Memory</H2> + <P> + Both tiers are plain Markdown. Open them in any editor to audit, + correct, or remove an entry. Huabu checks the size limit when it writes + Memory, but it does not trim a file that you edit beyond the limit. + </P> + <Callout tone="tip"> + Keep large reference material as nodes in a Space. Memory works best for + small facts and preferences that should influence future conversations. + </Callout> + + <H2>Run a Skill</H2> + <ol className={orderedListClassName}> + <li>Start a conversation with the built-in Agent.</li> + <li>Select the nodes the Skill should work on.</li> + <li> + Type <Code>/</Code> in the chat input to open the Skills menu. + </li> + <li>Choose a Skill, add any task-specific instruction, and send.</li> + </ol> + <Callout tone="info"> + The nodes selected when you send the request remain the Skill's + focus. + </Callout> + + <H2>Save a Skill</H2> + <P> + In an Agent conversation, type <Code>/create-skill</Code> followed by a + description of the reusable procedure. To change an existing Skill, use{' '} + <Code>/update-skill</Code> followed by the Skill and the change you + want. + </P> + <P> + When creating a Skill, Huabu checks the catalogue and warns if a similar + Skill may be a better update target. It does not silently turn creation + into an update. + </P> + <P> + User Skills are plain Markdown files stored under{' '} + <Code>setting/skills/<id>/SKILL.md</Code> in your Home. You can + inspect, edit, copy, or version-control them like other text files. + </P> + <Callout tone="tip"> + Keep each Skill focused on one reusable outcome. A short catalogue of + distinct procedures is easier to use than many overlapping variations. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/ai/ModelsAndCapabilities.tsx b/apps/docs/src/sections/ai/ModelsAndCapabilities.tsx new file mode 100644 index 000000000..279c0318b --- /dev/null +++ b/apps/docs/src/sections/ai/ModelsAndCapabilities.tsx @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Callout, + H2, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; + +const toc: TocEntry[] = [ + { id: 'choose-models', label: 'Choose your models' }, + { id: 'chat-model', label: 'Chat Model' }, + { id: 'utility-model', label: 'Utility Model' }, + { id: 'image-understanding', label: 'Image understanding' }, + { id: 'image-generation', label: 'Image generation' }, + { id: 'web-and-video', label: 'Web search and video transcripts' }, +]; + +export default function ModelsAndCapabilities() { + return ( + <PageLayout + title="Models & Capabilities" + description="Understand which model Huabu uses for each kind of work and configure optional web, image, and video capabilities." + toc={toc} + > + <H2>Choose your models</H2> + <P> + Open <strong>Settings > Huabu Agent</strong> to configure models and + optional capabilities. Only the Chat Model is required. + </P> + <Table + headers={['Setting', 'Used for', 'Required']} + rows={[ + [ + <strong>Chat Model</strong>, + 'Conversations, Agent work, and running Skills.', + 'Yes', + ], + [ + <strong>Utility Model</strong>, + 'Memory, Skill creation and updates, labels, summaries, keywords, and intent suggestions.', + 'No', + ], + [ + <strong>Image Generation</strong>, + 'Creating or editing images through Agent.', + 'No', + ], + ]} + /> + + <H2>Chat Model</H2> + <P> + The Chat Model powers built-in Chat and Agent conversations. It reasons + over your request, reads Space material, chooses tools, and plans Space + changes. Running a Skill to complete a task also uses this model. Chat + acts as the fallback when the Utility Model is not configured or cannot + process required image input. + </P> + <Callout tone="tip"> + Choose the most capable model you want to use for interactive work. It + needs a sufficiently large context window for longer conversations and + Spaces. + </Callout> + + <H2>Utility Model</H2> + <P> + The optional Utility Model handles frequent, lightweight background + work: Memory curation, Skill creation and updates, intent suggestions, + image and Frame labels, and content labels, summaries, and keywords. A + faster or less expensive model can reduce latency and cost for these + tasks. + </P> + <Callout tone="info"> + Leave Utility Model set to <strong>Follow chat model</strong> to use the + Chat Model for these tasks too. + </Callout> + + <H2>Image understanding</H2> + <P> + Chat and Agent can inspect images when the selected Chat Model supports + image input. Huabu also uses image input when it labels image nodes or + interprets visual context. If a configured Utility Model cannot accept + images, Huabu falls back to the Chat Model for that image task. + </P> + + <H2>Image generation</H2> + <P> + Image Generation is separate from the Chat and Utility Models. Configure + an Azure OpenAI image deployment in{' '} + <strong>Settings > Huabu Agent</strong> to let Agent create images or + use Space images and sketches as visual references. Built-in Chat is + read-only and does not generate images in the Space. + </P> + + <H2>Web search and video transcripts</H2> + <Table + headers={['Capability', 'What it enables', 'Setup']} + rows={[ + [ + <strong>Web Search</strong>, + 'Lets Chat and Agent find current information and cite the source URLs.', + 'Add a Tavily API key under Settings > Huabu Agent > Other Capabilities.', + ], + [ + <strong>YouTube Transcripts</strong>, + 'Lets Huabu import transcript text when processing YouTube material.', + 'Add a RapidAPI key under Settings > Huabu Agent > Other Capabilities.', + ], + ]} + /> + <Callout tone="info"> + Skills provide reusable instructions; they do not add new tools. Web + search, image generation, and transcript loading must be configured as + capabilities separately. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/ai/WorkWithAI.tsx b/apps/docs/src/sections/ai/WorkWithAI.tsx new file mode 100644 index 000000000..8283d1fa4 --- /dev/null +++ b/apps/docs/src/sections/ai/WorkWithAI.tsx @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Callout, + H2, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; +import { NODE_ICON } from '../../config/nodeIcons'; + +const toc: TocEntry[] = [ + { id: 'choose-how-to-work', label: 'Choose how to work' }, + { id: 'give-ai-context', label: 'Give AI the right context' }, + { id: 'let-agent-work', label: 'Let Agent work on the Space' }, + { id: 'review-ai-changes', label: 'Review AI changes' }, + { + id: 'work-beside-material', + label: 'Work beside your material with an Agent Node', + }, + { id: 'ask-and-discuss', label: 'Ask and discuss in Chat' }, +]; + +const listClassName = + 'text-fg-muted list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed'; +const orderedListClassName = + 'text-fg-muted list-decimal space-y-1.5 pl-5 text-[15px] leading-relaxed'; + +export default function WorkWithAI() { + return ( + <PageLayout + title="Work with AI" + description="Let an Agent shape your Space, or keep an Agent Node beside the material you are developing." + toc={toc} + > + <H2>Choose how to work</H2> + <P> + Huabu offers three ways to work with AI. Start with the outcome you + want, then choose the surface that keeps the conversation and any + changes in the right place. + </P> + <Table + headers={['What you want to do', 'Use']} + rows={[ + [ + 'Create, edit, arrange, or connect things in the Space', + <strong>Agent</strong>, + ], + [ + 'Keep a focused conversation beside the relevant material', + <> + <NODE_ICON.question + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Agent Node</strong> + </>, + ], + [ + 'Ask questions, discuss ideas, or understand selected material', + <strong>Chat</strong>, + ], + ]} + /> + + <H2>Give AI the right context</H2> + <P> + Good results start with a clear focus. Huabu sends references to the + nodes you select, and the AI can inspect their content and the + surrounding Space when it needs more detail. + </P> + <ul className={listClassName}> + <li> + <strong>Select relevant nodes before sending.</strong> The source + count beside the chat input confirms which nodes are in focus. + </li> + <li> + <strong>Paste or drop a file or image into the chat input.</strong> It + is attached to that message without first becoming a Space node. + </li> + <li> + <strong>Place an Agent Node beside the subject.</strong> Its position, + parent Frame, and nearby or connected nodes help define the local + context. + </li> + </ul> + <Callout tone="tip"> + Select only what matters. A smaller, explicit scope is usually easier + for the AI to interpret and easier for you to review. + </Callout> + + <H2>Let Agent work on the Space</H2> + <ol className={orderedListClassName}> + <li> + Open the Chat Panel and start a new conversation with{' '} + <strong>Agent</strong>. + </li> + <li> + Describe both the result and its shape—for example, “group these notes + into named Frames and connect each source to its summary.” + </li> + <li>Send the request and let Agent update the Space.</li> + <li>Review the resulting changes above the chat input.</li> + </ol> + <P> + Agent can create, edit, move, group, connect, and remove items as part + of a multi-step task. The changes appear in the Space as the Agent + works; the review card then lets you keep or revert them. + </P> + + <H2>Review AI changes</H2> + <P> + Agent changes are collected by conversation in a review card above the + chat input. Expand it to inspect individual changes or act on the whole + set. + </P> + <Table + headers={['Control', 'What it does']} + rows={[ + [ + 'Preview', + 'Press and hold to temporarily show the state before the change.', + ], + ['Keep', 'Keep one change and remove it from the review list.'], + ['Revert', 'Undo one change when it is still safe to do so.'], + ['Keep all', 'Keep every pending change in this conversation.'], + [ + 'Revert all', + 'Undo every pending change that can still be safely reverted.', + ], + ]} + /> + <Callout tone="warning"> + If you or another Agent edits the same item later, an earlier change may + become unsafe to revert. Huabu disables that revert instead of + overwriting newer work. + </Callout> + + <H2>Work beside your material with an Agent Node</H2> + <P> + An Agent Node keeps a dedicated conversation on the Space, making it + useful for open questions that should remain visible beside their source + material. + </P> + <ol className={orderedListClassName}> + <li> + Create an{' '} + <NODE_ICON.question + aria-hidden + className="inline-block size-[1em] align-[-0.15em]" + />{' '} + <strong>Agent Node</strong> from the toolbar or a node's + quick-create arrow. + </li> + <li> + The Chat Panel opens a conversation bound to the new Agent Node. + </li> + <li> + Choose the built-in Chat, Agent, or an available external agent. + </li> + <li>Write the question and send it.</li> + <li> + Double-click the Agent Node later to reopen and continue its thread. + </li> + </ol> + <P> + Put the Agent Node inside the Frame you want it to understand, or + connect it to the most relevant nodes. The conversation stays bound to + that node and to the agent selected for it. + </P> + + <H2>Ask and discuss in Chat</H2> + <ol className={orderedListClassName}> + <li>Open the Chat Panel from the top-right of the Space.</li> + <li> + Start a new conversation with <strong>Chat</strong>. + </li> + <li>Write your question and send it.</li> + </ol> + <P> + Chat is read-only: it can inspect the Space, read selected sources, + search files or the web, and explain what it finds, but it does not + modify the Space. Use it for summaries, comparisons, explanations, and + brainstorming. + </P> + <Callout tone="info"> + When the next step requires Space changes, start a new conversation with{' '} + <strong>Agent</strong> and describe the outcome you want. An existing + Chat conversation cannot be switched to Agent. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/reference/IssueReporting.tsx b/apps/docs/src/sections/reference/IssueReporting.tsx new file mode 100644 index 000000000..6ac2321ca --- /dev/null +++ b/apps/docs/src/sections/reference/IssueReporting.tsx @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Callout, + Code, + CodeBlock, + DocLink, + H2, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; + +const toc: TocEntry[] = [ + { id: 'before-reporting', label: 'Before reporting' }, + { id: 'describe-the-problem', label: 'Describe the problem' }, + { id: 'system-information', label: 'System information' }, + { id: 'screenshots-and-logs', label: 'Screenshots and logs' }, + { id: 'ai-problems', label: 'AI-related problems' }, + { id: 'submit-the-issue', label: 'Submit the issue' }, +]; + +export default function IssueReporting() { + return ( + <PageLayout + title="Report an Issue" + description="Report bugs and unexpected behavior through GitHub Issues. A short report with clear reproduction steps gives the team the best chance of identifying and fixing the problem." + toc={toc} + > + <Callout tone="info"> + Huabu uses{' '} + <DocLink href="https://github.com/microsoft/Huabu/issues"> + GitHub Issues + </DocLink>{' '} + as its public feedback channel. Never include API keys, access tokens, + private documents, or other sensitive data in an issue. + </Callout> + + <H2>Before reporting</H2> + <ul className="list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed text-gray-700"> + <li> + <strong>Update Huabu</strong> to the latest available version and + check whether the problem still occurs. + </li> + <li> + <strong>Search existing issues</strong> in the{' '} + <DocLink href="https://github.com/microsoft/Huabu/issues"> + Huabu issue tracker + </DocLink>{' '} + using the error message or a few words that describe the symptom. + </li> + </ul> + <P> + If an existing issue describes the same problem, add any new + reproduction details, your Huabu version, and your operating system to + that issue instead of opening a duplicate. + </P> + + <H2>Describe the problem</H2> + <P> + Use a specific title that describes the visible symptom, such as “PDF + node becomes blank after reopening a Space.” In the report, include the + following information: + </P> + <Table + headers={['Field', 'What to include']} + rows={[ + [ + <strong>What happened</strong>, + 'A short description of the behavior you observed, including any error message.', + ], + [ + <strong>What you expected</strong>, + 'What you were trying to do and what you expected Huabu to do instead.', + ], + [ + <strong>Steps to reproduce</strong>, + 'A numbered list of the shortest sequence that triggers the problem.', + ], + [ + <strong>Frequency</strong>, + 'Whether it happens every time, sometimes, or only once, and whether it also happens in a new Space.', + ], + ]} + /> + <P>For example:</P> + <CodeBlock language="text">{`What happened +The PDF node is blank after I close and reopen its Space. + +Expected behavior +The first page preview should appear after the Space reloads. + +Steps to reproduce +1. Open Huabu and create a new Space. +2. Drag a PDF into the Space. +3. Return to Home, then reopen the Space. +4. The PDF node is blank. + +Frequency +Every time with this PDF; it also happens in a new Space.`}</CodeBlock> + + <H2>System information</H2> + <P> + Include the Huabu version, operating system version, CPU architecture, + and whether you use the desktop or web app. In the desktop app, choose{' '} + <strong>Help → Troubleshooting → Copy System Information</strong>, then + paste the result into the issue. + </P> + + <H2>Screenshots and logs</H2> + <P> + Add a screenshot or short screen recording when the problem is visual or + difficult to describe. For crashes, failed requests, or unexpected + errors, choose <strong>Help → Troubleshooting → Open Server Log</strong>{' '} + and attach only the relevant part of <Code>server.log</Code>. + </P> + <Callout tone="warning"> + Review every attachment before uploading it. Remove API keys, access + tokens, private conversations, personal information, confidential + documents, and file paths that reveal information you do not want to + publish. + </Callout> + + <H2>AI-related problems</H2> + <P> + If the problem involves an AI response or edit, also include the details + below. Share only the minimum context needed to understand the problem. + </P> + <ul className="list-disc space-y-1.5 pl-5 text-[15px] leading-relaxed text-gray-700"> + <li> + Whether you used Chat, Agent mode, or an Agent node, and whether it + was powered by Huabu Agent or an external agent. + </li> + <li>The provider and model name, but never the API key.</li> + <li>Which nodes or materials were selected as context.</li> + <li> + The relevant prompt, response, or proposed edit, if safe to share, and + whether you applied any proposed changes. + </li> + </ul> + + <H2>Submit the issue</H2> + <P> + Once the report contains the problem, expected behavior, reproduction + steps, and system information, create a{' '} + <DocLink href="https://github.com/microsoft/Huabu/issues/new/choose"> + new GitHub issue + </DocLink> + . You can add screenshots or logs by dragging them into the issue + editor. + </P> + <Callout tone="tip"> + A minimal reproduction is more useful than a large attachment. Share a + sample file or exported Space only when it is necessary, safe to + publish, and stripped of private content. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/reference/Shortcuts.tsx b/apps/docs/src/sections/reference/Shortcuts.tsx new file mode 100644 index 000000000..d75136007 --- /dev/null +++ b/apps/docs/src/sections/reference/Shortcuts.tsx @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Fragment, useMemo } from 'react'; + +import { + Callout, + H2, + Kbd, + P, + PageLayout, + slugify, + Table, + type TocEntry, +} from '../../components'; +import { keyboardShortcutSections } from '../../config/shortcuts'; + +const PASTE_SECTION = { + id: 'paste-behaviour', + label: 'Paste behaviour', +} as const; + +/** + * Maps shortcut-template tokens that would otherwise collide with the `+` + * separator (e.g. `Ctrl/Cmd+Plus`) to their printable glyphs. Mirrors the + * mapping used by `utils/platform.ts` for the in-app shortcuts modal so the + * two surfaces stay visually consistent. + */ +const KEY_GLYPHS: Record<string, string> = { + Plus: '+', + Minus: '−', // U+2212 minus sign — distinct from the `+` separator + Equal: '=', +}; + +/** + * Render a shortcut template from `config/shortcuts.ts` as a row of + * `<Kbd>` chips. Unlike the in-app modal — which collapses `Ctrl/Cmd` to + * the platform-native modifier — the docs always render both `Ctrl` and + * `Cmd` so the page reads correctly regardless of the visitor's OS. + * + * Handles three template shapes: + * - `Key+Key+...` combinations (e.g. `Ctrl/Cmd+Shift+Z`) + * - `Key / Key` alternatives (e.g. `Delete / Backspace`) + * - `Key (qualifier)` annotations (e.g. `Space (hold)`) + */ +function ShortcutKbd({ template }: { template: string }) { + if (template.includes(' / ')) { + const alternatives = template.split(' / '); + return ( + <> + {alternatives.map((alt, index) => ( + <Fragment key={`alt-${index}`}> + {index > 0 && ' / '} + <ShortcutKbd template={alt} /> + </Fragment> + ))} + </> + ); + } + + const qualifierMatch = /^(.+?)\s+\(([^)]+)\)$/.exec(template); + if (qualifierMatch) { + const [, base, qualifier] = qualifierMatch; + return ( + <> + <ShortcutKbd template={base} /> ({qualifier}) + </> + ); + } + + const parts = template.split('+').map((part) => part.trim()); + return ( + <> + {parts.map((part, index) => ( + <Fragment key={`part-${index}`}> + {index > 0 && '+'} + {part === 'Ctrl/Cmd' ? ( + <> + <Kbd>Ctrl</Kbd>/<Kbd>Cmd</Kbd> + </> + ) : ( + <Kbd>{KEY_GLYPHS[part] ?? part}</Kbd> + )} + </Fragment> + ))} + </> + ); +} + +export default function Shortcuts() { + const toc = useMemo<TocEntry[]>( + () => [ + ...keyboardShortcutSections.map((section) => ({ + id: slugify(section.title), + label: section.title, + })), + { id: PASTE_SECTION.id, label: PASTE_SECTION.label }, + ], + [], + ); + + return ( + <PageLayout + title="Keyboard Shortcuts" + description={ + <> + <Kbd>Ctrl</Kbd> on Windows / Linux, <Kbd>Cmd</Kbd> (⌘) on macOS — the + tables below write it as <Kbd>Ctrl</Kbd>/<Kbd>Cmd</Kbd>. Press{' '} + <Kbd>?</Kbd> in any Space to view the same list in-app. Shortcuts in + the General section are available only in the desktop app. + </> + } + toc={toc} + > + {keyboardShortcutSections.map((section) => ( + <Fragment key={section.title}> + <H2>{section.title}</H2> + <Table + headers={['Shortcut', 'Action']} + rows={section.items.map((item) => [ + <ShortcutKbd key={item.keys} template={item.keys} />, + item.description, + ])} + /> + </Fragment> + ))} + + <H2>{PASTE_SECTION.label}</H2> + <P> + <Kbd>Ctrl</Kbd>/<Kbd>Cmd</Kbd>+<Kbd>V</Kbd> resolves to different node + types depending on what's on your clipboard: + </P> + <Table + headers={['Clipboard contents', 'Result']} + rows={[ + [ + 'Space nodes copied earlier', + 'Pasted as duplicates at the cursor position.', + ], + ['Image file', 'Creates an Image node and uploads it.'], + ['PDF file', 'Creates a PDF node and uploads it.'], + ['Video file', 'Creates a Video node and uploads it.'], + [ + 'One or more URLs (one per line)', + 'Auto-detected as image / PDF / web page / YouTube; one node per line.', + ], + ['Plain text', 'Creates a Note with the pasted text.'], + ]} + /> + + <Callout tone="info"> + Shortcuts adapt to your platform automatically — the modifier shows up + as <Kbd>Cmd</Kbd> on macOS and <Kbd>Ctrl</Kbd> on Windows / Linux. + </Callout> + + <Callout tone="info"> + On the <strong>Drag and drop</strong> row above: the cursor reflects the + active mode — a <strong>+</strong> badge means copy, no badge means + move. Drags that can't mutate their source (AI chat cards, web / + image cards, external files or URLs) always fall back to copy regardless + of the modifier. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/space/DataAndBackup.tsx b/apps/docs/src/sections/space/DataAndBackup.tsx new file mode 100644 index 000000000..f852ff941 --- /dev/null +++ b/apps/docs/src/sections/space/DataAndBackup.tsx @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Code, + CodeBlock, + H2, + P, + PageLayout, + Table, + type TocEntry, +} from '../../components'; + +const toc: TocEntry[] = [ + { id: 'home-layout', label: 'Home layout' }, + { id: 'per-space-files', label: 'Per-Space files' }, + { id: 'home-settings', label: 'Home settings & skills' }, + { id: 'hand-editing', label: 'Hand-editing files' }, +]; + +export default function DataAndBackup() { + return ( + <PageLayout + title="Data & Files" + description="Huabu stores each Space as a directory of plain files inside the Home folder you choose." + toc={toc} + > + <H2>Home layout</H2> + <P> + Each Space is a self-contained directory inside your Home. Its content, + attachments, AI memory, and conversation history stay together. + </P> + <CodeBlock language="text">{`<home>/ +├── <space-title>/ # one folder per Space +│ ├── space.json # this Space's topology +│ ├── nodes/<node-title>.md +│ ├── .artifacts/<artifactId>.<ext> +│ ├── .memory/space.md # Space memory +│ └── .history/ # conversation and event history +└── setting/ + ├── user.md # User memory + └── skills/<id>/SKILL.md # user-authored skills`}</CodeBlock> + + <H2>Per-Space files</H2> + <Table + headers={['Item', 'Format', 'Notes']} + rows={[ + [ + <Code>space.json</Code>, + 'JSON (atomic writes)', + 'Topology — nodes, edges, geometry, version. Each save writes a .tmp then renames.', + ], + [ + <Code>nodes/<title>.md</Code>, + 'Markdown + YAML frontmatter', + "One file per node (Note / Text / Web / PDF / Image / Video / Frame). Don't edit the id field.", + ], + [ + <Code>.artifacts/</Code>, + 'Raw binaries', + 'PDF / image / video originals; each filename is based on its artifact ID.', + ], + [ + <Code>.memory/space.md</Code>, + 'Markdown', + 'Space-level memory the AI writes.', + ], + [ + <Code>.history/</Code>, + 'JSON / JSONL', + 'Conversation threads and event timeline.', + ], + ]} + /> + + <H2>Home settings & skills</H2> + <P> + The <Code>setting/</Code> directory contains information shared across + Spaces: <Code>user.md</Code> stores user memory, while{' '} + <Code>skills/<id>/SKILL.md</Code> stores user-authored skills and + their supporting files. + </P> + + <H2>Hand-editing files</H2> + <Table + headers={['File', 'Hand-editable?']} + rows={[ + [ + <Code>space.json</Code>, + "❌ Don't edit — Huabu manages this file automatically.", + ], + [ + <> + <Code>nodes/<title>.md</Code> body + </>, + '✅ Yes — next Space load picks it up.', + ], + [ + <> + <Code>nodes/<title>.md</Code> frontmatter <Code>id</Code> + </>, + '❌ Never — node references rely on it.', + ], + [ + <> + <Code>nodes/<title>.md</Code> filename + </>, + '✅ Renaming the file renames the node on next load.', + ], + [ + <> + <Code>.artifacts/</Code> filenames + </>, + <> + ❌ Don't change — filename = artifactId, used by the + node's <Code>src</Code>. + </>, + ], + [ + <Code>.history/.../*.json</Code>, + 'Not recommended — but deletions only lose conversation context, not Space content.', + ], + [ + <Code>setting/user.md</Code>, + '✅ Edit freely; the AI will respect the 4 KB cap on its next write.', + ], + [ + <Code>setting/skills/<id>/SKILL.md</Code>, + '✅ Author your own skills here.', + ], + ]} + /> + </PageLayout> + ); +} diff --git a/apps/docs/src/sections/space/WorkInASpace.tsx b/apps/docs/src/sections/space/WorkInASpace.tsx new file mode 100644 index 000000000..4bdcac10e --- /dev/null +++ b/apps/docs/src/sections/space/WorkInASpace.tsx @@ -0,0 +1,378 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + Callout, + DocImage, + H2, + H3, + P, + PageLayout, + Shortcut, + Table, + type TocEntry, +} from '../../components'; +import { NODE_ICON } from '../../config/nodeIcons'; + +const toc: TocEntry[] = [ + { id: 'understand-the-space', label: 'Understand the Space' }, + { id: 'navigate-and-select', label: 'Navigate and select' }, + { id: 'use-the-toolbar', label: 'Use the toolbar' }, + { id: 'move-content-by-dragging', label: 'Move content by dragging' }, + { id: 'choose-the-right-node', label: 'Choose the right node' }, + { id: 'build-structure', label: 'Build structure' }, + { id: 'open-view-and-edit', label: 'Open, view, and edit' }, + { id: 'everyday-operations', label: 'Everyday operations' }, +]; + +const iconClassName = 'inline-block size-[1em] align-[-0.15em]'; + +export default function WorkInASpace() { + return ( + <PageLayout + title="Work in a Space" + description="Create, arrange, connect, and inspect the materials and ideas in a Huabu Space." + toc={toc} + > + <H2>Understand the Space</H2> + <P> + A <strong>Space</strong> is an infinite two-dimensional work surface for + one topic, question, or project. Everything on it is built from three + elements: <strong>nodes</strong> hold material or ideas,{' '} + <strong>edges</strong> express relationships, and{' '} + <strong>Frames</strong> group related nodes into named regions. + </P> + <P> + Position is useful too: keep related material close, separate competing + directions, and use Frames and labelled edges when a relationship should + be explicit. + </P> + + <H2>Navigate and select</H2> + <Table + headers={['Action', 'How']} + rows={[ + [ + 'Select', + 'Use the Select tool, then click a node. Click empty space to clear the selection.', + ], + [ + 'Select several nodes', + <> + Hold <Shortcut combo="mod" /> while clicking nodes, drag a + rectangle in empty space, or draw around them with Lasso. + </>, + ], + [ + 'Move', + 'Drag a node. If several nodes are selected, they move together.', + ], + [ + 'Pan', + <> + Scroll the mouse wheel, use the Pan tool, or hold{' '} + <Shortcut combo="Space" /> temporarily while dragging the Space. + </>, + ], + [ + 'Zoom', + 'Press Ctrl/Cmd+Plus to zoom in or Ctrl/Cmd+Minus to zoom out. You can also use a trackpad pinch gesture.', + ], + ]} + /> + <Callout tone="tip"> + When dragging a node across a Frame, hold <Shortcut combo="Space" /> to + move it without adding it to or removing it from that Frame. + </Callout> + + <H2>Use the toolbar</H2> + <P> + The toolbar is the main place to choose how you interact with the Space + and what you want to add. Select a creation tool, then click or drag in + the Space to place the new node. Press <Shortcut combo="Esc" /> to + cancel a placement tool. + </P> + <Table + headers={['Group', 'Tools', 'What they do']} + rows={[ + [ + 'Navigate', + 'Select (S), Pan (P), Lasso (L)', + 'Select and move nodes, move the viewport, or draw a freehand selection.', + ], + [ + 'Create', + 'Note (1), Text (2), Frame (3), Sketch (4)', + 'Create your own writing, labels, groups, and drawings.', + ], + [ + 'Import', + 'Upload Files, Add Links', + 'Bring images, PDFs, videos, HTML files, and web links into the Space.', + ], + [ + 'AI', + 'Agent (A)', + 'Place an Agent node and start an AI conversation beside the relevant material.', + ], + ]} + /> + <P> + You can also drag files from your computer onto the Space, paste files, + URLs, images, or text, and copy existing nodes. Huabu creates the + matching node type and places it near the pointer. + </P> + + <H2>Move content by dragging</H2> + <P> + Content can flow directly from your computer and other sources into the + Space and your writing. Drop a local file or a useful block or excerpt + onto empty space to make it a node. You can also drop extracted content + into a Note to continue working with it there. Huabu preserves the + appropriate format, so you do not need to import, copy, create a + destination, and paste in separate steps. + </P> + <Table + headers={['Drag from', 'Drop on empty space', 'Drop into a Note']} + rows={[ + [ + 'A local file', + 'Creates the matching Image, PDF, Video, or Web node and uploads the file.', + 'Not supported.', + ], + [ + 'A Chat reply', + 'Creates a Note from a text block or an Image from an image block.', + 'Appends the block, or inserts it at the indicator when the Note is expanded.', + ], + [ + 'A PDF selection', + 'Creates a Note from selected text or an Image from a captured area.', + 'Appends the text or image to the Note.', + ], + [ + 'A Note block', + 'Moves the block into a new Note.', + 'Moves the block to the end of the other Note.', + ], + ]} + /> + <P> + Only one content node can be expanded at a time. A selection dragged + from an expanded PDF or Note therefore lands on the target Note in the + Space and is appended to its end. When the target Note itself is + expanded, content available from Chat can follow the insertion indicator + between existing blocks, and its own blocks can be reordered. + </P> + <Callout tone="tip"> + Dragging from Chat or a PDF always copies the source. When dragging a + Note block, the default is move; hold <strong>Option</strong> on macOS + or <strong>Ctrl</strong> on Windows / Linux while dragging to copy it + instead. + </Callout> + + <H2>Choose the right node</H2> + <Table + headers={['Node', 'Use it for']} + rows={[ + [ + <> + <NODE_ICON.note aria-hidden className={iconClassName} />{' '} + <strong>Note</strong> + </>, + 'Ideas, prose, outlines, lists, quotes, code, and other structured Markdown content.', + ], + [ + <> + <NODE_ICON.text aria-hidden className={iconClassName} />{' '} + <strong>Text</strong> + </>, + 'Short titles, labels, captions, and visual annotations.', + ], + [ + <> + <NODE_ICON.image aria-hidden className={iconClassName} />{' '} + <strong>Image</strong> + </>, + 'Images, diagrams, and screenshots.', + ], + [ + <> + <NODE_ICON.pdf aria-hidden className={iconClassName} />{' '} + <strong>PDF</strong> + </>, + 'Reading a complete document and extracting text or regions from it.', + ], + [ + <> + <NODE_ICON.video aria-hidden className={iconClassName} />{' '} + <strong>Video</strong> + </>, + 'Local videos and supported video links.', + ], + [ + <> + <NODE_ICON.web aria-hidden className={iconClassName} />{' '} + <strong>Web</strong> + </>, + 'Web pages and extracted article content.', + ], + [ + <> + <NODE_ICON.frame aria-hidden className={iconClassName} />{' '} + <strong>Frame</strong> + </>, + 'A named group or region that contains and arranges other nodes.', + ], + [ + <> + <NODE_ICON.sketch aria-hidden className={iconClassName} />{' '} + <strong>Sketch</strong> + </>, + 'Freehand diagrams, marks, and visual thinking.', + ], + [ + <> + <NODE_ICON.question aria-hidden className={iconClassName} />{' '} + <strong>Agent</strong> + </>, + 'An AI conversation placed beside the material it concerns.', + ], + ]} + /> + + <H3>Text or Note?</H3> + <Table + headers={['', 'Text', 'Note']} + rows={[ + [ + 'Best for', + 'Titles, labels, short captions', + 'Ideas and longer writing', + ], + [ + 'Content', + 'A short plain-text string', + 'Rich text stored as Markdown', + ], + [ + 'Editing', + 'Directly in the Space', + 'In the node or expanded editor', + ], + [ + 'Formatting', + 'Typography and colour', + 'Headings, lists, quotes, code, links, and more', + ], + ]} + /> + <Callout tone="info"> + A simple rule: use <strong>Text</strong> to label the Space; use a{' '} + <strong>Note</strong> to develop an idea. + </Callout> + + <H2>Build structure</H2> + <H3>Group related work with Frames</H3> + <P> + A Frame is more than a rectangle: it gives a group a name and keeps its + contents together. Use one for a topic, project stage, chapter, source + collection, or intended output. Drag nodes into or out of a Frame, or + select several nodes and press <Shortcut combo="mod+g" /> to group them. + </P> + <P> + A Frame can keep its children in a free arrangement or lay them out in a + row or column. Moving the Frame moves its contents, while moving nodes + inside it changes their local arrangement. + </P> + + <H3>Create a connected Note or Agent node</H3> + <P> + Select a node to reveal arrows above, below, left, and right. Choose an + arrow, then choose <strong>Note</strong> or <strong>Agent</strong>. + Huabu places the new node on that side, avoids nearby content when + possible, and connects it to the original node automatically. + </P> + <DocImage + src="/docs/work-in-a-space/quick-create-arrows-full.png" + alt="A selected Note with directional arrows and the connected-node menu open beside the right arrow" + caption="Select a directional arrow, then choose Note or Agent to create and connect a new node on that side." + className="mx-auto max-w-3xl" + /> + + <H3>Connect existing nodes</H3> + <P> + Hover or select a node to reveal its small connection points. Drag from + one point to another node to create an edge. Select the edge to add a + label or change its direction. Use an edge when the relationship should + remain explicit even after the nodes move. + </P> + + <H2>Open, view, and edit</H2> + <P> + Double-click a content node to expand it. The expanded panel can stay + beside the Space in <strong>Split View</strong>, or switch to{' '} + <strong>Full View</strong> when you want more room to read or edit. + Close the panel to return to the same place in the Space. + </P> + + <H3>Work with a Note</H3> + <P> + A Note supports headings, lists, tasks, quotes, code, tables, links, and + other rich content. Expand it for the full editor; switch between rich + text and raw Markdown when needed. Drag blocks out to organize them as + separate Notes, or move them to the end of another Note in the Space. + When this Note is expanded, you can also place content dragged from Chat + at the insertion indicator. + </P> + + <H3>Work with a PDF</H3> + <P> + Expand a PDF to read the full document with page thumbnails. Highlight + text to drag it out as a Note, or select an area to drag it out as an + Image. Drop either one on empty space to create a node or into a Note to + add it to your writing. You can also send a capture to Chat, keeping the + extracted idea close to its source. + </P> + + <H2>Everyday operations</H2> + <Table + headers={['Action', 'How']} + rows={[ + [ + 'Copy and paste', + <> + <Shortcut combo="mod+c" /> and <Shortcut combo="mod+v" /> + </>, + ], + [ + 'Delete', + <> + Select nodes or edges, then press <Shortcut combo="Delete" /> or{' '} + <Shortcut combo="Backspace" />. + </>, + ], + [ + 'Undo and redo', + <> + <Shortcut combo="mod+z" /> and <Shortcut combo="mod+shift+z" /> + </>, + ], + [ + 'Align and distribute', + 'Select several nodes, then use the floating toolbar to align their edges or centres and spread them evenly.', + ], + [ + 'Resize', + 'Select a node and drag its resize handles. Available handles depend on the node type.', + ], + ]} + /> + <Callout tone="tip"> + Start simple: add a source, write a Note beside it, and group related + work in a Frame. Add edges only when a relationship needs to be stated + explicitly. + </Callout> + </PageLayout> + ); +} diff --git a/apps/docs/src/setting.png b/apps/docs/src/setting.png new file mode 100644 index 000000000..ebacd7464 Binary files /dev/null and b/apps/docs/src/setting.png differ diff --git a/apps/docs/src/vite-env.d.ts b/apps/docs/src/vite-env.d.ts new file mode 100644 index 000000000..bf58c5041 --- /dev/null +++ b/apps/docs/src/vite-env.d.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/// <reference types="vite/client" /> + +declare module '@pagefind/default-ui' { + export class PagefindUI { + constructor(options: { + element: string | HTMLElement; + bundlePath?: string; + baseUrl?: string; + showSubResults?: boolean; + pageSize?: number; + }); + } +} diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 000000000..aa4f9acf1 --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/apps/docs/tsconfig.node.json b/apps/docs/tsconfig.node.json new file mode 100644 index 000000000..f5537e20e --- /dev/null +++ b/apps/docs/tsconfig.node.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "target": "ES2022", + "lib": ["ES2022"], + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "strict": true, + "types": ["node"] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "scripts/**/*.mjs", + "src/normalizeBasePath.ts" + ] +} diff --git a/apps/docs/vite.config.ts b/apps/docs/vite.config.ts new file mode 100644 index 000000000..895b42858 --- /dev/null +++ b/apps/docs/vite.config.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { fileURLToPath, URL } from 'node:url'; + +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +import { normalizeBasePath } from './src/normalizeBasePath'; + +export default defineConfig(({ isSsrBuild }) => { + const parsedDocsPort = Number.parseInt(process.env.DOCS_PORT || '', 10); + const docsPort = + Number.isFinite(parsedDocsPort) && parsedDocsPort > 0 + ? parsedDocsPort + : 43127; + + return { + base: normalizeBasePath(process.env.DOCS_BASE_PATH), + plugins: [react()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + server: { + host: true, + port: docsPort, + strictPort: true, + }, + preview: { + host: true, + port: 43128, + strictPort: true, + }, + build: { + manifest: !isSsrBuild, + sourcemap: false, + }, + }; +}); diff --git a/apps/docs/vitest.config.ts b/apps/docs/vitest.config.ts new file mode 100644 index 000000000..54c74483d --- /dev/null +++ b/apps/docs/vitest.config.ts @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { defineConfig, mergeConfig } from 'vitest/config'; + +import viteConfig from './vite.config'; + +export default defineConfig(async (environment) => { + const resolved = + typeof viteConfig === 'function' + ? await viteConfig(environment) + : viteConfig; + + return mergeConfig(resolved, { + test: { + environment: 'happy-dom', + }, + }); +}); diff --git a/assets/huabu-logo.svg b/assets/huabu-logo.svg new file mode 100644 index 000000000..786a92514 --- /dev/null +++ b/assets/huabu-logo.svg @@ -0,0 +1,15 @@ +<!-- + Copyright (c) Microsoft Corporation. + Licensed under the MIT license. +--> +<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect width="120" height="120" rx="15.2727" fill="white" /> + <path d="M43.2866 37.9793C42.6598 37.1909 41.7168 36.6647 40.696 36.4958C39.6733 36.3281 38.6565 36.5315 37.8384 37.0819C37.0202 37.6324 36.4589 38.4907 36.247 39.489C36.0369 40.486 36.1936 41.5411 36.7134 42.4015C37.0505 42.9683 37.4302 43.6077 37.7798 44.207C40.6174 49.0677 43.3315 54.0021 45.8503 58.9738C47.9521 63.2599 46.1153 68.0076 42.3121 72.1522C42.1362 72.3381 41.9438 72.5356 41.7656 72.7068C41.6718 72.7979 41.5921 72.8707 41.4996 72.9555C41.4384 73.0073 41.2802 73.1548 41.346 73.0713C40.0063 74.2377 39.2406 75.8701 39.2659 77.6576C39.2884 79.4423 40.1 81.2357 41.4735 82.5953C42.847 83.9548 44.6559 84.7552 46.4537 84.7723C48.2544 84.7922 49.8965 84.0272 51.0674 82.6938C51.9065 81.7648 52.3191 81.178 52.8555 80.4322C57.8094 73.7927 60.8074 62.3376 54.8801 53.8622C51.5952 49.0168 48.1888 44.3443 44.647 39.7307C44.2036 39.1553 43.751 38.5701 43.2866 37.9793Z" fill="#484848"/> + <path d="M77.1812 81.7873C77.808 82.5757 78.751 83.1019 79.7718 83.2708C80.7944 83.4385 81.8113 83.2351 82.6294 82.6847C83.4476 82.1342 84.0089 81.2759 84.2208 80.2777C84.4308 79.2806 84.2741 78.2255 83.7543 77.3651C83.4172 76.7983 83.0376 76.1589 82.688 75.5596C79.8504 70.6989 77.1363 65.7645 74.6175 60.7928C72.5157 56.5067 74.3525 51.759 78.1557 47.6144C78.3316 47.4285 78.524 47.231 78.7022 47.0598C78.796 46.9687 78.8756 46.8959 78.9682 46.8111C79.0293 46.7593 79.1875 46.6118 79.1217 46.6953C80.4615 45.5289 81.2272 43.8965 81.2019 42.109C81.1794 40.3243 80.3678 38.5309 78.9943 37.1713C77.6208 35.8118 75.8118 35.0114 74.014 34.9943C72.2134 34.9744 70.5713 35.7394 69.4004 37.0728C68.5613 38.0018 68.1487 38.5886 67.6123 39.3345C62.6584 45.9739 59.6604 57.429 65.5877 65.9044C68.8726 70.7498 72.279 75.4223 75.8208 80.0359C76.2642 80.6113 76.7167 81.1965 77.1812 81.7873Z" fill="#484848"/> + <path d="M64 55C64 55 63.2598 58.3417 60.6667 58.9121C57.9176 59.5167 54 56.8056 54 56.8056" stroke="#484848" stroke-width="4"/> + <path d="M56 64C56 64 56.7402 60.6583 59.3333 60.0879C62.0824 59.4833 66 62.1944 66 62.1944" stroke="#484848" stroke-width="4"/> + <circle cx="21" cy="60" r="11.8309" fill="#00A4EF" stroke="#D9D9D9" stroke-width="0.338261" /> + <circle cx="60" cy="21" r="11.8309" fill="#F25022" stroke="#D9D9D9" stroke-width="0.338261" /> + <circle cx="60" cy="99" r="11.8309" fill="#FFB900" stroke="#D9D9D9" stroke-width="0.338261" /> + <circle cx="99" cy="60" r="11.8309" fill="#7FBA00" stroke="#D9D9D9" stroke-width="0.338261" /> +</svg> diff --git a/demo.js b/demo.js deleted file mode 100644 index 882292050..000000000 --- a/demo.js +++ /dev/null @@ -1,11 +0,0 @@ -const express = require("express"); - -const app = express(); - -app.get("/ping", (req, res) => { - const host = req.query.host; - // 仅做字符串响应,不执行任何命令 - res.send(`You requested ping for: ${host}`); -}); - -app.listen(3000); \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..13f9035a9 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import importPlugin from 'eslint-plugin-import'; +import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import globals from 'globals'; +import typescriptEslint from 'typescript-eslint'; + +export default typescriptEslint.config( + { + ignores: ['**/dist/**', '**/node_modules/**', '**/.ssr/**', '**/*.min.*'], + }, + js.configs.recommended, + ...typescriptEslint.configs.recommended, + { + files: ['**/*.{ts,tsx}'], + plugins: { + import: importPlugin, + }, + rules: { + curly: 'error', + semi: 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + fixStyle: 'separate-type-imports', + }, + ], + '@typescript-eslint/no-import-type-side-effects': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + 'internal', + ['parent', 'sibling'], + 'index', + 'type', + ], + pathGroups: [ + { pattern: '@/**', group: 'internal', position: 'before' }, + ], + pathGroupsExcludedImportTypes: ['type'], + distinctGroup: true, + 'newlines-between': 'always', + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], + }, + }, + { + files: ['apps/*/src/**/*.{ts,tsx,js,jsx}'], + plugins: { + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + 'jsx-a11y': jsxA11yPlugin, + }, + languageOptions: { + globals: { + ...globals.browser, + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + 'react/react-in-jsx-scope': 'off', + 'react/jsx-uses-react': 'off', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + }, + }, + { + files: [ + '*.config.{js,mjs,cjs}', + 'apps/*/*.config.{js,mjs,cjs}', + 'apps/*/scripts/**/*.{js,mjs,cjs}', + 'packages/*/scripts/**/*.{js,mjs,cjs}', + 'scripts/**/*.{js,mjs,cjs}', + ], + languageOptions: { + globals: { + ...globals.node, + }, + }, + }, + eslintConfigPrettier, +); diff --git a/package.json b/package.json new file mode 100644 index 000000000..cf4f0927d --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "huabu", + "private": true, + "scripts": { + "build": "pnpm -r --if-present run build", + "test": "pnpm -r --if-present run test", + "typecheck": "pnpm -r --if-present run typecheck", + "lint": "eslint . --report-unused-disable-directives --max-warnings 0", + "lint:fix": "pnpm run lint --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", + "check": "pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run test && pnpm run build && node scripts/check-headers.mjs" + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", + "globals": "^17.2.0", + "prettier": "3.8.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "typescript-eslint": "^8.54.0" + }, + "packageManager": "pnpm@10.34.3" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..21cffb776 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6202 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + devDependencies: + '@eslint/js': + specifier: ^9.39.2 + version: 9.39.4 + eslint: + specifier: ^9.39.2 + version: 9.39.4(jiti@2.7.0) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: + specifier: ^2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-jsx-a11y: + specifier: ^6.10.2 + version: 6.10.2(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.1.1(eslint@9.39.4(jiti@2.7.0)) + globals: + specifier: ^17.2.0 + version: 17.7.0 + prettier: + specifier: 3.8.1 + version: 3.8.1 + prettier-plugin-tailwindcss: + specifier: ^0.7.2 + version: 0.7.4(prettier@3.8.1) + typescript-eslint: + specifier: ^8.54.0 + version: 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + + apps/docs: + dependencies: + '@pagefind/default-ui': + specifier: ^1.4.0 + version: 1.5.2 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.577.0 + version: 0.577.0(react@18.3.1) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-router: + specifier: ^7.13.1 + version: 7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom: + specifier: ^7.13.1 + version: 7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: ^3.5.0 + version: 3.6.0 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.3.2 + '@types/node': + specifier: ^22.15.0 + version: 22.20.1 + '@types/react': + specifier: ^18.2.15 + version: 18.3.31 + '@types/react-dom': + specifier: ^18.2.7 + version: 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + happy-dom: + specifier: ^15.11.7 + version: 15.11.7 + pagefind: + specifier: ^1.4.0 + version: 1.5.2 + postcss: + specifier: ^8.5.6 + version: 8.5.16 + tailwindcss: + specifier: ^4.1.18 + version: 4.3.2 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^6.0.0 + version: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + vitest: + specifier: ^4.0.18 + version: 4.1.10(@types/node@22.20.1)(happy-dom@15.11.7)(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + +packages: + '@alloc/quick-lru@5.2.0': + resolution: + { + integrity: sha1-e/aLIMCjUPk2kV/K4G9Y4yAHzjA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@alloc/quick-lru/-/quick-lru-5.2.0.tgz, + } + engines: { node: '>=10' } + + '@babel/code-frame@7.29.7': + resolution: + { + integrity: sha1-8vu/6ofESiFZDsUVt3iywm2IZuc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/code-frame/-/code-frame-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/compat-data@7.29.7': + resolution: + { + integrity: sha1-bwI38PNtLlHAVwpjb67Z0tDv5ik=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/compat-data/-/compat-data-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/core@7.29.7': + resolution: + { + integrity: sha1-gMELFySAgpaLV6hXuRZAlx8gcPc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/core/-/core-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/generator@7.29.7': + resolution: + { + integrity: sha1-zKC4gn5rzzuhdniOfzsYCtbbL6M=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/generator/-/generator-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-compilation-targets@7.29.7': + resolution: + { + integrity: sha1-eh3vcEMCQBxH9k+oVYnpdK4hcEI=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-globals@7.29.7': + resolution: + { + integrity: sha1-8EqW+9hHMkGxB5JD9bPwOjAQq3s=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-globals/-/helper-globals-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-imports@7.29.7': + resolution: + { + integrity: sha1-7yUEilGOgo1zk/rFiC3dc5Idc5Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-transforms@7.29.7': + resolution: + { + integrity: sha1-sGJ0elmXuhOGNyATKLv/d5YFdK4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: + { + integrity: sha1-wKB2bxoTYX2KF0B9erj51IYiXqQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-string-parser@7.29.7': + resolution: + { + integrity: sha1-fwhx2Zgk0jE31g+G/PYTD9WhtR8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-identifier@7.29.7': + resolution: + { + integrity: sha1-vYcITO0MeW7Ea9pJLeboPSnon8I=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-option@7.29.7': + resolution: + { + integrity: sha1-zzFb6UAhOzVOtKvMC9Aevj9zvCo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/helpers@7.29.7': + resolution: + { + integrity: sha1-Rav951SJl+NDdsPmn+tHXP+0pgc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helpers/-/helpers-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/parser@7.29.7': + resolution: + { + integrity: sha1-g3uHOHy/XsVTDLY0s8Yi9o7bkzQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/parser/-/parser-7.29.7.tgz, + } + engines: { node: '>=6.0.0' } + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: + { + integrity: sha1-wkQkUnhYIgYk/Vmlseq0+kE8gDo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: + { + integrity: sha1-XPJaNomQa1ji8KLys3R4nmYnsV8=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.29.7': + resolution: + { + integrity: sha1-TZ1ABPZFzdME3pWMclFieE7KxwA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/template/-/template-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/traverse@7.29.7': + resolution: + { + integrity: sha1-xHsHpBuV2gkH0Ca13YlNmN59Ly0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/traverse/-/traverse-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@babel/types@7.29.7': + resolution: + { + integrity: sha1-gAXjHYJxLuetrvbiPGO3GmJ3CpI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/types/-/types-7.29.7.tgz, + } + engines: { node: '>=6.9.0' } + + '@esbuild/aix-ppc64@0.25.12': + resolution: + { + integrity: sha1-gPy+NhMOWLdnBRHoiLjoiiWe12w=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: + { + integrity: sha1-iqSWX40KeYLcIXNL9mATI6Ztp1I=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: + { + integrity: sha1-MAcSEB9/UPHSYnoWLm4JsQm2dno=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm/-/android-arm-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: + { + integrity: sha1-h9+ycWEgK9yVjvSLthsJx1j67hY=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-x64/-/android-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: + { + integrity: sha1-eRl4mOwf90XSHAceHHzDyALwwf0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: + { + integrity: sha1-FGQAqFYhM/RcTS6tzzfd0JcYB54=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: + { + integrity: sha1-HF+bpyBuFY/SskxZ+i0si7R8oP4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: + { + integrity: sha1-6mMfSja+qsS5J5+g/MbKKerusrM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: + { + integrity: sha1-4QZrzlg5TxsRQd7shVel8KIvWXc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: + { + integrity: sha1-RSzWayCTLQi9xTqLYcDjC69DSLk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: + { + integrity: sha1-sk+KzEW89UGSx/LzvhtT5lUer+A=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: + { + integrity: sha1-+c//p/yDIlcfvEyLMmjK8VvYGtA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: + { + integrity: sha1-V1oUvXRkT/q4ka3H1+YNJ1KW8s0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: + { + integrity: sha1-dbmccKlfvV93OddpK+/mBgFZGGk=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: + { + integrity: sha1-LjJZRAMhpE553fdTXDJQV9qHXNY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: + { + integrity: sha1-F2dsq7/lko2lsqDW311YzQjbJmM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: + { + integrity: sha1-BYN3VoXKggZtBMNQfwlSTTzXowY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: + { + integrity: sha1-8ExAScsuJS/paxb+2Q9wdGsT9KQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: + { + integrity: sha1-d9oNCg2CbXySHuo9QCklSLJYoHY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: + { + integrity: sha1-Ypb1hnrt7yioGyKrIAnHhqlS3M0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: + { + integrity: sha1-+NIzAzYOJ7Fs8GWyO7/0PBQUJnk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: + { + integrity: sha1-SeC3aHRKOSS+DX/ZfdbOmykj2I0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: + { + integrity: sha1-pu19Z3jWflKMgfsWWyP0kRubE9Y=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: + { + integrity: sha1-msFMN44bZTrxfQjn0840yu9YcyM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: + { + integrity: sha1-kYlC3LuzXMFPyjmvuRteaj0Scmc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: + { + integrity: sha1-m9rYF2vngRrRSNH4dyNZBB9GxsU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz, + } + engines: { node: '>=18' } + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: + { + integrity: sha1-TpCvZ7xR3e5s3vUoTt9XLsN2tZU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: + { + integrity: sha1-vM32Fbz3tujbgw7AuNIcmiXeWXs=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint-community/regexpp/-/regexpp-4.12.2.tgz, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/config-array@0.21.2': + resolution: + { + integrity: sha1-8p4iBXrVMWzyODbO6aNMgf/8t+Y=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/config-array/-/config-array-0.21.2.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/config-helpers@0.4.2': + resolution: + { + integrity: sha1-G9AGzut+LlWyt3OrMY0wDhpmrto=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/config-helpers/-/config-helpers-0.4.2.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@0.17.0': + resolution: + { + integrity: sha1-dyJYIEE9lhdQnak0IZCiAZ54dhw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/core/-/core-0.17.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/eslintrc@3.3.5': + resolution: + { + integrity: sha1-wTF5PPwae5bySoPgqLvUuIFVjGA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/eslintrc/-/eslintrc-3.3.5.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/js@9.39.4': + resolution: + { + integrity: sha1-o/g7/G/ZvzOoU9+s0LSbOY61lsE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/js/-/js-9.39.4.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/object-schema@2.1.7': + resolution: + { + integrity: sha1-biEmoTR+hqTe34cG7Gf/jhB+u60=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/object-schema/-/object-schema-2.1.7.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/plugin-kit@0.4.1': + resolution: + { + integrity: sha1-l3nj/Zt+4zVxpXQ1z0M1oXlKbLI=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@humanfs/core@0.19.2': + resolution: + { + integrity: sha1-qCcsoDsqz0kmcCIrIyC2xCG/3mA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@humanfs/core/-/core-0.19.2.tgz, + } + engines: { node: '>=18.18.0' } + + '@humanfs/node@0.16.8': + resolution: + { + integrity: sha1-j4AMzME/T4zTEW4tnAqUk52j4+0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@humanfs/node/-/node-0.16.8.tgz, + } + engines: { node: '>=18.18.0' } + + '@humanfs/types@0.15.0': + resolution: + { + integrity: sha1-8qCfYgEjkLK/8/xvskjd7IwJoJA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@humanfs/types/-/types-0.15.0.tgz, + } + engines: { node: '>=18.18.0' } + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { + integrity: sha1-r1smkaIrRL6EewyoFkHF+2rQFyw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz, + } + engines: { node: '>=12.22' } + + '@humanwhocodes/retry@0.4.3': + resolution: + { + integrity: sha1-wrnS43TuYsWG062+qHGZsdenpro=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@humanwhocodes/retry/-/retry-0.4.3.tgz, + } + engines: { node: '>=18.18' } + + '@jridgewell/gen-mapping@0.3.13': + resolution: + { + integrity: sha1-Y0Khn0Q0dRjJPkOxrGnes8Rlah8=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz, + } + + '@jridgewell/remapping@2.3.5': + resolution: + { + integrity: sha1-N1xHbRlylHhRuh4Vro8SMEdEWqE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/remapping/-/remapping-2.3.5.tgz, + } + + '@jridgewell/resolve-uri@3.1.2': + resolution: + { + integrity: sha1-eg7mAfYPmaIMfHxf8MgDiMEYm9Y=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz, + } + engines: { node: '>=6.0.0' } + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: + { + integrity: sha1-aRKwDSxjHA0Vzhp6tXzWV/Ko+Lo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz, + } + + '@jridgewell/trace-mapping@0.3.31': + resolution: + { + integrity: sha1-2xXWeByTHzolGj2sOVAcmKYIL9A=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz, + } + + '@pagefind/darwin-arm64@1.5.2': + resolution: + { + integrity: sha1-llFS/8IrzNgpngZffPvGhpob/+A=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz, + } + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.5.2': + resolution: + { + integrity: sha1-tbm0dnPK97KAiRFU4qR1q8SZ+t0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz, + } + cpu: [x64] + os: [darwin] + + '@pagefind/default-ui@1.5.2': + resolution: + { + integrity: sha1-ZHPNLDSpS4IhxTNKX+8xTNSETDc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/default-ui/-/default-ui-1.5.2.tgz, + } + + '@pagefind/freebsd-x64@1.5.2': + resolution: + { + integrity: sha1-z24nnZOMI2hzG7ZVi/j2agyAomk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz, + } + cpu: [x64] + os: [freebsd] + + '@pagefind/linux-arm64@1.5.2': + resolution: + { + integrity: sha1-iyLPwaNMWQM8W9Zmdreobvug9fQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz, + } + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.5.2': + resolution: + { + integrity: sha1-pzPRwKnZBTEfafiGh3HAzEKQn+Q=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz, + } + cpu: [x64] + os: [linux] + + '@pagefind/windows-arm64@1.5.2': + resolution: + { + integrity: sha1-bWyzlaVhNqkrkcC9hm9/7DsLvnw=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz, + } + cpu: [arm64] + os: [win32] + + '@pagefind/windows-x64@1.5.2': + resolution: + { + integrity: sha1-kx/byfAPcgV5UM0mDvll1P0CqS4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz, + } + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: + { + integrity: sha1-R9K/TO9tRwsi9YMbQg+JZOC/dV8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz, + } + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: + { + integrity: sha1-XphJtmHCIpz5Z6CNvi276ejJkeU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz, + } + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: + { + integrity: sha1-WwaZ7l3UhLIiye10r/Q8keqLF/g=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz, + } + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: + { + integrity: sha1-i8UsnXo86NBTPDUanJNd54HaoG8=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz, + } + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: + { + integrity: sha1-ui7z6PsxDwrzVYjycM+lqpbkh2Q=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz, + } + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: + { + integrity: sha1-k7EL2/6K2iJri8DALva39URHTZY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz, + } + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: + { + integrity: sha1-PoqjjvPJwwCUaHHj/bsMMOCiD4Y=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz, + } + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: + { + integrity: sha1-HXmUOEuwrRvEGSG1BuFkLU+df8M=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz, + } + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: + { + integrity: sha1-plQPR8+ESla4DKn/ldKs37LO+Xs=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz, + } + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: + { + integrity: sha1-QE8gRWUYQMv0jakbptD0kPC8LL8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz, + } + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: + { + integrity: sha1-o0BP/d97R0tIyZuciTtiR7t2W6U=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz, + } + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: + { + integrity: sha1-6KrG1Umzd5ReNJiC8Zm3yOt1yjg=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz, + } + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: + { + integrity: sha1-bi5E6lAxCzpYIHipFeX+uHnIINQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz, + } + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: + { + integrity: sha1-aJgwLabXegU3zeZLK0xrYGWb0RA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz, + } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: + { + integrity: sha1-MzcXyV3Vpmvvj2Pn74qf2EX9GNA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz, + } + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: + { + integrity: sha1-gbwGujgDUgBNAfSCbrfNzO+gW60=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz, + } + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: + { + integrity: sha1-lafNOd4hOJrWeIpShOqqc44pykw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz, + } + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: + { + integrity: sha1-BubbLsG8SLU3THkj74PC6wJLJFI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz, + } + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: + { + integrity: sha1-XcgYmIKF4J6IeQxkYt73JBPfLaM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz, + } + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: + { + integrity: sha1-IID0qTNJ6a/TS+b8GjfgH8i/yA8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz, + } + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: + { + integrity: sha1-IdZKistmIhckuSPlGvUzPfGvBEs=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz, + } + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: + { + integrity: sha1-jg/NnQIUHjN7TFtc/1dsuadrG6A=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz, + } + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: + { + integrity: sha1-vbTMTv1Y7+gIIDNH8PVGPw6hblI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz, + } + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: + { + integrity: sha1-26695a/STq4O7+kV2QFjLny1mGA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz, + } + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: + { + integrity: sha1-hBCehf6l+PE1NJn5ZXj9wqDosTg=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz, + } + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: + { + integrity: sha1-NnHOP5uSjVwB+Hl5LVwLYK4U1K0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz, + } + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: + { + integrity: sha1-kn3S+um8M2FAOsLHoAwy3c6a1+g=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rtsao/scc/-/scc-1.1.0.tgz, + } + + '@standard-schema/spec@1.1.0': + resolution: + { + integrity: sha1-p5tV26+GBIEvUtFAssmrQbwVC7g=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@standard-schema/spec/-/spec-1.1.0.tgz, + } + + '@tailwindcss/node@4.3.2': + resolution: + { + integrity: sha1-K6VjsF/2YrkXLJZF14pe3Vn5brM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/node/-/node-4.3.2.tgz, + } + + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: + { + integrity: sha1-rdT466JlsgsUY0oicXYhozUM1tQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: + { + integrity: sha1-JDm3yZFnnABtFu0AJkxg1UBWdVQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: + { + integrity: sha1-AW/7s3W7X+q6sP1Cx0SzzyFkeWg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: + { + integrity: sha1-b+ESsQP/ZZZxSDKTxhyFNlFjXuA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: + { + integrity: sha1-MP/7SVrFnqAQRu5Iwr+iR8pu2l4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: + { + integrity: sha1-kQ9HVOmqj4sEwBi+urYnpSvVaLk=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: + { + integrity: sha1-vbmGG7UGQgnsX/EBr/9tBG+xXs0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: + { + integrity: sha1-QkJ2KHYQYHMDo19S8qFyuVr0iYo=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: + { + integrity: sha1-YG1dwv19LozdfR8cSh9NVjkFbgU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: + { + integrity: sha1-9dTtK80SUHIXzBqS5PxQf9rG3Yw=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz, + } + engines: { node: '>=14.0.0' } + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: + { + integrity: sha1-pOx2EIP+9VxTDiBPrUPtumKeob8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: + { + integrity: sha1-OL8OOHjRev7bAmB1i2YZc7LZ3nA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz, + } + engines: { node: '>= 20' } + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.2': + resolution: + { + integrity: sha1-ghV/sNW+vxGII0hVxbfcdU2lQGU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/oxide/-/oxide-4.3.2.tgz, + } + engines: { node: '>= 20' } + + '@tailwindcss/postcss@4.3.2': + resolution: + { + integrity: sha1-hUW8NrVanKIM5wrgDeVYLpF6xcQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tailwindcss/postcss/-/postcss-4.3.2.tgz, + } + + '@types/babel__core@7.20.5': + resolution: + { + integrity: sha1-PfFfJ7qFMZyqB7oI0HIYibs5wBc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__core/-/babel__core-7.20.5.tgz, + } + + '@types/babel__generator@7.27.0': + resolution: + { + integrity: sha1-tYGSlMUReZV6+uw0FEL5NB5BCKk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__generator/-/babel__generator-7.27.0.tgz, + } + + '@types/babel__template@7.4.4': + resolution: + { + integrity: sha1-VnJRNwHBshmbxtrWNqnXSRWGdm8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__template/-/babel__template-7.4.4.tgz, + } + + '@types/babel__traverse@7.28.0': + resolution: + { + integrity: sha1-B9cT1szg0mXJhJ2wy+YtP2Hzb3Q=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__traverse/-/babel__traverse-7.28.0.tgz, + } + + '@types/chai@5.2.3': + resolution: + { + integrity: sha1-jpzZ4cNYH6azQaWu1ViOsoW+C0o=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/chai/-/chai-5.2.3.tgz, + } + + '@types/deep-eql@4.0.2': + resolution: + { + integrity: sha1-M0MRlx06BxIefrkbaEpgXn7qnL0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/deep-eql/-/deep-eql-4.0.2.tgz, + } + + '@types/estree@1.0.9': + resolution: + { + integrity: sha1-zz8Oh2177hWpOrkluCv1cKOQSiQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/estree/-/estree-1.0.9.tgz, + } + + '@types/json-schema@7.0.15': + resolution: + { + integrity: sha1-WWoXRyM2lNUPatinhp/Lb1bPWEE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/json-schema/-/json-schema-7.0.15.tgz, + } + + '@types/json5@0.0.29': + resolution: + { + integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/json5/-/json5-0.0.29.tgz, + } + + '@types/node@22.20.1': + resolution: + { + integrity: sha1-hOfN9jzaogwTSqMXzMkBqiHhbw4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/node/-/node-22.20.1.tgz, + } + + '@types/prop-types@15.7.15': + resolution: + { + integrity: sha1-5uWobWAr6spxzlFj+t9fldcJMcc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/prop-types/-/prop-types-15.7.15.tgz, + } + + '@types/react-dom@18.3.7': + resolution: + { + integrity: sha1-uJ3fLNg7T+r8xOLqQa/fuVoNGU8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react-dom/-/react-dom-18.3.7.tgz, + } + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: + { + integrity: sha1-teleKP/M6rjZgvM/LrB24XZTwqQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react/-/react-18.3.31.tgz, + } + + '@typescript-eslint/eslint-plugin@8.63.0': + resolution: + { + integrity: sha1-DYXQ7BoosONfSEzIIgqL8IQBnnE=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@typescript-eslint/parser': ^8.63.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.63.0': + resolution: + { + integrity: sha1-KZMzjDeZA+avxyw1Mq5uFblzNNQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/parser/-/parser-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.63.0': + resolution: + { + integrity: sha1-AaPQVQqGASdESpk5dJq0NFkc1xo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/project-service/-/project-service-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.63.0': + resolution: + { + integrity: sha1-yc9+zSNPfsNG9i5cfSjfr01Qe00=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/tsconfig-utils@8.63.0': + resolution: + { + integrity: sha1-9+HPmgKbtx9AJ/+kGUuoKvtWTNM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.63.0': + resolution: + { + integrity: sha1-nADzYhQBhsWI2oaz4QwhKAC2S4U=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.63.0': + resolution: + { + integrity: sha1-azKwpZE1IFVNgamGrP/7otMraWM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/types/-/types-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/typescript-estree@8.63.0': + resolution: + { + integrity: sha1-pvnJzSkOmCA60phQsNclKdyDvnM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.63.0': + resolution: + { + integrity: sha1-tqbIr/HOvR3kQQs6Qrfsm6ZwPyM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/utils/-/utils-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.63.0': + resolution: + { + integrity: sha1-KFOo4ztS8jVwM4+WzInLIj2qvUQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@vitejs/plugin-react@4.7.0': + resolution: + { + integrity: sha1-ZHr057t1rTrdV452KtmEuQ9KJLk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz, + } + engines: { node: ^14.18.0 || >=16.0.0 } + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@4.1.10': + resolution: + { + integrity: sha1-eZwG/ES7DPfieEE3tifFzBcyhdQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/expect/-/expect-4.1.10.tgz, + } + + '@vitest/mocker@4.1.10': + resolution: + { + integrity: sha1-JBOYerTNf6HCthS0BMQHv2rR6tE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/mocker/-/mocker-4.1.10.tgz, + } + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.10': + resolution: + { + integrity: sha1-dVQucnOgjMEP1Nja1OPrHxbNlYw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/pretty-format/-/pretty-format-4.1.10.tgz, + } + + '@vitest/runner@4.1.10': + resolution: + { + integrity: sha1-/r8KIakWhCFCLRlVNw5gb+q2A1U=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/runner/-/runner-4.1.10.tgz, + } + + '@vitest/snapshot@4.1.10': + resolution: + { + integrity: sha1-fj6f7H1NRyMuSTz9y9IXDeQ3HAQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/snapshot/-/snapshot-4.1.10.tgz, + } + + '@vitest/spy@4.1.10': + resolution: + { + integrity: sha1-XAv6l7Vrup43QDyXbbd2/2q1b2U=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/spy/-/spy-4.1.10.tgz, + } + + '@vitest/utils@4.1.10': + resolution: + { + integrity: sha1-/8cQVfGL/Msf0FhjZevCgkiS5AM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitest/utils/-/utils-4.1.10.tgz, + } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/acorn-jsx/-/acorn-jsx-5.3.2.tgz, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: + { + integrity: sha1-F4WtuE+vjYrdEDabk4Jvwr0I8f4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/acorn/-/acorn-8.17.0.tgz, + } + engines: { node: '>=0.4.0' } + hasBin: true + + ajv@6.15.0: + resolution: + { + integrity: sha1-B+mCx0YmFnqnoklcU4F4ktcTlJI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ajv/-/ajv-6.15.0.tgz, + } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha1-7dgDYornHATIWuegkG7a00tkiTc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ansi-styles/-/ansi-styles-4.3.0.tgz, + } + engines: { node: '>=8' } + + argparse@2.0.1: + resolution: + { + integrity: sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/argparse/-/argparse-2.0.1.tgz, + } + + aria-query@5.3.2: + resolution: + { + integrity: sha1-k/gaQ0gOM6M48ZFjo9EKUMAdzVk=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/aria-query/-/aria-query-5.3.2.tgz, + } + engines: { node: '>= 0.4' } + + array-buffer-byte-length@1.0.2: + resolution: + { + integrity: sha1-OE0So3KVrsN2mrAirTI6GKUcz4s=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + array-includes@3.1.9: + resolution: + { + integrity: sha1-HwzKoI6Qzbw+tDMhD5A60PF8Pzo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array-includes/-/array-includes-3.1.9.tgz, + } + engines: { node: '>= 0.4' } + + array.prototype.findlast@1.2.5: + resolution: + { + integrity: sha1-Pk+8swoVp/W/ZM8vquItE5wuSQQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz, + } + engines: { node: '>= 0.4' } + + array.prototype.findlastindex@1.2.6: + resolution: + { + integrity: sha1-z6EGXIHctk40VXybgdAS9qQhxWQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz, + } + engines: { node: '>= 0.4' } + + array.prototype.flat@1.3.3: + resolution: + { + integrity: sha1-U0qvnm6N15+2uamRf4Oe8exjr+U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz, + } + engines: { node: '>= 0.4' } + + array.prototype.flatmap@1.3.3: + resolution: + { + integrity: sha1-cSzHkq5wNwrkBYYmRinjOqtd04s=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz, + } + engines: { node: '>= 0.4' } + + array.prototype.tosorted@1.1.4: + resolution: + { + integrity: sha1-/pVGeP9TA05xfqM1KgPwsLhvf/w=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz, + } + engines: { node: '>= 0.4' } + + arraybuffer.prototype.slice@1.0.4: + resolution: + { + integrity: sha1-nXYNhNvdBtDL+SyISWFaGnqzGDw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz, + } + engines: { node: '>= 0.4' } + + assertion-error@2.0.1: + resolution: + { + integrity: sha1-9kGhlrM1aQsQcL8AtudZP+wZC/c=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/assertion-error/-/assertion-error-2.0.1.tgz, + } + engines: { node: '>=12' } + + ast-types-flow@0.0.8: + resolution: + { + integrity: sha1-CoXhySaVdprBOkKLtlPnU4vqJ9Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ast-types-flow/-/ast-types-flow-0.0.8.tgz, + } + + async-function@1.0.0: + resolution: + { + integrity: sha1-UJyfymDq+FA0xoKYOBiOTkyP+ys=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/async-function/-/async-function-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + available-typed-arrays@1.0.7: + resolution: + { + integrity: sha1-pcw3XWoDwu/IelU/PgsVIt7xSEY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz, + } + engines: { node: '>= 0.4' } + + axe-core@4.12.1: + resolution: + { + integrity: sha1-af4r9t/Asklzr5Gx2OlF954bfEQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/axe-core/-/axe-core-4.12.1.tgz, + } + engines: { node: '>=4' } + + axobject-query@4.1.0: + resolution: + { + integrity: sha1-KHaMdtDjz/IbxiqeLQtqwwBCoe4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/axobject-query/-/axobject-query-4.1.0.tgz, + } + engines: { node: '>= 0.4' } + + balanced-match@1.0.2: + resolution: + { + integrity: sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/balanced-match/-/balanced-match-1.0.2.tgz, + } + + balanced-match@4.0.4: + resolution: + { + integrity: sha1-v7EGYv7tgZaixi58aOF3IMJ0F5o=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/balanced-match/-/balanced-match-4.0.4.tgz, + } + engines: { node: 18 || 20 || >=22 } + + baseline-browser-mapping@2.10.42: + resolution: + { + integrity: sha1-GV3MdrqiaaSX8LB97Kzhaf7prFg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz, + } + engines: { node: '>=6.0.0' } + hasBin: true + + brace-expansion@1.1.16: + resolution: + { + integrity: sha1-cj06MMBVjCJavJ/Eeac+FOJsPC8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/brace-expansion/-/brace-expansion-1.1.16.tgz, + } + + brace-expansion@5.0.7: + resolution: + { + integrity: sha1-Gw5GlltHna1lr3N7SgJ5CgVJgzc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/brace-expansion/-/brace-expansion-5.0.7.tgz, + } + engines: { node: 18 || 20 || >=22 } + + browserslist@4.28.5: + resolution: + { + integrity: sha1-Q4t9OMDUtHdAu7NneNW9ygGzeDg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/browserslist/-/browserslist-4.28.5.tgz, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: + { + integrity: sha1-S1QowiK+mF15w9gmV0edvgtZstY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + call-bind@1.0.9: + resolution: + { + integrity: sha1-OaZEcAyAvH0MqRAvxtHUOy/X7uc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/call-bind/-/call-bind-1.0.9.tgz, + } + engines: { node: '>= 0.4' } + + call-bound@1.0.4: + resolution: + { + integrity: sha1-I43pNdKippKSjFOMfM+pEGf9Bio=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/call-bound/-/call-bound-1.0.4.tgz, + } + engines: { node: '>= 0.4' } + + callsites@3.1.0: + resolution: + { + integrity: sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/callsites/-/callsites-3.1.0.tgz, + } + engines: { node: '>=6' } + + caniuse-lite@1.0.30001803: + resolution: + { + integrity: sha1-sqXWluBCvIME3NSULDn+Mw+7yyQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz, + } + + chai@6.2.2: + resolution: + { + integrity: sha1-rkG1LJrKh3NFBTYnF/MlX6zaNg4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/chai/-/chai-6.2.2.tgz, + } + engines: { node: '>=18' } + + chalk@4.1.2: + resolution: + { + integrity: sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/chalk/-/chalk-4.1.2.tgz, + } + engines: { node: '>=10' } + + clsx@2.1.1: + resolution: + { + integrity: sha1-7tOXyf2L2IK/sY3qtxAgSaLzKZk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/clsx/-/clsx-2.1.1.tgz, + } + engines: { node: '>=6' } + + color-convert@2.0.1: + resolution: + { + integrity: sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/color-convert/-/color-convert-2.0.1.tgz, + } + engines: { node: '>=7.0.0' } + + color-name@1.1.4: + resolution: + { + integrity: sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/color-name/-/color-name-1.1.4.tgz, + } + + concat-map@0.0.1: + resolution: + { + integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/concat-map/-/concat-map-0.0.1.tgz, + } + + convert-source-map@2.0.0: + resolution: + { + integrity: sha1-S1YPZJ/E6RjdCrdc9JYei8iC2Co=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/convert-source-map/-/convert-source-map-2.0.0.tgz, + } + + cookie@1.1.1: + resolution: + { + integrity: sha1-O7m9/II2nbnC9pyTycPOsxDIizw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cookie/-/cookie-1.1.1.tgz, + } + engines: { node: '>=18' } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha1-ilj+ePANzXDDcEUXWd+/rwPo7p8=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cross-spawn/-/cross-spawn-7.0.6.tgz, + } + engines: { node: '>= 8' } + + csstype@3.2.3: + resolution: + { + integrity: sha1-7EjA8+mT5QZIyG2lWeJhCZXPmJo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/csstype/-/csstype-3.2.3.tgz, + } + + damerau-levenshtein@1.0.8: + resolution: + { + integrity: sha1-tD0obMvTa8Wy9+1ByvLQq6H4puc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz, + } + + data-view-buffer@1.0.2: + resolution: + { + integrity: sha1-IRoDupXsr3eYqMcZjXlTYhH4hXA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/data-view-buffer/-/data-view-buffer-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + data-view-byte-length@1.0.2: + resolution: + { + integrity: sha1-noD3ylJFPOPpPSWjUxh2fqdwRzU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + data-view-byte-offset@1.0.1: + resolution: + { + integrity: sha1-BoMH+bcat2274QKROJ4CCFZgYZE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + debug@3.2.7: + resolution: + { + integrity: sha1-clgLfpFF+zm2Z2+cXl+xALk0F5o=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/debug/-/debug-3.2.7.tgz, + } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: + { + integrity: sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/debug/-/debug-4.4.3.tgz, + } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: + { + integrity: sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/deep-is/-/deep-is-0.1.4.tgz, + } + + define-data-property@1.1.4: + resolution: + { + integrity: sha1-iU3BQbt9MGCuQ2b2oBB+aPvkjF4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/define-data-property/-/define-data-property-1.1.4.tgz, + } + engines: { node: '>= 0.4' } + + define-properties@1.2.1: + resolution: + { + integrity: sha1-EHgcxhbrlRqAoDS6/Kpzd/avK2w=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/define-properties/-/define-properties-1.2.1.tgz, + } + engines: { node: '>= 0.4' } + + detect-libc@2.1.2: + resolution: + { + integrity: sha1-aJxdzcGQDvVYOky59te0c3QgdK0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/detect-libc/-/detect-libc-2.1.2.tgz, + } + engines: { node: '>=8' } + + doctrine@2.1.0: + resolution: + { + integrity: sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/doctrine/-/doctrine-2.1.0.tgz, + } + engines: { node: '>=0.10.0' } + + dunder-proto@1.0.1: + resolution: + { + integrity: sha1-165mfh3INIL4tw/Q9u78UNow9Yo=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dunder-proto/-/dunder-proto-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + electron-to-chromium@1.5.389: + resolution: + { + integrity: sha1-U4vp6+x4Am1Nq6a+Mhq4VN+sKo8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz, + } + + emoji-regex@9.2.2: + resolution: + { + integrity: sha1-hAyIA7DYBH9P8M+WMXazLU7z7XI=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/emoji-regex/-/emoji-regex-9.2.2.tgz, + } + + enhanced-resolve@5.21.6: + resolution: + { + integrity: sha1-qiB7Q89ljmqzugaJbtwAwTwxJ8Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz, + } + engines: { node: '>=10.13.0' } + + entities@4.5.0: + resolution: + { + integrity: sha1-XSaOpecRPsdMTQM7eepaNaSI+0g=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/entities/-/entities-4.5.0.tgz, + } + engines: { node: '>=0.12' } + + es-abstract-get@1.0.0: + resolution: + { + integrity: sha1-Hq6HEB9Cvt62p0DoxQUSca74kIg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-abstract-get/-/es-abstract-get-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + es-abstract@1.24.2: + resolution: + { + integrity: sha1-Lb04wYBzXumD93WFFAonBqlj7Zo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-abstract/-/es-abstract-1.24.2.tgz, + } + engines: { node: '>= 0.4' } + + es-define-property@1.0.1: + resolution: + { + integrity: sha1-mD6y+aZyTpMD9hrd8BHHLgngsPo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-define-property/-/es-define-property-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + es-errors@1.3.0: + resolution: + { + integrity: sha1-BfdaJdq5jk+x3NXhRywFRtUFfI8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-errors/-/es-errors-1.3.0.tgz, + } + engines: { node: '>= 0.4' } + + es-iterator-helpers@1.3.3: + resolution: + { + integrity: sha1-CiKdw4raBFC4z6qFgJ/XPbs0ETw=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz, + } + engines: { node: '>= 0.4' } + + es-module-lexer@2.3.0: + resolution: + { + integrity: sha1-/adwI0w0UGTBIuuQXhxCAP+kzn4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-module-lexer/-/es-module-lexer-2.3.0.tgz, + } + + es-object-atoms@1.1.2: + resolution: + { + integrity: sha1-otCzcyBXJN+lJdI7DD4bHKWCyZs=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-object-atoms/-/es-object-atoms-1.1.2.tgz, + } + engines: { node: '>= 0.4' } + + es-set-tostringtag@2.1.0: + resolution: + { + integrity: sha1-8x274MGDsAptJutjJcgQwP0YvU0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz, + } + engines: { node: '>= 0.4' } + + es-shim-unscopables@1.1.0: + resolution: + { + integrity: sha1-Q43zVSDaxdEF85Q9knVJ6jsA9LU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + es-to-primitive@1.3.4: + resolution: + { + integrity: sha1-DIVCkc8Ne0Oda55XceqDf/rx+ZE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-to-primitive/-/es-to-primitive-1.3.4.tgz, + } + engines: { node: '>= 0.4' } + + esbuild@0.25.12: + resolution: + { + integrity: sha1-l6HQQfSrAML84vg40rmWmi0ql6U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esbuild/-/esbuild-0.25.12.tgz, + } + engines: { node: '>=18' } + hasBin: true + + escalade@3.2.0: + resolution: + { + integrity: sha1-ARo/aYVroYnf+n3I/M6Z0qh5A+U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escalade/-/escalade-3.2.0.tgz, + } + engines: { node: '>=6' } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz, + } + engines: { node: '>=10' } + + eslint-config-prettier@10.1.8: + resolution: + { + integrity: sha1-FXNM5K+MJ3jMMvCwGzewtc0ey5c=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz, + } + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.10: + resolution: + { + integrity: sha1-hM4wBav8MAWIzyO7rBqr7B/G6ME=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz, + } + + eslint-module-utils@2.14.0: + resolution: + { + integrity: sha1-YR+OHGzrKak+uUnhzGcLgod2SBk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz, + } + engines: { node: '>=4' } + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: + { + integrity: sha1-YCtV+qbkyuql6XDBmLXACjdwiYA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz, + } + engines: { node: '>=4' } + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: + { + integrity: sha1-0oErsjvxq0Zl8XGOpELoNy5jhIM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz, + } + engines: { node: '>=4.0' } + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.1.1: + resolution: + { + integrity: sha1-5nQsrXXZcMCj8w19P6gKR4T1WSc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz, + } + engines: { node: '>=18' } + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: + { + integrity: sha1-KXVRFHK92hsnKzTXeTNcmw6HcGU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz, + } + engines: { node: '>=4' } + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: + { + integrity: sha1-iOZGogf61hQ2/6OetQUUcgBlXII=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-scope/-/eslint-scope-8.4.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha1-DNcv6FUOPC6uFWqWpN3c0cisWAA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: + { + integrity: sha1-TP6mD+fdCtjoFuHtAmwdUlG1EsE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@5.0.1: + resolution: + { + integrity: sha1-njyUiWl4JNLUzjqK0SYo+R6fWb4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@9.39.4: + resolution: + { + integrity: sha1-hV2hsuKtZtxZkRlfNeJivOyBF7U=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/eslint/-/eslint-9.39.4.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: + { + integrity: sha1-1U9JSdRikAWh+haNk3w/8ffiqDc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/espree/-/espree-10.4.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.7.0: + resolution: + { + integrity: sha1-CNBI8mHw3e21uulfRoCUY9nJSW0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esquery/-/esquery-1.7.0.tgz, + } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { + integrity: sha1-eteWTWeauyi+5yzsY3WLHF0smSE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esrecurse/-/esrecurse-4.3.0.tgz, + } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { + integrity: sha1-LupSkHAvJquP5TcDcP+GyWXSESM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/estraverse/-/estraverse-5.3.0.tgz, + } + engines: { node: '>=4.0' } + + estree-walker@3.0.3: + resolution: + { + integrity: sha1-Z8PlSexAKkh7T8GT0ZU6UkdSNA0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/estree-walker/-/estree-walker-3.0.3.tgz, + } + + esutils@2.0.3: + resolution: + { + integrity: sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esutils/-/esutils-2.0.3.tgz, + } + engines: { node: '>=0.10.0' } + + expect-type@1.4.0: + resolution: + { + integrity: sha1-JO338MxppE0AhWe6RZSrlvPDo9Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/expect-type/-/expect-type-1.4.0.tgz, + } + engines: { node: '>=12.0.0' } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz, + } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz, + } + + fdir@6.5.0: + resolution: + { + integrity: sha1-7Sq5Z6MxreYvGNB32uGSaE1Q01A=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fdir/-/fdir-6.5.0.tgz, + } + engines: { node: '>=12.0.0' } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: + { + integrity: sha1-d4e93PETG/+5JjbGlFe7wO3W2B8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/file-entry-cache/-/file-entry-cache-8.0.0.tgz, + } + engines: { node: '>=16.0.0' } + + find-up@5.0.0: + resolution: + { + integrity: sha1-TJKBnstwg1YeT0okCoa+UZj1Nvw=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/find-up/-/find-up-5.0.0.tgz, + } + engines: { node: '>=10' } + + flat-cache@4.0.1: + resolution: + { + integrity: sha1-Ds45/LFO4BL0sEEL0z3ZwfAREnw=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/flat-cache/-/flat-cache-4.0.1.tgz, + } + engines: { node: '>=16' } + + flatted@3.4.2: + resolution: + { + integrity: sha1-9cI8EH8PN96NvfJPE3IrO5jVJyY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/flatted/-/flatted-3.4.2.tgz, + } + + for-each@0.3.5: + resolution: + { + integrity: sha1-1lBogCeCaSD+6wr3R+57lCGkHUc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/for-each/-/for-each-0.3.5.tgz, + } + engines: { node: '>= 0.4' } + + fsevents@2.3.3: + resolution: + { + integrity: sha1-ysZAd4XQNnWipeGlMFxpezR9kNY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fsevents/-/fsevents-2.3.3.tgz, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { + integrity: sha1-LALYZNl/PqbIgwxGTL0Rq26rehw=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/function-bind/-/function-bind-1.1.2.tgz, + } + + function.prototype.name@1.2.0: + resolution: + { + integrity: sha1-dY8+hPpUJnJFS9XhTLCBpc4H9ww=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/function.prototype.name/-/function.prototype.name-1.2.0.tgz, + } + engines: { node: '>= 0.4' } + + functions-have-names@1.2.3: + resolution: + { + integrity: sha1-BAT+TuK6L2B/Dg7DyAuumUEzuDQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/functions-have-names/-/functions-have-names-1.2.3.tgz, + } + + generator-function@2.0.1: + resolution: + { + integrity: sha1-DnXdQQ0SQ2h6C6LpUblO7bj3N6I=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/generator-function/-/generator-function-2.0.1.tgz, + } + engines: { node: '>= 0.4' } + + gensync@1.0.0-beta.2: + resolution: + { + integrity: sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/gensync/-/gensync-1.0.0-beta.2.tgz, + } + engines: { node: '>=6.9.0' } + + get-intrinsic@1.3.0: + resolution: + { + integrity: sha1-dD8OO2lkqTpUke0b/6rgVNf5jQE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-intrinsic/-/get-intrinsic-1.3.0.tgz, + } + engines: { node: '>= 0.4' } + + get-proto@1.0.1: + resolution: + { + integrity: sha1-FQs/J0OGnvPoUewMSdFbHRTQDuE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-proto/-/get-proto-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + get-symbol-description@1.1.0: + resolution: + { + integrity: sha1-e91U4L7+j/yfO04gMiDZ8eiBtu4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-symbol-description/-/get-symbol-description-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + glob-parent@6.0.2: + resolution: + { + integrity: sha1-bSN9mQg5UMeSkPJMdkKj3poo+eM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/glob-parent/-/glob-parent-6.0.2.tgz, + } + engines: { node: '>=10.13.0' } + + globals@14.0.0: + resolution: + { + integrity: sha1-iY10E8Kbq89rr+Vvyt3thYrack4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/globals/-/globals-14.0.0.tgz, + } + engines: { node: '>=18' } + + globals@17.7.0: + resolution: + { + integrity: sha1-VT1VCQtN3oIJ7C2kJYDW5+fYsQ0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/globals/-/globals-17.7.0.tgz, + } + engines: { node: '>=18' } + + globalthis@1.0.4: + resolution: + { + integrity: sha1-dDDtOpddl7+1m8zkH1yruvplEjY=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/globalthis/-/globalthis-1.0.4.tgz, + } + engines: { node: '>= 0.4' } + + gopd@1.2.0: + resolution: + { + integrity: sha1-ifVrghe9vIgCvSmd9tfxCB1+UaE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/gopd/-/gopd-1.2.0.tgz, + } + engines: { node: '>= 0.4' } + + graceful-fs@4.2.11: + resolution: + { + integrity: sha1-QYPk6L8Iu24Fu7L30uDI9xLKQOM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/graceful-fs/-/graceful-fs-4.2.11.tgz, + } + + happy-dom@15.11.7: + resolution: + { + integrity: sha1-25hU8R5d0/1Ksgy7z9972eF82XE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/happy-dom/-/happy-dom-15.11.7.tgz, + } + engines: { node: '>=18.0.0' } + + has-bigints@1.1.0: + resolution: + { + integrity: sha1-KGB+llrJZ+A80qLHCiY2oe2tSf4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-bigints/-/has-bigints-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + has-flag@4.0.0: + resolution: + { + integrity: sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-flag/-/has-flag-4.0.0.tgz, + } + engines: { node: '>=8' } + + has-property-descriptors@1.0.2: + resolution: + { + integrity: sha1-lj7X0HHce/XwhMW/vg0bYiJYaFQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz, + } + + has-proto@1.2.0: + resolution: + { + integrity: sha1-XeWm6r2V/f/ZgYtDBV6AZeOf6dU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-proto/-/has-proto-1.2.0.tgz, + } + engines: { node: '>= 0.4' } + + has-symbols@1.1.0: + resolution: + { + integrity: sha1-/JxqeDoISVHQuXH+EBjegTcHozg=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-symbols/-/has-symbols-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + has-tostringtag@1.0.2: + resolution: + { + integrity: sha1-LNxC1AvvLltO6rfAGnPFTOerWrw=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-tostringtag/-/has-tostringtag-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + hasown@2.0.4: + resolution: + { + integrity: sha1-jGLYy5C+sqrV0KW2dYGtmFTD8AM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hasown/-/hasown-2.0.4.tgz, + } + engines: { node: '>= 0.4' } + + hermes-estree@0.25.1: + resolution: + { + integrity: sha1-au7BfRmDtOq/aXIfOqPrcFsX9IA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hermes-estree/-/hermes-estree-0.25.1.tgz, + } + + hermes-parser@0.25.1: + resolution: + { + integrity: sha1-W+Dkh7IJCIbGK9ihFyTNdm1fVNE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hermes-parser/-/hermes-parser-0.25.1.tgz, + } + + ignore@5.3.2: + resolution: + { + integrity: sha1-PNQOcp82Q/2HywTlC/DrcivFlvU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ignore/-/ignore-5.3.2.tgz, + } + engines: { node: '>= 4' } + + ignore@7.0.5: + resolution: + { + integrity: sha1-TLX2zX1MerA2VzjHrqiIuqbX79k=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ignore/-/ignore-7.0.5.tgz, + } + engines: { node: '>= 4' } + + import-fresh@3.3.1: + resolution: + { + integrity: sha1-nOy1ZQPAraHydB271lRuSxO1fM8=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/import-fresh/-/import-fresh-3.3.1.tgz, + } + engines: { node: '>=6' } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/imurmurhash/-/imurmurhash-0.1.4.tgz, + } + engines: { node: '>=0.8.19' } + + internal-slot@1.1.0: + resolution: + { + integrity: sha1-HqyRdilH0vcFa8g42T4TsulgSWE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/internal-slot/-/internal-slot-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + is-array-buffer@3.0.5: + resolution: + { + integrity: sha1-ZXQuHmh70sxmYlMGj9hwf+TUQoA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-array-buffer/-/is-array-buffer-3.0.5.tgz, + } + engines: { node: '>= 0.4' } + + is-async-function@2.1.1: + resolution: + { + integrity: sha1-PmkBjI4E5ztzh5PQIL/ohLn9NSM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-async-function/-/is-async-function-2.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-bigint@1.1.0: + resolution: + { + integrity: sha1-3aejRF31ekJYPbQihoLrp8QXBnI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-bigint/-/is-bigint-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + is-boolean-object@1.2.2: + resolution: + { + integrity: sha1-cGf0dwmAmjk8cf9bs+E12KkhXZ4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-boolean-object/-/is-boolean-object-1.2.2.tgz, + } + engines: { node: '>= 0.4' } + + is-callable@1.2.7: + resolution: + { + integrity: sha1-O8KoXqdC2eNiBdys3XLKH9xRsFU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-callable/-/is-callable-1.2.7.tgz, + } + engines: { node: '>= 0.4' } + + is-core-module@2.16.2: + resolution: + { + integrity: sha1-PgdFCoCA684/vwysSU9NKrMk4II=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-core-module/-/is-core-module-2.16.2.tgz, + } + engines: { node: '>= 0.4' } + + is-data-view@1.0.2: + resolution: + { + integrity: sha1-uuCkG5aImGwhiN2mZX5WuPnmO44=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-data-view/-/is-data-view-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + is-date-object@1.1.0: + resolution: + { + integrity: sha1-rYVUGZb8eqiycpcB0ntzGfldgvc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-date-object/-/is-date-object-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + is-document.all@1.0.0: + resolution: + { + integrity: sha1-FjpL+zYsbtexGM5GzezE433uMZU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-document.all/-/is-document.all-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + is-extglob@2.1.1: + resolution: + { + integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-extglob/-/is-extglob-2.1.1.tgz, + } + engines: { node: '>=0.10.0' } + + is-finalizationregistry@1.1.1: + resolution: + { + integrity: sha1-7v3NxslN3QZ02chYh7+T+USpfJA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-generator-function@1.1.2: + resolution: + { + integrity: sha1-rjth49XqTkg5uQutIrAjNQUaF9U=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-generator-function/-/is-generator-function-1.1.2.tgz, + } + engines: { node: '>= 0.4' } + + is-glob@4.0.3: + resolution: + { + integrity: sha1-ZPYeQsu7LuwgcanawLKLoeZdUIQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-glob/-/is-glob-4.0.3.tgz, + } + engines: { node: '>=0.10.0' } + + is-map@2.0.3: + resolution: + { + integrity: sha1-7elrf+HicLPERl46RlZYdkkm1i4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-map/-/is-map-2.0.3.tgz, + } + engines: { node: '>= 0.4' } + + is-negative-zero@2.0.3: + resolution: + { + integrity: sha1-ztkDoCespjgbd3pXQwadc3akl0c=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-negative-zero/-/is-negative-zero-2.0.3.tgz, + } + engines: { node: '>= 0.4' } + + is-number-object@1.1.1: + resolution: + { + integrity: sha1-FEsh6VobwUggXcwoFKkTTsQbJUE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-number-object/-/is-number-object-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-regex@1.2.1: + resolution: + { + integrity: sha1-dtcKPtEO+b5I61d4h9dCBb8MrSI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-regex/-/is-regex-1.2.1.tgz, + } + engines: { node: '>= 0.4' } + + is-set@2.0.3: + resolution: + { + integrity: sha1-irIJ6kJGCBQTct7W4MsgDvHZ0B0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-set/-/is-set-2.0.3.tgz, + } + engines: { node: '>= 0.4' } + + is-shared-array-buffer@1.0.4: + resolution: + { + integrity: sha1-m2eES9m38ka6BwjDqT40Jpx3T28=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz, + } + engines: { node: '>= 0.4' } + + is-string@1.1.1: + resolution: + { + integrity: sha1-kuo/PVxbbgOcqGd+WsjQfqdzy7k=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-string/-/is-string-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-symbol@1.1.1: + resolution: + { + integrity: sha1-9HdhJ59TLisFpwJKdQbbvtrNBjQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-symbol/-/is-symbol-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-typed-array@1.1.15: + resolution: + { + integrity: sha1-S/tKRbYc7oOlpG+6d45OjVnAzgs=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-typed-array/-/is-typed-array-1.1.15.tgz, + } + engines: { node: '>= 0.4' } + + is-weakmap@2.0.2: + resolution: + { + integrity: sha1-v3JhXWSd/l9pkHnFS4PkfRrhnP0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-weakmap/-/is-weakmap-2.0.2.tgz, + } + engines: { node: '>= 0.4' } + + is-weakref@1.1.1: + resolution: + { + integrity: sha1-7qQwGCvo1kF0vZa/+8RvIb8/kpM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-weakref/-/is-weakref-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + is-weakset@2.0.4: + resolution: + { + integrity: sha1-yfXesLwZBsbW8QJ/KE3fRZJJ2so=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-weakset/-/is-weakset-2.0.4.tgz, + } + engines: { node: '>= 0.4' } + + isarray@2.0.5: + resolution: + { + integrity: sha1-ivHkwSISRMxiRZ+vOJQNTmRKVyM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/isarray/-/isarray-2.0.5.tgz, + } + + isexe@2.0.0: + resolution: + { + integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/isexe/-/isexe-2.0.0.tgz, + } + + iterator.prototype@1.1.5: + resolution: + { + integrity: sha1-EslZop3jLeCqO7u4AfTXdwZtrjk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/iterator.prototype/-/iterator.prototype-1.1.5.tgz, + } + engines: { node: '>= 0.4' } + + jiti@2.7.0: + resolution: + { + integrity: sha1-l0Io8vTKK8IYhaF5e0X+po6VDGQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jiti/-/jiti-2.7.0.tgz, + } + hasBin: true + + js-tokens@4.0.0: + resolution: + { + integrity: sha1-GSA/tZmR35jjoocFDUZHzerzJJk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-tokens/-/js-tokens-4.0.0.tgz, + } + + js-yaml@4.3.0: + resolution: + { + integrity: sha1-0ZAFcqf3zwtfVAyDZz5gutNDZZI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-yaml/-/js-yaml-4.3.0.tgz, + } + hasBin: true + + jsesc@3.1.0: + resolution: + { + integrity: sha1-dNM1ojT2ftGZB/2t+sfM+dQJgl0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsesc/-/jsesc-3.1.0.tgz, + } + engines: { node: '>=6' } + hasBin: true + + json-buffer@3.0.1: + resolution: + { + integrity: sha1-kziAKjDTtmBfvgYT4JQAjKjAWhM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-buffer/-/json-buffer-3.0.1.tgz, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha1-afaofZUTq4u4/mO9sJecRI5oRmA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz, + } + + json5@1.0.2: + resolution: + { + integrity: sha1-Y9mNYPIbMTt3xNbaGL+mnYDh1ZM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json5/-/json5-1.0.2.tgz, + } + hasBin: true + + json5@2.2.3: + resolution: + { + integrity: sha1-eM1vGhm9wStz21rQxh79ZsHikoM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json5/-/json5-2.2.3.tgz, + } + engines: { node: '>=6' } + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: + { + integrity: sha1-R2a9BajioRryIr7NGeFVdeUqhTo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz, + } + engines: { node: '>=4.0' } + + keyv@4.5.4: + resolution: + { + integrity: sha1-qHmpnilFL5QkOfKkBeOvizHU3pM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/keyv/-/keyv-4.5.4.tgz, + } + + language-subtag-registry@0.3.23: + resolution: + { + integrity: sha1-I1KeBNnjt0Z51wFC3z/S627Fcuc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz, + } + + language-tags@1.0.9: + resolution: + { + integrity: sha1-H/3NDsD6+0sb5/ixHzBq0PnAh3c=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/language-tags/-/language-tags-1.0.9.tgz, + } + engines: { node: '>=0.10' } + + levn@0.4.1: + resolution: + { + integrity: sha1-rkViwAdHO5MqYgDUAyaN0v/8at4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/levn/-/levn-0.4.1.tgz, + } + engines: { node: '>= 0.8.0' } + + lightningcss-android-arm64@1.32.0: + resolution: + { + integrity: sha1-8DOIURbf79nG9UeHUj41FLYeGWg=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: + { + integrity: sha1-ULcYcbAcgZlYS2SeKSVH+up6+bU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: + { + integrity: sha1-NfPpczLRMLnKGB4RtWje1q68bV4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: + { + integrity: sha1-l3enZHK2Ttb/lDQq1kx7r9eUpXU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: + { + integrity: sha1-E65lLhq3O5E117faFy9mbEEK1T0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: + { + integrity: sha1-QXhYeVqUWS9oASOhsfnaig4e8zU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: + { + integrity: sha1-a+NmkugQtxgECAL9gJYjz/5zITM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: + { + integrity: sha1-C3gDr06yHP043Tn+Kru1PH3QkfY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: + { + integrity: sha1-iNyLqGXd3bGsXvBLDxYYBEGMFjs=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: + { + integrity: sha1-TzC6P6XpJfW3n5RejMDRdsOxqzg=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: + { + integrity: sha1-FBqlYFZFBkkokCu0rwRfp9n0Igo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: + { + integrity: sha1-uFqulkhtyxv0mnyFcSISc/Tx5Kk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lightningcss/-/lightningcss-1.32.0.tgz, + } + engines: { node: '>= 12.0.0' } + + locate-path@6.0.0: + resolution: + { + integrity: sha1-VTIeswn+u8WcSAHZMackUqaB0oY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/locate-path/-/locate-path-6.0.0.tgz, + } + engines: { node: '>=10' } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lodash.merge/-/lodash.merge-4.6.2.tgz, + } + + loose-envify@1.4.0: + resolution: + { + integrity: sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/loose-envify/-/loose-envify-1.4.0.tgz, + } + hasBin: true + + lru-cache@5.1.1: + resolution: + { + integrity: sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lru-cache/-/lru-cache-5.1.1.tgz, + } + + lucide-react@0.577.0: + resolution: + { + integrity: sha1-iuie3YQV+ZxjGaOLQNnFE/b6UGI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lucide-react/-/lucide-react-0.577.0.tgz, + } + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: + { + integrity: sha1-VnY+wJoPqAkd8nh5/ZTRkHjADZE=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/magic-string/-/magic-string-0.30.21.tgz, + } + + math-intrinsics@1.1.0: + resolution: + { + integrity: sha1-oN10voHiqlwvJ+Zc4oNgXuTit/k=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/math-intrinsics/-/math-intrinsics-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + minimatch@10.2.5: + resolution: + { + integrity: sha1-vUhoegvjjtKWE5kQVgD4MglYYdE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimatch/-/minimatch-10.2.5.tgz, + } + engines: { node: 18 || 20 || >=22 } + + minimatch@3.1.5: + resolution: + { + integrity: sha1-WAyI+NVEXyvWqo88re+g3nn71p4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimatch/-/minimatch-3.1.5.tgz, + } + + minimist@1.2.8: + resolution: + { + integrity: sha1-waRk52kzAuCCoHXO4MBXdBrEdyw=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimist/-/minimist-1.2.8.tgz, + } + + ms@2.1.3: + resolution: + { + integrity: sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ms/-/ms-2.1.3.tgz, + } + + nanoid@3.3.15: + resolution: + { + integrity: sha1-NsSQ+tjG6GyCTJQN/d6Zm2ntQxY=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nanoid/-/nanoid-3.3.15.tgz, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + natural-compare@1.4.0: + resolution: + { + integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/natural-compare/-/natural-compare-1.4.0.tgz, + } + + node-exports-info@1.6.2: + resolution: + { + integrity: sha1-JDoxBWd9Z4HNJuanI1D9kopctG8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-exports-info/-/node-exports-info-1.6.2.tgz, + } + engines: { node: '>= 0.4' } + + node-releases@2.0.51: + resolution: + { + integrity: sha1-zcCEM1d/WzKtAWlEgXJuIu61Su8=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-releases/-/node-releases-2.0.51.tgz, + } + engines: { node: '>=18' } + + object-assign@4.1.1: + resolution: + { + integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object-assign/-/object-assign-4.1.1.tgz, + } + engines: { node: '>=0.10.0' } + + object-inspect@1.13.4: + resolution: + { + integrity: sha1-g3UmXiG8IND6WCwi4bE0hdbgAhM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object-inspect/-/object-inspect-1.13.4.tgz, + } + engines: { node: '>= 0.4' } + + object-keys@1.1.1: + resolution: + { + integrity: sha1-HEfyct8nfzsdrwYWd9nILiMixg4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object-keys/-/object-keys-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + object.assign@4.1.7: + resolution: + { + integrity: sha1-jBTKGkJMalYbC7KiL2b1BJqUXT0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object.assign/-/object.assign-4.1.7.tgz, + } + engines: { node: '>= 0.4' } + + object.entries@1.1.9: + resolution: + { + integrity: sha1-5HcKahREr7Yb05+YQBi1vt4l+LM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object.entries/-/object.entries-1.1.9.tgz, + } + engines: { node: '>= 0.4' } + + object.fromentries@2.0.8: + resolution: + { + integrity: sha1-9xldipuXvZXLwZmeqTns0aKwDGU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object.fromentries/-/object.fromentries-2.0.8.tgz, + } + engines: { node: '>= 0.4' } + + object.groupby@1.0.3: + resolution: + { + integrity: sha1-mxJcNiOBKfb3thlUoecXYUjVAC4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object.groupby/-/object.groupby-1.0.3.tgz, + } + engines: { node: '>= 0.4' } + + object.values@1.2.1: + resolution: + { + integrity: sha1-3u1SClCAn/f3Wnz9S8ZMegOMYhY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/object.values/-/object.values-1.2.1.tgz, + } + engines: { node: '>= 0.4' } + + obug@2.1.3: + resolution: + { + integrity: sha1-wCxg+Vq9YDQJMw52fbfygjGTMx4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/obug/-/obug-2.1.3.tgz, + } + engines: { node: '>=12.20.0' } + + optionator@0.9.4: + resolution: + { + integrity: sha1-fqHBpdkddk+yghOciP4R4YKjpzQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/optionator/-/optionator-0.9.4.tgz, + } + engines: { node: '>= 0.8.0' } + + own-keys@1.0.1: + resolution: + { + integrity: sha1-5ABpEKK/kTWFKJZ27r1vOQz1E1g=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/own-keys/-/own-keys-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + p-limit@3.1.0: + resolution: + { + integrity: sha1-4drMvnjQ0TiMoYxk/qOOPlfjcGs=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-limit/-/p-limit-3.1.0.tgz, + } + engines: { node: '>=10' } + + p-locate@5.0.0: + resolution: + { + integrity: sha1-g8gxXGeFAF470CGDlBHJ4RDm2DQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-locate/-/p-locate-5.0.0.tgz, + } + engines: { node: '>=10' } + + pagefind@1.5.2: + resolution: + { + integrity: sha1-RH3YABjX/QwpJKVmOfe3K7vR8WU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/pagefind/-/pagefind-1.5.2.tgz, + } + hasBin: true + + parent-module@1.0.1: + resolution: + { + integrity: sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/parent-module/-/parent-module-1.0.1.tgz, + } + engines: { node: '>=6' } + + path-exists@4.0.0: + resolution: + { + integrity: sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-exists/-/path-exists-4.0.0.tgz, + } + engines: { node: '>=8' } + + path-key@3.1.1: + resolution: + { + integrity: sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-key/-/path-key-3.1.1.tgz, + } + engines: { node: '>=8' } + + path-parse@1.0.7: + resolution: + { + integrity: sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-parse/-/path-parse-1.0.7.tgz, + } + + pathe@2.0.3: + resolution: + { + integrity: sha1-PsvsVUIWhbcKnahyss/z4cvtFxY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/pathe/-/pathe-2.0.3.tgz, + } + + picocolors@1.1.1: + resolution: + { + integrity: sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picocolors/-/picocolors-1.1.1.tgz, + } + + picomatch@4.0.5: + resolution: + { + integrity: sha1-UepXoX2G9gX4EDlZX7xA7QalX6s=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picomatch/-/picomatch-4.0.5.tgz, + } + engines: { node: '>=12' } + + possible-typed-array-names@1.1.0: + resolution: + { + integrity: sha1-k+NYK8DlQmWG2dB7ee5A/IQd5K4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + postcss@8.5.16: + resolution: + { + integrity: sha1-EjDOC13zVMJMDqRfmc5faognnSg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/postcss/-/postcss-8.5.16.tgz, + } + engines: { node: ^10 || ^12 || >=14 } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha1-3rxkidem5rDnYRiIzsiAM30xY5Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prelude-ls/-/prelude-ls-1.2.1.tgz, + } + engines: { node: '>= 0.8.0' } + + prettier-plugin-tailwindcss@0.7.4: + resolution: + { + integrity: sha1-6t0/nS5L+6KZSyL4DdolgEpgipo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.4.tgz, + } + engines: { node: '>=20.19' } + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.8.1: + resolution: + { + integrity: sha1-7fSJd8+ZFVj0/L2KO6YBW6KjoXM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prettier/-/prettier-3.8.1.tgz, + } + engines: { node: '>=14' } + hasBin: true + + prop-types@15.8.1: + resolution: + { + integrity: sha1-Z9h78aaU9IQ1zzMsJK8QIUoxQLU=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/prop-types/-/prop-types-15.8.1.tgz, + } + + punycode@2.3.1: + resolution: + { + integrity: sha1-AnQi4vrsCyXhVJw+G9gwm5EztuU=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/punycode/-/punycode-2.3.1.tgz, + } + engines: { node: '>=6' } + + react-dom@18.3.1: + resolution: + { + integrity: sha1-wiZdeVEbV9R5s90/36UVNklMXLQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-dom/-/react-dom-18.3.1.tgz, + } + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: + { + integrity: sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-is/-/react-is-16.13.1.tgz, + } + + react-refresh@0.17.0: + resolution: + { + integrity: sha1-t+V5w2V/I9BOzL5K0uWKjtUeflM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-refresh/-/react-refresh-0.17.0.tgz, + } + engines: { node: '>=0.10.0' } + + react-router-dom@7.18.1: + resolution: + { + integrity: sha1-DRsTjikTkwWa1IHD4Q42Y4WpeKQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-router-dom/-/react-router-dom-7.18.1.tgz, + } + engines: { node: '>=20.0.0' } + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.18.1: + resolution: + { + integrity: sha1-YSWdFZS5XBrOKZ7kxXRTVw8MIvE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-router/-/react-router-7.18.1.tgz, + } + engines: { node: '>=20.0.0' } + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@18.3.1: + resolution: + { + integrity: sha1-SauJIAnFOTNiW9FrJTP8dUyrKJE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react/-/react-18.3.1.tgz, + } + engines: { node: '>=0.10.0' } + + reflect.getprototypeof@1.0.10: + resolution: + { + integrity: sha1-xikhnnijMW2LYEx2XvaJlpZOe/k=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz, + } + engines: { node: '>= 0.4' } + + regexp.prototype.flags@1.5.4: + resolution: + { + integrity: sha1-GtbGLUSiWQB+VbOXDgD3Ru+8qhk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz, + } + engines: { node: '>= 0.4' } + + resolve-from@4.0.0: + resolution: + { + integrity: sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/resolve-from/-/resolve-from-4.0.0.tgz, + } + engines: { node: '>=4' } + + resolve@2.0.0-next.7: + resolution: + { + integrity: sha1-ujsDXUse58UiQm7uc8q8sP1VFd0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/resolve/-/resolve-2.0.0-next.7.tgz, + } + engines: { node: '>= 0.4' } + hasBin: true + + rollup@4.62.2: + resolution: + { + integrity: sha1-2Q/Ey4EfBxMDyJC3eVlWNPNflUE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rollup/-/rollup-4.62.2.tgz, + } + engines: { node: '>=18.0.0', npm: '>=8.0.0' } + hasBin: true + + safe-array-concat@1.1.4: + resolution: + { + integrity: sha1-pUzJthpX8ztCq608vdo6KzjMVxk=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/safe-array-concat/-/safe-array-concat-1.1.4.tgz, + } + engines: { node: '>=0.4' } + + safe-push-apply@1.0.0: + resolution: + { + integrity: sha1-AYUOmBwWAtOYyFCB82Dk5tA9J/U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/safe-push-apply/-/safe-push-apply-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + safe-regex-test@1.1.0: + resolution: + { + integrity: sha1-f4fftnoxUHguqvGFg/9dFxGsEME=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/safe-regex-test/-/safe-regex-test-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + scheduler@0.23.2: + resolution: + { + integrity: sha1-QUumSjsoKJLpRM8hCOzAeNEVzcM=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/scheduler/-/scheduler-0.23.2.tgz, + } + + semver@6.3.1: + resolution: + { + integrity: sha1-VW0u+GiRRuRtzqS/3QlfNDTf/LQ=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-6.3.1.tgz, + } + hasBin: true + + semver@7.8.5: + resolution: + { + integrity: sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz, + } + engines: { node: '>=10' } + hasBin: true + + set-cookie-parser@2.7.2: + resolution: + { + integrity: sha1-zNCGc6muXS5E6iot4lCJ5nx+32g=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz, + } + + set-function-length@1.2.2: + resolution: + { + integrity: sha1-qscjFBmOrtl1z3eyw7a4gGleVEk=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/set-function-length/-/set-function-length-1.2.2.tgz, + } + engines: { node: '>= 0.4' } + + set-function-name@2.0.2: + resolution: + { + integrity: sha1-FqcFxaDcL15jjKltiozU4cK5CYU=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/set-function-name/-/set-function-name-2.0.2.tgz, + } + engines: { node: '>= 0.4' } + + set-proto@1.0.0: + resolution: + { + integrity: sha1-B2Dbz/MLLX6AH9bhmYPlbaM3Vl4=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/set-proto/-/set-proto-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + shebang-command@2.0.0: + resolution: + { + integrity: sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/shebang-command/-/shebang-command-2.0.0.tgz, + } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/shebang-regex/-/shebang-regex-3.0.0.tgz, + } + engines: { node: '>=8' } + + side-channel-list@1.0.1: + resolution: + { + integrity: sha1-wuC1oUpUCuvuO7xsP4ZmzJtQkSc=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/side-channel-list/-/side-channel-list-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + side-channel-map@1.0.1: + resolution: + { + integrity: sha1-1rtrN5Asb+9RdOX1M/q0xzKib0I=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/side-channel-map/-/side-channel-map-1.0.1.tgz, + } + engines: { node: '>= 0.4' } + + side-channel-weakmap@1.0.2: + resolution: + { + integrity: sha1-Ed2hnVNo5Azp7CvcH7DsvAeQ7Oo=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + side-channel@1.1.1: + resolution: + { + integrity: sha1-6gLGLgXcS+pn1EQvD7ce4ZL44Ks=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/side-channel/-/side-channel-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + siginfo@2.0.0: + resolution: + { + integrity: sha1-MudscLeXJOO7Vny51UPrhYzPrzA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/siginfo/-/siginfo-2.0.0.tgz, + } + + source-map-js@1.2.1: + resolution: + { + integrity: sha1-HOVlD93YerwJnto33P8CTCZnrkY=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/source-map-js/-/source-map-js-1.2.1.tgz, + } + engines: { node: '>=0.10.0' } + + stackback@0.0.2: + resolution: + { + integrity: sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/stackback/-/stackback-0.0.2.tgz, + } + + std-env@4.2.0: + resolution: + { + integrity: sha1-jr4OxgSFZoq0ciezEvQlTN+AydM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/std-env/-/std-env-4.2.0.tgz, + } + + stop-iteration-iterator@1.1.0: + resolution: + { + integrity: sha1-9IH/cKVI9hJNAxLDqhTL+nqlQq0=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + string.prototype.includes@2.0.1: + resolution: + { + integrity: sha1-7O7yEoNkB2GoHb4W1scXGk7ffZI=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz, + } + engines: { node: '>= 0.4' } + + string.prototype.matchall@4.0.12: + resolution: + { + integrity: sha1-bIh0DkmtSVaxMyqRHpSVg6J11MA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz, + } + engines: { node: '>= 0.4' } + + string.prototype.repeat@1.0.0: + resolution: + { + integrity: sha1-6Qhy7gMIspQ1qiYnX24bdi2u4Bo=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz, + } + + string.prototype.trim@1.2.11: + resolution: + { + integrity: sha1-5r0ZzaOYXQWkLdox89300100MOM=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz, + } + engines: { node: '>= 0.4' } + + string.prototype.trimend@1.0.10: + resolution: + { + integrity: sha1-vmvPTz/gRgvezNss9PlxsxD4NG4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz, + } + engines: { node: '>= 0.4' } + + string.prototype.trimstart@1.0.8: + resolution: + { + integrity: sha1-fug03ajHwX7/MRhHK7Nb/tqjTd4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz, + } + engines: { node: '>= 0.4' } + + strip-bom@3.0.0: + resolution: + { + integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-bom/-/strip-bom-3.0.0.tgz, + } + engines: { node: '>=4' } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-json-comments/-/strip-json-comments-3.1.1.tgz, + } + engines: { node: '>=8' } + + supports-color@7.2.0: + resolution: + { + integrity: sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/supports-color/-/supports-color-7.2.0.tgz, + } + engines: { node: '>=8' } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { + integrity: sha1-btpL00SjyUrqN21MwxvHcxEDngk=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz, + } + engines: { node: '>= 0.4' } + + tailwind-merge@3.6.0: + resolution: + { + integrity: sha1-iNgyQtHde8hHIj9z3PIQ3R8u4Rw=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tailwind-merge/-/tailwind-merge-3.6.0.tgz, + } + + tailwindcss@4.3.2: + resolution: + { + integrity: sha1-QI7mfXZ6D+97F0Z0u5xc4TalrOE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tailwindcss/-/tailwindcss-4.3.2.tgz, + } + + tapable@2.3.3: + resolution: + { + integrity: sha1-XafJmSxGA4IhJnmFqyhCGoh58WA=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tapable/-/tapable-2.3.3.tgz, + } + engines: { node: '>=6' } + + tinybench@2.9.0: + resolution: + { + integrity: sha1-EDyfi6bXI3pHq23R3P93JRhjQms=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinybench/-/tinybench-2.9.0.tgz, + } + + tinyexec@1.2.4: + resolution: + { + integrity: sha1-rkW7Lt69qUxw9OqJfg8SQ+Rw23E=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyexec/-/tinyexec-1.2.4.tgz, + } + engines: { node: '>=18' } + + tinyglobby@0.2.17: + resolution: + { + integrity: sha1-ViqabJ6ys7Ej05cZ+a9btE/NdjE=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyglobby/-/tinyglobby-0.2.17.tgz, + } + engines: { node: '>=12.0.0' } + + tinyrainbow@3.1.0: + resolution: + { + integrity: sha1-HYpiOJP5XPCi3bnl0RFQ4ZFAlCE=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tinyrainbow/-/tinyrainbow-3.1.0.tgz, + } + engines: { node: '>=14.0.0' } + + ts-api-utils@2.5.0: + resolution: + { + integrity: sha1-Ss1KFV4ic0mQpe0f6el/ETvLN8E=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ts-api-utils/-/ts-api-utils-2.5.0.tgz, + } + engines: { node: '>=18.12' } + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: + { + integrity: sha1-UpnsYF5VsauyPsk57xXtr0gwcNQ=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz, + } + + type-check@0.4.0: + resolution: + { + integrity: sha1-B7ggO/pwVsBlcFDjzNLDdzC6uPE=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/type-check/-/type-check-0.4.0.tgz, + } + engines: { node: '>= 0.8.0' } + + typed-array-buffer@1.0.3: + resolution: + { + integrity: sha1-pyOVRQpIaewDP9VJNxtHrzou5TY=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz, + } + engines: { node: '>= 0.4' } + + typed-array-byte-length@1.0.3: + resolution: + { + integrity: sha1-hAegT314aE89JSqhoUPSt3tBYM4=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz, + } + engines: { node: '>= 0.4' } + + typed-array-byte-offset@1.0.4: + resolution: + { + integrity: sha1-rjaYuOyRqKuUUBYQiu8A1b/xI1U=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz, + } + engines: { node: '>= 0.4' } + + typed-array-length@1.0.8: + resolution: + { + integrity: sha1-C3Dpgsnp2v4t721kWP9LPy0rbXA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typed-array-length/-/typed-array-length-1.0.8.tgz, + } + engines: { node: '>= 0.4' } + + typescript-eslint@8.63.0: + resolution: + { + integrity: sha1-HCtlyYlXKvcRP7rn9Ux/+rD76xI=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript-eslint/-/typescript-eslint-8.63.0.tgz, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: + { + integrity: sha1-W09Z4VMQqxeiFvXWz1PuR27eZw8=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript/-/typescript-5.9.3.tgz, + } + engines: { node: '>=14.17' } + hasBin: true + + unbox-primitive@1.1.0: + resolution: + { + integrity: sha1-jZ0snt7qhGDH81AzqIhnlEk00eI=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unbox-primitive/-/unbox-primitive-1.1.0.tgz, + } + engines: { node: '>= 0.4' } + + undici-types@6.21.0: + resolution: + { + integrity: sha1-aR0ArzkJvpOn+qE75hs6W1DvEss=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/undici-types/-/undici-types-6.21.0.tgz, + } + + update-browserslist-db@1.2.3: + resolution: + { + integrity: sha1-ZNdttYcTE2rL60xJEUNmzGzC6A0=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz, + } + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: + { + integrity: sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/uri-js/-/uri-js-4.4.1.tgz, + } + + vite@6.4.3: + resolution: + { + integrity: sha1-haFk23znBvKndoEu+is0DxchhY4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vite/-/vite-6.4.3.tgz, + } + engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.10: + resolution: + { + integrity: sha1-fpKF7+JksRZwULejp/80eI4bevw=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vitest/-/vitest-4.1.10.tgz, + } + engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@7.0.0: + resolution: + { + integrity: sha1-JWtOGIK+feu/AdBfCqIDl3jqCAo=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/webidl-conversions/-/webidl-conversions-7.0.0.tgz, + } + engines: { node: '>=12' } + + whatwg-mimetype@3.0.0: + resolution: + { + integrity: sha1-X6GnYjhn/xr2yj3HKta4pCCL66c=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz, + } + engines: { node: '>=12' } + + which-boxed-primitive@1.1.1: + resolution: + { + integrity: sha1-127Cfff6Fl8Y1YCDdKX+I8KbF24=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz, + } + engines: { node: '>= 0.4' } + + which-builtin-type@1.2.1: + resolution: + { + integrity: sha1-iRg9obSQerCJprAgKcxdjWV0Jw4=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/which-builtin-type/-/which-builtin-type-1.2.1.tgz, + } + engines: { node: '>= 0.4' } + + which-collection@1.0.2: + resolution: + { + integrity: sha1-Yn73YkOSChB+fOjpYZHevksWwqA=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/which-collection/-/which-collection-1.0.2.tgz, + } + engines: { node: '>= 0.4' } + + which-typed-array@1.1.22: + resolution: + { + integrity: sha1-jzzHiu+0C0NzRt1Aodv6XR2kP+k=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/which-typed-array/-/which-typed-array-1.1.22.tgz, + } + engines: { node: '>= 0.4' } + + which@2.0.2: + resolution: + { + integrity: sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/which/-/which-2.0.2.tgz, + } + engines: { node: '>= 8' } + hasBin: true + + why-is-node-running@2.3.0: + resolution: + { + integrity: sha1-o/aalxB/SUs83Dvd3Yg6fWXOvwQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/why-is-node-running/-/why-is-node-running-2.3.0.tgz, + } + engines: { node: '>=8' } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha1-0sRcbdT7zmIaZvE2y+Mor9BBCzQ=, + tarball: https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/word-wrap/-/word-wrap-1.2.5.tgz, + } + engines: { node: '>=0.10.0' } + + yallist@3.1.1: + resolution: + { + integrity: sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yallist/-/yallist-3.1.1.tgz, + } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha1-ApTrPe4FAo0x7hpfosVWpqrxChs=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yocto-queue/-/yocto-queue-0.1.0.tgz, + } + engines: { node: '>=10' } + + zod-validation-error@4.0.2: + resolution: + { + integrity: sha1-vGBeuknOD81ZjBJ/7hwja+PyKRg=, + tarball: https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zod-validation-error/-/zod-validation-error-4.0.2.tgz, + } + engines: { node: '>=18.0.0' } + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: + { + integrity: sha1-toDxcohdGLvr8hqDTqJeVaG781Y=, + tarball: https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zod/-/zod-4.4.3.tgz, + } + +snapshots: + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.5 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@pagefind/darwin-arm64@1.5.2': + optional: true + + '@pagefind/darwin-x64@1.5.2': + optional: true + + '@pagefind/default-ui@1.5.2': {} + + '@pagefind/freebsd-x64@1.5.2': + optional: true + + '@pagefind/linux-arm64@1.5.2': + optional: true + + '@pagefind/linux-x64@1.5.2': + optional: true + + '@pagefind/windows-arm64@1.5.2': + optional: true + + '@pagefind/windows-x64@1.5.2': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@tailwindcss/node@4.3.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.2 + + '@tailwindcss/oxide-android-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide@4.3.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-x64': 4.3.2 + '@tailwindcss/oxide-freebsd-x64': 4.3.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-x64-musl': 4.3.2 + '@tailwindcss/oxide-wasm32-wasi': 4.3.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 + + '@tailwindcss/postcss@4.3.2': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + postcss: 8.5.16 + tailwindcss: 4.3.2 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + + '@types/react@18.3.31': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/type-utils': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + eslint: 9.39.4(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + + '@typescript-eslint/tsconfig-utils@8.63.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.63.0': {} + + '@typescript-eslint/typescript-estree@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.63.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@4.1.10': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.10(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@vitest/spy': 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + + '@vitest/pretty-format@4.1.10': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.10': {} + + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.12.1: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.42: {} + + brace-expansion@1.1.16: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.7: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.5: + dependencies: + baseline-browser-mapping: 2.10.42 + caniuse-lite: 1.0.30001803 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.5) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001803: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.389: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@4.5.0: {} + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + 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.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.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 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + 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.1 + math-intrinsics: 1.1.0 + 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.4 + safe-array-concat: 1.1.4 + 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.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.3: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-module-lexer@2.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@rtsao/scc': 1.1.0 + 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: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.7.0)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.12.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.4(jiti@2.7.0) + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + eslint: 9.39.4(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.3 + eslint: 9.39.4(jiti@2.7.0) + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.4.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.7.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + happy-dom@15.11.7: + dependencies: + entities: 4.5.0 + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.577.0(react@18.3.1): + dependencies: + react: 18.3.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.7 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.16 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.15: {} + + natural-compare@1.4.0: {} + + node-exports-info@1.6.2: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.51: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + obug@2.1.3: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pagefind@1.5.2: + optionalDependencies: + '@pagefind/darwin-arm64': 1.5.2 + '@pagefind/darwin-x64': 1.5.2 + '@pagefind/freebsd-x64': 1.5.2 + '@pagefind/linux-arm64': 1.5.2 + '@pagefind/linux-x64': 1.5.2 + '@pagefind/windows-arm64': 1.5.2 + '@pagefind/windows-x64': 1.5.2 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.16: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-tailwindcss@0.7.4(prettier@3.8.1): + dependencies: + prettier: 3.8.1 + + prettier@3.8.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@16.13.1: {} + + react-refresh@0.17.0: {} + + react-router-dom@7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.1.1 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + 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 + + resolve-from@4.0.0: {} + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.2 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-cookie-parser@2.7.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.2: {} + + tapable@2.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.0: {} + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.63.0(@typescript-eslint/parser@8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.5): + dependencies: + browserslist: 4.28.5 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.16 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + + vitest@4.1.10(@types/node@22.20.1)(happy-dom@15.11.7)(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.20.1 + happy-dom: 15.11.7 + transitivePeerDependencies: + - msw + + webidl-conversions@7.0.0: {} + + whatwg-mimetype@3.0.0: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..286cf7f56 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - apps/* + - packages/* diff --git a/scripts/check-headers.mjs b/scripts/check-headers.mjs new file mode 100644 index 000000000..1271a4796 --- /dev/null +++ b/scripts/check-headers.mjs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import { extname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = resolve(fileURLToPath(new URL('..', import.meta.url))); +const headerLines = [ + 'Copyright (c) Microsoft Corporation.', + 'Licensed under the MIT license.', +]; + +const commentStyleBySuffix = new Map([ + ['.c', 'slash'], + ['.cc', 'slash'], + ['.cpp', 'slash'], + ['.cs', 'slash'], + ['.cts', 'slash'], + ['.cxx', 'slash'], + ['.h', 'slash'], + ['.hpp', 'slash'], + ['.java', 'slash'], + ['.js', 'slash'], + ['.jsx', 'slash'], + ['.mjs', 'slash'], + ['.mts', 'slash'], + ['.ts', 'slash'], + ['.tsx', 'slash'], + ['.py', 'hash'], + ['.pyi', 'hash'], + ['.pyw', 'hash'], + ['.ps1', 'hash'], + ['.sh', 'hash'], + ['.css', 'block'], + ['.html', 'html'], + ['.less', 'block'], + ['.scss', 'block'], + ['.svelte', 'html'], + ['.vue', 'html'], +]); + +function expectedHeader(style) { + if (style === 'hash') { + return headerLines.map((line) => `# ${line}`); + } + if (style === 'block') { + return ['/*', ...headerLines.map((line) => ` * ${line}`), ' */']; + } + if (style === 'html') { + return ['<!--', ...headerLines.map((line) => ` ${line}`), '-->']; + } + return headerLines.map((line) => `// ${line}`); +} + +const trackedPaths = execFileSync('git', ['ls-files', '-z'], { + cwd: repoRoot, + encoding: 'utf8', +}) + .split('\0') + .filter(Boolean) + .filter((path) => commentStyleBySuffix.has(extname(path).toLowerCase())) + .sort(); + +const failures = []; + +for (const trackedPath of trackedPaths) { + const absolutePath = resolve(repoRoot, trackedPath); + if (!existsSync(absolutePath)) { + continue; + } + + const suffix = extname(trackedPath).toLowerCase(); + const expected = expectedHeader(commentStyleBySuffix.get(suffix)); + let contents; + try { + contents = readFileSync(absolutePath, 'utf8').replace(/^\uFEFF/, ''); + } catch (error) { + failures.push( + `${trackedPath}: unable to read tracked source file (${error.code ?? 'unknown error'})`, + ); + continue; + } + const lines = contents.split(/\r?\n/); + const start = lines[0]?.startsWith('#!') ? 1 : 0; + + if (!expected.every((line, index) => lines[start + index] === line)) { + failures.push( + `${trackedPath}: expected lines ${start + 1}-${start + expected.length} to be ${expected.join(' / ')}`, + ); + continue; + } + + const blankLineIndex = start + expected.length; + if (lines.length > blankLineIndex && lines[blankLineIndex] !== '') { + failures.push( + `${trackedPath}: expected a blank line after the license header`, + ); + } +} + +if (failures.length > 0) { + console.error('Source header check failed:'); + for (const failure of failures) { + console.error(` - ${failure}`); + } + process.exitCode = 1; +} else { + console.log('All tracked source files have the required license header.'); +}