mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +00:00
Search suggestions (#85)
The motivation for building search suggestions is two-fold: (1) to make the zoekt query language more approachable by presenting all available options to the user, and (2) make it easier for power-users to craft complex queries. The meat-n-potatoes of this change are concentrated in searchBar.tsx and searchSuggestionBox.tsx. The suggestions box works by maintaining a state-machine of "modes". By default, the box is in the refine mode, where suggestions for different prefixes (e.g., repo:, lang:, etc.) are suggested to the user. When one of these prefixes is matched, the state-machine transitions to the corresponding mode (e.g., repository, language, etc.) and surfaces suggestions for that mode (if any). The query is split up into parts by spaces " " (e.g., 'test repo:hello' -> ['test', 'repo:hello']). See splitQuery. The part that has the cursor over it is considered the active part. We evaluate which mode the state machine is in based on the active part. When a suggestion is clicked, we only modify the active part of the query. Three modes are currently missing suggestion data: file (file names), revision (branch / tag names), and symbol (symbol names). In future PRs, we will need to introduce endpoints into the backend to allow the frontend to fetch this data and surface it as suggestions..
This commit is contained in:
parent
3fe2d3295a
commit
7f952ce163
27 changed files with 2365 additions and 211 deletions
28
.github/workflows/test-web.yml
vendored
Normal file
28
.github/workflows/test-web.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Test Web
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: "true"
|
||||||
|
- name: Use Node.Js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20.x'
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: yarn workspace @sourcebot/web test
|
||||||
|
|
||||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"dbaeumer.vscode-eslint"
|
"dbaeumer.vscode-eslint",
|
||||||
|
"bradlc.vscode-tailwindcss"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
|
|
@ -7,5 +7,16 @@
|
||||||
{
|
{
|
||||||
"pattern": "./packages/*/"
|
"pattern": "./packages/*/"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
// @see : https://cva.style/docs/getting-started/installation#intellisense
|
||||||
|
"tailwindCSS.experimental.classRegex": [
|
||||||
|
[
|
||||||
|
"cva\\(([^)]*)\\)",
|
||||||
|
"[\"'`]([^\"'`]*).*?[\"'`]"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"cx\\(([^)]*)\\)",
|
||||||
|
"(?:'|\"|`)([^']*)(?:'|\"|`)"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn workspaces run build",
|
"build": "yarn workspaces run build",
|
||||||
"test": "yarn workspace @sourcebot/backend test",
|
"test": "yarn workspaces run test",
|
||||||
"dev": "npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web",
|
"dev": "npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web",
|
||||||
"dev:zoekt": "export PATH=\"$PWD/bin:$PATH\" && zoekt-webserver -index .sourcebot/index -rpc",
|
"dev:zoekt": "export PATH=\"$PWD/bin:$PATH\" && zoekt-webserver -index .sourcebot/index -rpc",
|
||||||
"dev:backend": "yarn workspace @sourcebot/backend dev:watch",
|
"dev:backend": "yarn workspace @sourcebot/backend dev:watch",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/commands": "^6.6.0",
|
"@codemirror/commands": "^6.6.0",
|
||||||
|
|
@ -77,9 +78,12 @@
|
||||||
"eslint-config-next": "14.2.6",
|
"eslint-config-next": "14.2.6",
|
||||||
"eslint-plugin-react": "^7.35.0",
|
"eslint-plugin-react": "^7.35.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
|
"jsdom": "^25.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5",
|
||||||
|
"vite-tsconfig-paths": "^5.1.3",
|
||||||
|
"vitest": "^2.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import { GitHubLogoIcon, DiscordLogoIcon } from "@radix-ui/react-icons";
|
||||||
import { SettingsDropdown } from "./settingsDropdown";
|
import { SettingsDropdown } from "./settingsDropdown";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import logoDark from "../../public/sb_logo_dark_small.png";
|
import logoDark from "../../../public/sb_logo_dark_small.png";
|
||||||
import logoLight from "../../public/sb_logo_light_small.png";
|
import logoLight from "../../../public/sb_logo_light_small.png";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
const SOURCEBOT_DISCORD_URL = "https://discord.gg/6Fhp27x7Pb";
|
const SOURCEBOT_DISCORD_URL = "https://discord.gg/6Fhp27x7Pb";
|
||||||
224
packages/web/src/app/components/searchBar/constants.ts
Normal file
224
packages/web/src/app/components/searchBar/constants.ts
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
import { Suggestion, SuggestionMode } from "./searchSuggestionsBox";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of search prefixes that can be used while the
|
||||||
|
* `refine` suggestion mode is active.
|
||||||
|
*/
|
||||||
|
enum SearchPrefix {
|
||||||
|
repo = "repo:",
|
||||||
|
r = "r:",
|
||||||
|
lang = "lang:",
|
||||||
|
file = "file:",
|
||||||
|
rev = "rev:",
|
||||||
|
revision = "revision:",
|
||||||
|
b = "b:",
|
||||||
|
branch = "branch:",
|
||||||
|
sym = "sym:",
|
||||||
|
content = "content:",
|
||||||
|
archived = "archived:",
|
||||||
|
case = "case:",
|
||||||
|
fork = "fork:",
|
||||||
|
public = "public:"
|
||||||
|
}
|
||||||
|
|
||||||
|
const negate = (prefix: SearchPrefix) => {
|
||||||
|
return `-${prefix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuggestionModeMapping = {
|
||||||
|
suggestionMode: SuggestionMode,
|
||||||
|
prefixes: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps search prefixes to a suggestion mode. When a query starts
|
||||||
|
* with a prefix, the corresponding suggestion mode is enabled.
|
||||||
|
* @see [searchSuggestionsBox.tsx](./searchSuggestionsBox.tsx)
|
||||||
|
*/
|
||||||
|
export const suggestionModeMappings: SuggestionModeMapping[] = [
|
||||||
|
{
|
||||||
|
suggestionMode: "repo",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.repo, negate(SearchPrefix.repo),
|
||||||
|
SearchPrefix.r, negate(SearchPrefix.r),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "language",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.lang, negate(SearchPrefix.lang),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "file",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.file, negate(SearchPrefix.file),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "content",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.content, negate(SearchPrefix.content),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "revision",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.rev, negate(SearchPrefix.rev),
|
||||||
|
SearchPrefix.revision, negate(SearchPrefix.revision),
|
||||||
|
SearchPrefix.branch, negate(SearchPrefix.branch),
|
||||||
|
SearchPrefix.b, negate(SearchPrefix.b),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "symbol",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.sym, negate(SearchPrefix.sym),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "archived",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.archived
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "case",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.case
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "fork",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.fork
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
suggestionMode: "public",
|
||||||
|
prefixes: [
|
||||||
|
SearchPrefix.public
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const refineModeSuggestions: Suggestion[] = [
|
||||||
|
{
|
||||||
|
value: SearchPrefix.repo,
|
||||||
|
description: "Include only results from the given repository.",
|
||||||
|
spotlight: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.repo),
|
||||||
|
description: "Exclude results from the given repository."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.lang,
|
||||||
|
description: "Include only results from the given language.",
|
||||||
|
spotlight: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.lang),
|
||||||
|
description: "Exclude results from the given language."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.file,
|
||||||
|
description: "Include only results from filepaths matching the given search pattern.",
|
||||||
|
spotlight: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.file),
|
||||||
|
description: "Exclude results from file paths matching the given search pattern."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.rev,
|
||||||
|
description: "Search a given branch or tag instead of the default branch.",
|
||||||
|
spotlight: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.rev),
|
||||||
|
description: "Exclude results from the given branch or tag."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.sym,
|
||||||
|
description: "Include only symbols matching the given search pattern.",
|
||||||
|
spotlight: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.sym),
|
||||||
|
description: "Exclude results from symbols matching the given search pattern."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.content,
|
||||||
|
description: "Include only results from files if their content matches the given search pattern."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: negate(SearchPrefix.content),
|
||||||
|
description: "Exclude results from files if their content matches the given search pattern."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.archived,
|
||||||
|
description: "Include results from archived repositories.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.case,
|
||||||
|
description: "Control case-sensitivity of search patterns."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.fork,
|
||||||
|
description: "Include only results from forked repositories."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: SearchPrefix.public,
|
||||||
|
description: "Filter on repository visibility."
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const publicModeSuggestions: Suggestion[] = [
|
||||||
|
{
|
||||||
|
value: "yes",
|
||||||
|
description: "Only include results from public repositories."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "no",
|
||||||
|
description: "Only include results from private repositories."
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const forkModeSuggestions: Suggestion[] = [
|
||||||
|
{
|
||||||
|
value: "yes",
|
||||||
|
description: "Only include results from forked repositories."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "no",
|
||||||
|
description: "Only include results from non-forked repositories."
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const caseModeSuggestions: Suggestion[] = [
|
||||||
|
{
|
||||||
|
value: "auto",
|
||||||
|
description: "Search patterns are case-insensitive if all characters are lowercase, and case sensitive otherwise (default)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "yes",
|
||||||
|
description: "Case sensitive search."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "no",
|
||||||
|
description: "Case insensitive search."
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const archivedModeSuggestions: Suggestion[] = [
|
||||||
|
{
|
||||||
|
value: "yes",
|
||||||
|
description: "Only include results in archived repositories."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "no",
|
||||||
|
description: "Only include results in non-archived repositories."
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
2
packages/web/src/app/components/searchBar/index.ts
Normal file
2
packages/web/src/app/components/searchBar/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
export { SearchBar } from "./searchBar";
|
||||||
712
packages/web/src/app/components/searchBar/languages.ts
Normal file
712
packages/web/src/app/components/searchBar/languages.ts
Normal file
|
|
@ -0,0 +1,712 @@
|
||||||
|
|
||||||
|
// From https://github.com/github-linguist/linguist/blob/main/lib/linguist/languages.yml
|
||||||
|
const languages = [
|
||||||
|
"1C Enterprise",
|
||||||
|
"2-Dimensional Array",
|
||||||
|
"4D",
|
||||||
|
"ABAP",
|
||||||
|
"ABAP CDS",
|
||||||
|
"ABNF",
|
||||||
|
"AGS Script",
|
||||||
|
"AIDL",
|
||||||
|
"AL",
|
||||||
|
"AMPL",
|
||||||
|
"ANTLR",
|
||||||
|
"API Blueprint",
|
||||||
|
"APL",
|
||||||
|
"ASL",
|
||||||
|
"ASN.1",
|
||||||
|
"ASP.NET",
|
||||||
|
"ATS",
|
||||||
|
"ActionScript",
|
||||||
|
"Ada",
|
||||||
|
"Adblock Filter List",
|
||||||
|
"Adobe Font Metrics",
|
||||||
|
"Agda",
|
||||||
|
"Alloy",
|
||||||
|
"Alpine Abuild",
|
||||||
|
"Altium Designer",
|
||||||
|
"AngelScript",
|
||||||
|
"Ant Build System",
|
||||||
|
"Antlers",
|
||||||
|
"ApacheConf",
|
||||||
|
"Apex",
|
||||||
|
"Apollo Guidance Computer",
|
||||||
|
"AppleScript",
|
||||||
|
"Arc",
|
||||||
|
"AsciiDoc",
|
||||||
|
"AspectJ",
|
||||||
|
"Assembly",
|
||||||
|
"Astro",
|
||||||
|
"Asymptote",
|
||||||
|
"Augeas",
|
||||||
|
"AutoHotkey",
|
||||||
|
"AutoIt",
|
||||||
|
"Avro IDL",
|
||||||
|
"Awk",
|
||||||
|
"BASIC",
|
||||||
|
"Ballerina",
|
||||||
|
"Batchfile",
|
||||||
|
"Beef",
|
||||||
|
"Befunge",
|
||||||
|
"Berry",
|
||||||
|
"BibTeX",
|
||||||
|
"Bicep",
|
||||||
|
"Bikeshed",
|
||||||
|
"Bison",
|
||||||
|
"BitBake",
|
||||||
|
"Blade",
|
||||||
|
"BlitzBasic",
|
||||||
|
"BlitzMax",
|
||||||
|
"Bluespec",
|
||||||
|
"Bluespec BH",
|
||||||
|
"Boo",
|
||||||
|
"Boogie",
|
||||||
|
"Brainfuck",
|
||||||
|
"BrighterScript",
|
||||||
|
"Brightscript",
|
||||||
|
"Browserslist",
|
||||||
|
"C",
|
||||||
|
"C#",
|
||||||
|
"C++",
|
||||||
|
"C-ObjDump",
|
||||||
|
"C2hs Haskell",
|
||||||
|
"CAP CDS",
|
||||||
|
"CIL",
|
||||||
|
"CLIPS",
|
||||||
|
"CMake",
|
||||||
|
"COBOL",
|
||||||
|
"CODEOWNERS",
|
||||||
|
"COLLADA",
|
||||||
|
"CSON",
|
||||||
|
"CSS",
|
||||||
|
"CSV",
|
||||||
|
"CUE",
|
||||||
|
"CWeb",
|
||||||
|
"Cabal Config",
|
||||||
|
"Cadence",
|
||||||
|
"Cairo",
|
||||||
|
"CameLIGO",
|
||||||
|
"Cap'n Proto",
|
||||||
|
"CartoCSS",
|
||||||
|
"Ceylon",
|
||||||
|
"Chapel",
|
||||||
|
"Charity",
|
||||||
|
"Checksums",
|
||||||
|
"ChucK",
|
||||||
|
"Circom",
|
||||||
|
"Cirru",
|
||||||
|
"Clarion",
|
||||||
|
"Clarity",
|
||||||
|
"Classic ASP",
|
||||||
|
"Clean",
|
||||||
|
"Click",
|
||||||
|
"Clojure",
|
||||||
|
"Closure Templates",
|
||||||
|
"Cloud Firestore Security Rules",
|
||||||
|
"CoNLL-U",
|
||||||
|
"CodeQL",
|
||||||
|
"CoffeeScript",
|
||||||
|
"ColdFusion",
|
||||||
|
"ColdFusion CFC",
|
||||||
|
"Common Lisp",
|
||||||
|
"Common Workflow Language",
|
||||||
|
"Component Pascal",
|
||||||
|
"Cool",
|
||||||
|
"Coq",
|
||||||
|
"Cpp-ObjDump",
|
||||||
|
"Creole",
|
||||||
|
"Crystal",
|
||||||
|
"Csound",
|
||||||
|
"Csound Document",
|
||||||
|
"Csound Score",
|
||||||
|
"Cuda",
|
||||||
|
"Cue Sheet",
|
||||||
|
"Curry",
|
||||||
|
"Cycript",
|
||||||
|
"Cypher",
|
||||||
|
"Cython",
|
||||||
|
"D",
|
||||||
|
"D-ObjDump",
|
||||||
|
"D2",
|
||||||
|
"DIGITAL Command Language",
|
||||||
|
"DM",
|
||||||
|
"DNS Zone",
|
||||||
|
"DTrace",
|
||||||
|
"Dafny",
|
||||||
|
"Darcs Patch",
|
||||||
|
"Dart",
|
||||||
|
"DataWeave",
|
||||||
|
"Debian Package Control File",
|
||||||
|
"DenizenScript",
|
||||||
|
"Dhall",
|
||||||
|
"Diff",
|
||||||
|
"DirectX 3D File",
|
||||||
|
"Dockerfile",
|
||||||
|
"Dogescript",
|
||||||
|
"Dotenv",
|
||||||
|
"Dylan",
|
||||||
|
"E",
|
||||||
|
"E-mail",
|
||||||
|
"EBNF",
|
||||||
|
"ECL",
|
||||||
|
"ECLiPSe",
|
||||||
|
"EJS",
|
||||||
|
"EQ",
|
||||||
|
"Eagle",
|
||||||
|
"Earthly",
|
||||||
|
"Easybuild",
|
||||||
|
"Ecere Projects",
|
||||||
|
"Ecmarkup",
|
||||||
|
"EditorConfig",
|
||||||
|
"Edje Data Collection",
|
||||||
|
"Eiffel",
|
||||||
|
"Elixir",
|
||||||
|
"Elm",
|
||||||
|
"Elvish",
|
||||||
|
"Elvish Transcript",
|
||||||
|
"Emacs Lisp",
|
||||||
|
"EmberScript",
|
||||||
|
"Erlang",
|
||||||
|
"Euphoria",
|
||||||
|
"F#",
|
||||||
|
"F*",
|
||||||
|
"FIGlet Font",
|
||||||
|
"FLUX",
|
||||||
|
"Factor",
|
||||||
|
"Fancy",
|
||||||
|
"Fantom",
|
||||||
|
"Faust",
|
||||||
|
"Fennel",
|
||||||
|
"Filebench WML",
|
||||||
|
"Filterscript",
|
||||||
|
"Fluent",
|
||||||
|
"Formatted",
|
||||||
|
"Forth",
|
||||||
|
"Fortran",
|
||||||
|
"Fortran Free Form",
|
||||||
|
"FreeBasic",
|
||||||
|
"FreeMarker",
|
||||||
|
"Frege",
|
||||||
|
"Futhark",
|
||||||
|
"G-code",
|
||||||
|
"GAML",
|
||||||
|
"GAMS",
|
||||||
|
"GAP",
|
||||||
|
"GCC Machine Description",
|
||||||
|
"GDB",
|
||||||
|
"GDScript",
|
||||||
|
"GEDCOM",
|
||||||
|
"GLSL",
|
||||||
|
"GN",
|
||||||
|
"GSC",
|
||||||
|
"Game Maker Language",
|
||||||
|
"Gemfile.lock",
|
||||||
|
"Gemini",
|
||||||
|
"Genero",
|
||||||
|
"Genero Forms",
|
||||||
|
"Genie",
|
||||||
|
"Genshi",
|
||||||
|
"Gentoo Ebuild",
|
||||||
|
"Gentoo Eclass",
|
||||||
|
"Gerber Image",
|
||||||
|
"Gettext Catalog",
|
||||||
|
"Gherkin",
|
||||||
|
"Git Attributes",
|
||||||
|
"Git Config",
|
||||||
|
"Git Revision List",
|
||||||
|
"Gleam",
|
||||||
|
"Glyph",
|
||||||
|
"Glyph Bitmap Distribution Format",
|
||||||
|
"Gnuplot",
|
||||||
|
"Go",
|
||||||
|
"Go Checksums",
|
||||||
|
"Go Module",
|
||||||
|
"Go Workspace",
|
||||||
|
"Godot Resource",
|
||||||
|
"Golo",
|
||||||
|
"Gosu",
|
||||||
|
"Grace",
|
||||||
|
"Gradle",
|
||||||
|
"Gradle Kotlin DSL",
|
||||||
|
"Grammatical Framework",
|
||||||
|
"Graph Modeling Language",
|
||||||
|
"GraphQL",
|
||||||
|
"Graphviz (DOT)",
|
||||||
|
"Groovy",
|
||||||
|
"Groovy Server Pages",
|
||||||
|
"HAProxy",
|
||||||
|
"HCL",
|
||||||
|
"HLSL",
|
||||||
|
"HOCON",
|
||||||
|
"HTML",
|
||||||
|
"HTML+ECR",
|
||||||
|
"HTML+EEX",
|
||||||
|
"HTML+ERB",
|
||||||
|
"HTML+PHP",
|
||||||
|
"HTML+Razor",
|
||||||
|
"HTTP",
|
||||||
|
"HXML",
|
||||||
|
"Hack",
|
||||||
|
"Haml",
|
||||||
|
"Handlebars",
|
||||||
|
"Harbour",
|
||||||
|
"Haskell",
|
||||||
|
"Haxe",
|
||||||
|
"HiveQL",
|
||||||
|
"HolyC",
|
||||||
|
"Hosts File",
|
||||||
|
"Hy",
|
||||||
|
"HyPhy",
|
||||||
|
"IDL",
|
||||||
|
"IGOR Pro",
|
||||||
|
"INI",
|
||||||
|
"IRC log",
|
||||||
|
"Idris",
|
||||||
|
"Ignore List",
|
||||||
|
"ImageJ Macro",
|
||||||
|
"Imba",
|
||||||
|
"Inform 7",
|
||||||
|
"Ink",
|
||||||
|
"Inno Setup",
|
||||||
|
"Io",
|
||||||
|
"Ioke",
|
||||||
|
"Isabelle",
|
||||||
|
"Isabelle ROOT",
|
||||||
|
"J",
|
||||||
|
"JAR Manifest",
|
||||||
|
"JCL",
|
||||||
|
"JFlex",
|
||||||
|
"JSON",
|
||||||
|
"JSON with Comments",
|
||||||
|
"JSON5",
|
||||||
|
"JSONLD",
|
||||||
|
"JSONiq",
|
||||||
|
"Janet",
|
||||||
|
"Jasmin",
|
||||||
|
"Java",
|
||||||
|
"Java Properties",
|
||||||
|
"Java Server Pages",
|
||||||
|
"JavaScript",
|
||||||
|
"JavaScript+ERB",
|
||||||
|
"Jest Snapshot",
|
||||||
|
"JetBrains MPS",
|
||||||
|
"Jinja",
|
||||||
|
"Jison",
|
||||||
|
"Jison Lex",
|
||||||
|
"Jolie",
|
||||||
|
"Jsonnet",
|
||||||
|
"Julia",
|
||||||
|
"Jupyter Notebook",
|
||||||
|
"Just",
|
||||||
|
"KRL",
|
||||||
|
"Kaitai Struct",
|
||||||
|
"KakouneScript",
|
||||||
|
"KerboScript",
|
||||||
|
"KiCad Layout",
|
||||||
|
"KiCad Legacy Layout",
|
||||||
|
"KiCad Schematic",
|
||||||
|
"Kickstart",
|
||||||
|
"Kit",
|
||||||
|
"Kotlin",
|
||||||
|
"Kusto",
|
||||||
|
"LFE",
|
||||||
|
"LLVM",
|
||||||
|
"LOLCODE",
|
||||||
|
"LSL",
|
||||||
|
"LTspice Symbol",
|
||||||
|
"LabVIEW",
|
||||||
|
"Lark",
|
||||||
|
"Lasso",
|
||||||
|
"Latte",
|
||||||
|
"Lean",
|
||||||
|
"Less",
|
||||||
|
"Lex",
|
||||||
|
"LigoLANG",
|
||||||
|
"LilyPond",
|
||||||
|
"Limbo",
|
||||||
|
"Linker Script",
|
||||||
|
"Linux Kernel Module",
|
||||||
|
"Liquid",
|
||||||
|
"Literate Agda",
|
||||||
|
"Literate CoffeeScript",
|
||||||
|
"Literate Haskell",
|
||||||
|
"LiveScript",
|
||||||
|
"Logos",
|
||||||
|
"Logtalk",
|
||||||
|
"LookML",
|
||||||
|
"LoomScript",
|
||||||
|
"Lua",
|
||||||
|
"M",
|
||||||
|
"M4",
|
||||||
|
"M4Sugar",
|
||||||
|
"MATLAB",
|
||||||
|
"MAXScript",
|
||||||
|
"MDX",
|
||||||
|
"MLIR",
|
||||||
|
"MQL4",
|
||||||
|
"MQL5",
|
||||||
|
"MTML",
|
||||||
|
"MUF",
|
||||||
|
"Macaulay2",
|
||||||
|
"Makefile",
|
||||||
|
"Mako",
|
||||||
|
"Markdown",
|
||||||
|
"Marko",
|
||||||
|
"Mask",
|
||||||
|
"Mathematica",
|
||||||
|
"Maven POM",
|
||||||
|
"Max",
|
||||||
|
"Mercury",
|
||||||
|
"Mermaid",
|
||||||
|
"Meson",
|
||||||
|
"Metal",
|
||||||
|
"Microsoft Developer Studio Project",
|
||||||
|
"Microsoft Visual Studio Solution",
|
||||||
|
"MiniD",
|
||||||
|
"MiniYAML",
|
||||||
|
"Mint",
|
||||||
|
"Mirah",
|
||||||
|
"Modelica",
|
||||||
|
"Modula-2",
|
||||||
|
"Modula-3",
|
||||||
|
"Module Management System",
|
||||||
|
"Monkey",
|
||||||
|
"Monkey C",
|
||||||
|
"Moocode",
|
||||||
|
"MoonScript",
|
||||||
|
"Motoko",
|
||||||
|
"Motorola 68K Assembly",
|
||||||
|
"Move",
|
||||||
|
"Muse",
|
||||||
|
"Mustache",
|
||||||
|
"Myghty",
|
||||||
|
"NASL",
|
||||||
|
"NCL",
|
||||||
|
"NEON",
|
||||||
|
"NL",
|
||||||
|
"NPM Config",
|
||||||
|
"NSIS",
|
||||||
|
"NWScript",
|
||||||
|
"Nasal",
|
||||||
|
"Nearley",
|
||||||
|
"Nemerle",
|
||||||
|
"NetLinx",
|
||||||
|
"NetLinx+ERB",
|
||||||
|
"NetLogo",
|
||||||
|
"NewLisp",
|
||||||
|
"Nextflow",
|
||||||
|
"Nginx",
|
||||||
|
"Nim",
|
||||||
|
"Ninja",
|
||||||
|
"Nit",
|
||||||
|
"Nix",
|
||||||
|
"Nu",
|
||||||
|
"NumPy",
|
||||||
|
"Nunjucks",
|
||||||
|
"Nushell",
|
||||||
|
"OASv2-json",
|
||||||
|
"OASv2-yaml",
|
||||||
|
"OASv3-json",
|
||||||
|
"OASv3-yaml",
|
||||||
|
"OCaml",
|
||||||
|
"ObjDump",
|
||||||
|
"Object Data Instance Notation",
|
||||||
|
"ObjectScript",
|
||||||
|
"Objective-C",
|
||||||
|
"Objective-C++",
|
||||||
|
"Objective-J",
|
||||||
|
"Odin",
|
||||||
|
"Omgrofl",
|
||||||
|
"Opa",
|
||||||
|
"Opal",
|
||||||
|
"Open Policy Agent",
|
||||||
|
"OpenAPI Specification v2",
|
||||||
|
"OpenAPI Specification v3",
|
||||||
|
"OpenCL",
|
||||||
|
"OpenEdge ABL",
|
||||||
|
"OpenQASM",
|
||||||
|
"OpenRC runscript",
|
||||||
|
"OpenSCAD",
|
||||||
|
"OpenStep Property List",
|
||||||
|
"OpenType Feature File",
|
||||||
|
"Option List",
|
||||||
|
"Org",
|
||||||
|
"Ox",
|
||||||
|
"Oxygene",
|
||||||
|
"Oz",
|
||||||
|
"P4",
|
||||||
|
"PDDL",
|
||||||
|
"PEG.js",
|
||||||
|
"PHP",
|
||||||
|
"PLSQL",
|
||||||
|
"PLpgSQL",
|
||||||
|
"POV-Ray SDL",
|
||||||
|
"Pact",
|
||||||
|
"Pan",
|
||||||
|
"Papyrus",
|
||||||
|
"Parrot",
|
||||||
|
"Parrot Assembly",
|
||||||
|
"Parrot Internal Representation",
|
||||||
|
"Pascal",
|
||||||
|
"Pawn",
|
||||||
|
"Pep8",
|
||||||
|
"Perl",
|
||||||
|
"Pic",
|
||||||
|
"Pickle",
|
||||||
|
"PicoLisp",
|
||||||
|
"PigLatin",
|
||||||
|
"Pike",
|
||||||
|
"PlantUML",
|
||||||
|
"Pod",
|
||||||
|
"Pod 6",
|
||||||
|
"PogoScript",
|
||||||
|
"Polar",
|
||||||
|
"Pony",
|
||||||
|
"Portugol",
|
||||||
|
"PostCSS",
|
||||||
|
"PostScript",
|
||||||
|
"PowerBuilder",
|
||||||
|
"PowerShell",
|
||||||
|
"Prisma",
|
||||||
|
"Processing",
|
||||||
|
"Procfile",
|
||||||
|
"Proguard",
|
||||||
|
"Prolog",
|
||||||
|
"Promela",
|
||||||
|
"Propeller Spin",
|
||||||
|
"Protocol Buffer",
|
||||||
|
"Protocol Buffer Text Format",
|
||||||
|
"Public Key",
|
||||||
|
"Pug",
|
||||||
|
"Puppet",
|
||||||
|
"Pure Data",
|
||||||
|
"PureBasic",
|
||||||
|
"PureScript",
|
||||||
|
"Pyret",
|
||||||
|
"Python",
|
||||||
|
"Python console",
|
||||||
|
"Python traceback",
|
||||||
|
"Q#",
|
||||||
|
"QML",
|
||||||
|
"QMake",
|
||||||
|
"Qt Script",
|
||||||
|
"Quake",
|
||||||
|
"R",
|
||||||
|
"RAML",
|
||||||
|
"RBS",
|
||||||
|
"RDoc",
|
||||||
|
"REALbasic",
|
||||||
|
"REXX",
|
||||||
|
"RMarkdown",
|
||||||
|
"RPC",
|
||||||
|
"RPGLE",
|
||||||
|
"RPM Spec",
|
||||||
|
"RUNOFF",
|
||||||
|
"Racket",
|
||||||
|
"Ragel",
|
||||||
|
"Raku",
|
||||||
|
"Rascal",
|
||||||
|
"Raw token data",
|
||||||
|
"ReScript",
|
||||||
|
"Readline Config",
|
||||||
|
"Reason",
|
||||||
|
"ReasonLIGO",
|
||||||
|
"Rebol",
|
||||||
|
"Record Jar",
|
||||||
|
"Red",
|
||||||
|
"Redcode",
|
||||||
|
"Redirect Rules",
|
||||||
|
"Regular Expression",
|
||||||
|
"Ren'Py",
|
||||||
|
"RenderScript",
|
||||||
|
"Rez",
|
||||||
|
"Rich Text Format",
|
||||||
|
"Ring",
|
||||||
|
"Riot",
|
||||||
|
"RobotFramework",
|
||||||
|
"Roff",
|
||||||
|
"Roff Manpage",
|
||||||
|
"Rouge",
|
||||||
|
"RouterOS Script",
|
||||||
|
"Ruby",
|
||||||
|
"Rust",
|
||||||
|
"SAS",
|
||||||
|
"SCSS",
|
||||||
|
"SELinux Policy",
|
||||||
|
"SMT",
|
||||||
|
"SPARQL",
|
||||||
|
"SQF",
|
||||||
|
"SQL",
|
||||||
|
"SQLPL",
|
||||||
|
"SRecode Template",
|
||||||
|
"SSH Config",
|
||||||
|
"STAR",
|
||||||
|
"STL",
|
||||||
|
"STON",
|
||||||
|
"SVG",
|
||||||
|
"SWIG",
|
||||||
|
"Sage",
|
||||||
|
"SaltStack",
|
||||||
|
"Sass",
|
||||||
|
"Scala",
|
||||||
|
"Scaml",
|
||||||
|
"Scenic",
|
||||||
|
"Scheme",
|
||||||
|
"Scilab",
|
||||||
|
"Self",
|
||||||
|
"ShaderLab",
|
||||||
|
"Shell",
|
||||||
|
"ShellCheck Config",
|
||||||
|
"ShellSession",
|
||||||
|
"Shen",
|
||||||
|
"Sieve",
|
||||||
|
"Simple File Verification",
|
||||||
|
"Singularity",
|
||||||
|
"Slash",
|
||||||
|
"Slice",
|
||||||
|
"Slim",
|
||||||
|
"SmPL",
|
||||||
|
"Smali",
|
||||||
|
"Smalltalk",
|
||||||
|
"Smarty",
|
||||||
|
"Smithy",
|
||||||
|
"Snakemake",
|
||||||
|
"Solidity",
|
||||||
|
"Soong",
|
||||||
|
"SourcePawn",
|
||||||
|
"Spline Font Database",
|
||||||
|
"Squirrel",
|
||||||
|
"Stan",
|
||||||
|
"Standard ML",
|
||||||
|
"Starlark",
|
||||||
|
"Stata",
|
||||||
|
"StringTemplate",
|
||||||
|
"Stylus",
|
||||||
|
"SubRip Text",
|
||||||
|
"SugarSS",
|
||||||
|
"SuperCollider",
|
||||||
|
"Svelte",
|
||||||
|
"Sway",
|
||||||
|
"Sweave",
|
||||||
|
"Swift",
|
||||||
|
"SystemVerilog",
|
||||||
|
"TI Program",
|
||||||
|
"TL-Verilog",
|
||||||
|
"TLA",
|
||||||
|
"TOML",
|
||||||
|
"TSQL",
|
||||||
|
"TSV",
|
||||||
|
"TSX",
|
||||||
|
"TXL",
|
||||||
|
"Talon",
|
||||||
|
"Tcl",
|
||||||
|
"Tcsh",
|
||||||
|
"TeX",
|
||||||
|
"Tea",
|
||||||
|
"Terra",
|
||||||
|
"Texinfo",
|
||||||
|
"Text",
|
||||||
|
"TextMate Properties",
|
||||||
|
"Textile",
|
||||||
|
"Thrift",
|
||||||
|
"Turing",
|
||||||
|
"Turtle",
|
||||||
|
"Twig",
|
||||||
|
"Type Language",
|
||||||
|
"TypeScript",
|
||||||
|
"Typst",
|
||||||
|
"Unified Parallel C",
|
||||||
|
"Unity3D Asset",
|
||||||
|
"Unix Assembly",
|
||||||
|
"Uno",
|
||||||
|
"UnrealScript",
|
||||||
|
"UrWeb",
|
||||||
|
"V",
|
||||||
|
"VBA",
|
||||||
|
"VBScript",
|
||||||
|
"VCL",
|
||||||
|
"VHDL",
|
||||||
|
"Vala",
|
||||||
|
"Valve Data Format",
|
||||||
|
"Velocity Template Language",
|
||||||
|
"Verilog",
|
||||||
|
"Vim Help File",
|
||||||
|
"Vim Script",
|
||||||
|
"Vim Snippet",
|
||||||
|
"Visual Basic .NET",
|
||||||
|
"Visual Basic 6.0",
|
||||||
|
"Volt",
|
||||||
|
"Vue",
|
||||||
|
"Vyper",
|
||||||
|
"WDL",
|
||||||
|
"WGSL",
|
||||||
|
"Wavefront Material",
|
||||||
|
"Wavefront Object",
|
||||||
|
"Web Ontology Language",
|
||||||
|
"WebAssembly",
|
||||||
|
"WebAssembly Interface Type",
|
||||||
|
"WebIDL",
|
||||||
|
"WebVTT",
|
||||||
|
"Wget Config",
|
||||||
|
"Whiley",
|
||||||
|
"Wikitext",
|
||||||
|
"Win32 Message File",
|
||||||
|
"Windows Registry Entries",
|
||||||
|
"Witcher Script",
|
||||||
|
"Wollok",
|
||||||
|
"World of Warcraft Addon Data",
|
||||||
|
"Wren",
|
||||||
|
"X BitMap",
|
||||||
|
"X Font Directory Index",
|
||||||
|
"X PixMap",
|
||||||
|
"X10",
|
||||||
|
"XC",
|
||||||
|
"XCompose",
|
||||||
|
"XML",
|
||||||
|
"XML Property List",
|
||||||
|
"XPages",
|
||||||
|
"XProc",
|
||||||
|
"XQuery",
|
||||||
|
"XS",
|
||||||
|
"XSLT",
|
||||||
|
"Xojo",
|
||||||
|
"Xonsh",
|
||||||
|
"Xtend",
|
||||||
|
"YAML",
|
||||||
|
"YANG",
|
||||||
|
"YARA",
|
||||||
|
"YASnippet",
|
||||||
|
"Yacc",
|
||||||
|
"Yul",
|
||||||
|
"ZAP",
|
||||||
|
"ZIL",
|
||||||
|
"Zeek",
|
||||||
|
"ZenScript",
|
||||||
|
"Zephir",
|
||||||
|
"Zig",
|
||||||
|
"Zimpl",
|
||||||
|
"cURL Config",
|
||||||
|
"desktop",
|
||||||
|
"dircolors",
|
||||||
|
"eC",
|
||||||
|
"edn",
|
||||||
|
"fish",
|
||||||
|
"hoon",
|
||||||
|
"jq",
|
||||||
|
"kvlang",
|
||||||
|
"mIRC Script",
|
||||||
|
"mcfunction",
|
||||||
|
"mupad",
|
||||||
|
"nanorc",
|
||||||
|
"nesC",
|
||||||
|
"ooc",
|
||||||
|
"q",
|
||||||
|
"reStructuredText",
|
||||||
|
"robots.txt",
|
||||||
|
"sed",
|
||||||
|
"wisp",
|
||||||
|
"xBase",
|
||||||
|
]
|
||||||
|
|
||||||
|
export default languages;
|
||||||
292
packages/web/src/app/components/searchBar/searchBar.tsx
Normal file
292
packages/web/src/app/components/searchBar/searchBar.tsx
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useTailwind } from "@/hooks/useTailwind";
|
||||||
|
import { Repository, SearchQueryParams } from "@/lib/types";
|
||||||
|
import { cn, createPathWithQueryParams } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
cursorCharLeft,
|
||||||
|
cursorCharRight,
|
||||||
|
cursorDocEnd,
|
||||||
|
cursorDocStart,
|
||||||
|
cursorLineBoundaryBackward,
|
||||||
|
cursorLineBoundaryForward,
|
||||||
|
deleteCharBackward,
|
||||||
|
deleteCharForward,
|
||||||
|
deleteGroupBackward,
|
||||||
|
deleteGroupForward,
|
||||||
|
deleteLineBoundaryBackward,
|
||||||
|
deleteLineBoundaryForward,
|
||||||
|
history,
|
||||||
|
historyKeymap,
|
||||||
|
selectAll,
|
||||||
|
selectCharLeft,
|
||||||
|
selectCharRight,
|
||||||
|
selectDocEnd,
|
||||||
|
selectDocStart,
|
||||||
|
selectLineBoundaryBackward,
|
||||||
|
selectLineBoundaryForward
|
||||||
|
} from "@codemirror/commands";
|
||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
import CodeMirror, { Annotation, EditorView, KeyBinding, keymap, ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
||||||
|
import { cva } from "class-variance-authority";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
import { SearchSuggestionsBox, Suggestion } from "./searchSuggestionsBox";
|
||||||
|
import { useClickListener } from "@/hooks/useClickListener";
|
||||||
|
import { getRepos } from "../../api/(client)/client";
|
||||||
|
import languages from "./languages";
|
||||||
|
import { zoekt } from "./zoektLanguageExtension";
|
||||||
|
|
||||||
|
interface SearchBarProps {
|
||||||
|
className?: string;
|
||||||
|
size?: "default" | "sm";
|
||||||
|
defaultQuery?: string;
|
||||||
|
autoFocus?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchBarKeymap: readonly KeyBinding[] = ([
|
||||||
|
{ key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
|
||||||
|
{ key: "ArrowRight", run: cursorCharRight, shift: selectCharRight, preventDefault: true },
|
||||||
|
|
||||||
|
{ key: "Home", run: cursorLineBoundaryBackward, shift: selectLineBoundaryBackward, preventDefault: true },
|
||||||
|
{ key: "Mod-Home", run: cursorDocStart, shift: selectDocStart },
|
||||||
|
|
||||||
|
{ key: "End", run: cursorLineBoundaryForward, shift: selectLineBoundaryForward, preventDefault: true },
|
||||||
|
{ key: "Mod-End", run: cursorDocEnd, shift: selectDocEnd },
|
||||||
|
|
||||||
|
{ key: "Mod-a", run: selectAll },
|
||||||
|
|
||||||
|
{ key: "Backspace", run: deleteCharBackward, shift: deleteCharBackward },
|
||||||
|
{ key: "Delete", run: deleteCharForward },
|
||||||
|
{ key: "Mod-Backspace", mac: "Alt-Backspace", run: deleteGroupBackward },
|
||||||
|
{ key: "Mod-Delete", mac: "Alt-Delete", run: deleteGroupForward },
|
||||||
|
{ mac: "Mod-Backspace", run: deleteLineBoundaryBackward },
|
||||||
|
{ mac: "Mod-Delete", run: deleteLineBoundaryForward }
|
||||||
|
] as KeyBinding[]).concat(historyKeymap);
|
||||||
|
|
||||||
|
const searchBarContainerVariants = cva(
|
||||||
|
"search-bar-container flex items-center p-0.5 border rounded-md relative",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
default: "h-10",
|
||||||
|
sm: "h-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
size: "default",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SearchBar = ({
|
||||||
|
className,
|
||||||
|
size,
|
||||||
|
defaultQuery,
|
||||||
|
autoFocus,
|
||||||
|
}: SearchBarProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const tailwind = useTailwind();
|
||||||
|
const suggestionBoxRef = useRef<HTMLDivElement>(null);
|
||||||
|
const editorRef = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
const [cursorPosition, setCursorPosition] = useState(0);
|
||||||
|
const [isSuggestionsBoxEnabled, setIsSuggestionsBoxEnabled ] = useState(false);
|
||||||
|
const [isSuggestionsBoxFocused, setIsSuggestionsBoxFocused] = useState(false);
|
||||||
|
|
||||||
|
const focusEditor = useCallback(() => editorRef.current?.view?.focus(), []);
|
||||||
|
const focusSuggestionsBox = useCallback(() => suggestionBoxRef.current?.focus(), []);
|
||||||
|
|
||||||
|
const [_query, setQuery] = useState(defaultQuery ?? "");
|
||||||
|
const query = useMemo(() => {
|
||||||
|
// Replace any newlines with spaces to handle
|
||||||
|
// copy & pasting text with newlines.
|
||||||
|
return _query.replaceAll(/\n/g, " ");
|
||||||
|
}, [_query]);
|
||||||
|
|
||||||
|
const [repos, setRepos] = useState<Repository[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
getRepos().then((response) => {
|
||||||
|
setRepos(response.List.Repos.map(r => r.Repository));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const suggestionData = useMemo(() => {
|
||||||
|
const repoSuggestions: Suggestion[] = repos.map((repo) => {
|
||||||
|
return {
|
||||||
|
value: repo.Name,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const languageSuggestions: Suggestion[] = languages.map((lang) => {
|
||||||
|
const spotlight = [
|
||||||
|
"Python",
|
||||||
|
"Java",
|
||||||
|
"TypeScript",
|
||||||
|
"Go",
|
||||||
|
"C++",
|
||||||
|
"C#"
|
||||||
|
].includes(lang);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: lang,
|
||||||
|
spotlight,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
repos: repoSuggestions,
|
||||||
|
languages: languageSuggestions,
|
||||||
|
}
|
||||||
|
}, [repos]);
|
||||||
|
|
||||||
|
const theme = useMemo(() => {
|
||||||
|
return createTheme({
|
||||||
|
theme: 'light',
|
||||||
|
settings: {
|
||||||
|
background: tailwind.theme.colors.background,
|
||||||
|
foreground: tailwind.theme.colors.foreground,
|
||||||
|
caret: '#AEAFAD',
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{
|
||||||
|
tag: t.keyword,
|
||||||
|
color: tailwind.theme.colors.highlight,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: t.paren,
|
||||||
|
color: tailwind.theme.colors.highlight,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}, [tailwind]);
|
||||||
|
|
||||||
|
const extensions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
keymap.of(searchBarKeymap),
|
||||||
|
history(),
|
||||||
|
zoekt(),
|
||||||
|
EditorView.updateListener.of(update => {
|
||||||
|
if (update.selectionSet) {
|
||||||
|
const selection = update.state.selection.main;
|
||||||
|
if (selection.empty) {
|
||||||
|
setCursorPosition(selection.anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Hotkey to focus the search bar.
|
||||||
|
useHotkeys('/', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
focusEditor();
|
||||||
|
setIsSuggestionsBoxEnabled(true);
|
||||||
|
if (editorRef.current?.view) {
|
||||||
|
cursorDocEnd({
|
||||||
|
state: editorRef.current.view.state,
|
||||||
|
dispatch: editorRef.current.view.dispatch,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Collapse the suggestions box if the user clicks outside of the search bar container.
|
||||||
|
useClickListener('.search-bar-container', (isElementClicked) => {
|
||||||
|
if (!isElementClicked) {
|
||||||
|
setIsSuggestionsBoxEnabled(false);
|
||||||
|
} else {
|
||||||
|
setIsSuggestionsBoxEnabled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
const url = createPathWithQueryParams('/search',
|
||||||
|
[SearchQueryParams.query, query],
|
||||||
|
)
|
||||||
|
router.push(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(searchBarContainerVariants({ size, className }))}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsSuggestionsBoxEnabled(false);
|
||||||
|
onSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsSuggestionsBoxEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsSuggestionsBoxEnabled(true);
|
||||||
|
focusSuggestionsBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CodeMirror
|
||||||
|
ref={editorRef}
|
||||||
|
className="overflow-x-auto scrollbar-hide w-full"
|
||||||
|
placeholder={"Search..."}
|
||||||
|
value={query}
|
||||||
|
onChange={(value) => {
|
||||||
|
setQuery(value);
|
||||||
|
// Whenever the user types, we want to re-enable
|
||||||
|
// the suggestions box.
|
||||||
|
setIsSuggestionsBoxEnabled(true);
|
||||||
|
}}
|
||||||
|
theme={theme}
|
||||||
|
basicSetup={false}
|
||||||
|
extensions={extensions}
|
||||||
|
indentWithTab={false}
|
||||||
|
autoFocus={autoFocus ?? false}
|
||||||
|
/>
|
||||||
|
<SearchSuggestionsBox
|
||||||
|
ref={suggestionBoxRef}
|
||||||
|
query={query}
|
||||||
|
onCompletion={(newQuery: string, newCursorPosition: number) => {
|
||||||
|
setQuery(newQuery);
|
||||||
|
|
||||||
|
// Move the cursor to it's new position.
|
||||||
|
// @note : normally, react-codemirror handles syncing `query`
|
||||||
|
// and the document state, but this happens on re-render. Since
|
||||||
|
// we want to move the cursor before the component re-renders,
|
||||||
|
// we manually update the document state inline.
|
||||||
|
editorRef.current?.view?.dispatch({
|
||||||
|
changes: { from: 0, to: query.length, insert: newQuery },
|
||||||
|
annotations: [Annotation.define<boolean>().of(true)],
|
||||||
|
});
|
||||||
|
|
||||||
|
editorRef.current?.view?.dispatch({
|
||||||
|
selection: { anchor: newCursorPosition, head: newCursorPosition },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-focus the editor since suggestions cause focus to be lost (both click & keyboard)
|
||||||
|
editorRef.current?.view?.focus();
|
||||||
|
}}
|
||||||
|
isEnabled={isSuggestionsBoxEnabled}
|
||||||
|
onReturnFocus={() => {
|
||||||
|
focusEditor();
|
||||||
|
}}
|
||||||
|
isFocused={isSuggestionsBoxFocused}
|
||||||
|
onFocus={() => {
|
||||||
|
setIsSuggestionsBoxFocused(document.activeElement === suggestionBoxRef.current);
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
setIsSuggestionsBoxFocused(document.activeElement === suggestionBoxRef.current);
|
||||||
|
}}
|
||||||
|
cursorPosition={cursorPosition}
|
||||||
|
data={suggestionData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
import { expect, test } from 'vitest'
|
||||||
|
import { completeSuggestion, splitQuery } from './searchSuggestionsBox'
|
||||||
|
|
||||||
|
test('splitQuery returns a single element when the query is empty', () => {
|
||||||
|
const { queryParts, cursorIndex } = splitQuery('', 0);
|
||||||
|
expect(cursorIndex).toEqual(0);
|
||||||
|
expect(queryParts).toEqual(['']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery splits on spaces', () => {
|
||||||
|
const query = String.raw`repo:^github\.com/example/example$ world`;
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, 0);
|
||||||
|
expect(queryParts).toEqual(query.split(" "));
|
||||||
|
expect(cursorIndex).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery groups parts that are in the same quote capture group into a single part', () => {
|
||||||
|
const part1 = 'lang:"1C Enterprise"';
|
||||||
|
const part2 = "hello";
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(`${part1} ${part2}`, 12);
|
||||||
|
expect(queryParts).toEqual([part1, part2]);
|
||||||
|
expect(cursorIndex).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery does not support nested quote capture groups', () => {
|
||||||
|
const { queryParts } = splitQuery('lang:"My language "with quotes"" hello', 0);
|
||||||
|
expect(queryParts).toEqual(['lang:"My language "with', 'quotes""', 'hello']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery groups all parts together when a quote capture group is not closed', () => {
|
||||||
|
const query = '"hello asdf ok'
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, 0);
|
||||||
|
expect(queryParts).toEqual([query]);
|
||||||
|
expect(cursorIndex).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery correclty locates the cursor index given the cursor position (1)', () => {
|
||||||
|
const query = 'foo bar "fizz buzz"';
|
||||||
|
|
||||||
|
const { queryParts: parts1, cursorIndex: index1 } = splitQuery(query, 0);
|
||||||
|
expect(parts1).toEqual(['foo', 'bar', '"fizz buzz"']);
|
||||||
|
expect(parts1[index1]).toBe('foo');
|
||||||
|
|
||||||
|
const { queryParts: parts2, cursorIndex: index2 } = splitQuery(query, 6);
|
||||||
|
expect(parts2).toEqual(['foo', 'bar', '"fizz buzz"']);
|
||||||
|
expect(parts2[index2]).toBe('bar');
|
||||||
|
|
||||||
|
const { queryParts: parts3, cursorIndex: index3 } = splitQuery(query, 15);
|
||||||
|
expect(parts3).toEqual(['foo', 'bar', '"fizz buzz"']);
|
||||||
|
expect(parts3[index3]).toBe('"fizz buzz"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery correclty locates the cursor index given the cursor position (2)', () => {
|
||||||
|
const query = 'a b';
|
||||||
|
expect(splitQuery(query, 0).cursorIndex).toBe(0);
|
||||||
|
expect(splitQuery(query, 1).cursorIndex).toBe(0);
|
||||||
|
expect(splitQuery(query, 2).cursorIndex).toBe(1);
|
||||||
|
expect(splitQuery(query, 3).cursorIndex).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery can handle multiple spaces adjacent', () => {
|
||||||
|
expect(splitQuery("a b ", 0).queryParts).toEqual(['a', '', '', 'b', '', '']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery locates the cursor index to the last query part when the cursor position is at the end of the query', () => {
|
||||||
|
const query = "as df";
|
||||||
|
const cursorPos = query.length;
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, cursorPos);
|
||||||
|
expect(cursorIndex).toBe(queryParts.length - 1);
|
||||||
|
expect(queryParts[cursorIndex]).toBe("df");
|
||||||
|
expect(queryParts).toEqual(['as', 'df']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('splitQuery sets the cursor index to 0 when the cursor position is out of bounds', () => {
|
||||||
|
const query = "hello world";
|
||||||
|
const cursorPos = query.length + 1;
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, cursorPos);
|
||||||
|
expect(cursorIndex).toBe(0);
|
||||||
|
expect(queryParts[cursorIndex]).toBe("hello");
|
||||||
|
expect(queryParts).toEqual(['hello', 'world']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion can complete a empty query', () => {
|
||||||
|
const suggestionQuery = ``;
|
||||||
|
const query = ``;
|
||||||
|
const suggestion = "hello";
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: false,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedNewQuery = String.raw`hello`;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(newQuery.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion can complete with a empty suggestion query', () => {
|
||||||
|
const suggestionQuery = ``;
|
||||||
|
const query = `case:`;
|
||||||
|
const suggestion = "auto";
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: false,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition: query.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedNewQuery = `case:auto`;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(newQuery.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion inserts a trailing space when trailingSpace is true and the completion is at the end of the query', () => {
|
||||||
|
const suggestionQuery = 'a';
|
||||||
|
const part1 = String.raw`lang:Go`;
|
||||||
|
const part2 = String.raw`case:${suggestionQuery}`;
|
||||||
|
const query = `${part1} ${part2}`
|
||||||
|
const suggestion = 'auto';
|
||||||
|
const cursorPosition = query.length;
|
||||||
|
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: true,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedPart2 = `case:auto`
|
||||||
|
const expectedNewQuery = `${part1} ${expectedPart2} `;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(newQuery.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion does not insert a trailing space when trailingSpace is true and the completion is not at the end of the query', () => {
|
||||||
|
const suggestionQuery = 'G';
|
||||||
|
const part1 = String.raw`lang:${suggestionQuery}`;
|
||||||
|
const part2 = String.raw`case:auto`;
|
||||||
|
const query = `${part1} ${part2}`
|
||||||
|
const suggestion = 'Go';
|
||||||
|
const cursorPosition = part1.length;
|
||||||
|
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: true,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedPart1 = `lang:Go`
|
||||||
|
const expectedNewQuery = `${expectedPart1} ${part2}`; // Notice no trailing space
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(expectedPart1.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion wraps suggestions in quotes when the suggestion contains a space and regexEscaped is false', () => {
|
||||||
|
const suggestionQuery = `m`;
|
||||||
|
const query = `lang:${suggestionQuery}`;
|
||||||
|
const suggestion = `my language`;
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: false,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition: query.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedNewQuery = `lang:"my language"`;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(newQuery.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestion completes on query parts that are inbetween other parts', () => {
|
||||||
|
const part1 = String.raw`repo:^github\.com/sourcebot\x2ddev/sourcebot$`;
|
||||||
|
const suggestionQuery = 'Type';
|
||||||
|
const part2 = String.raw`lang:${suggestionQuery}`;
|
||||||
|
const part3 = String.raw`case:auto`;
|
||||||
|
const query = `${part1} ${part2} ${part3}`;
|
||||||
|
const suggestion = 'TypeScript';
|
||||||
|
const cursorPosition = ([part1, part2].join(" ").length);
|
||||||
|
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace: false,
|
||||||
|
regexEscaped: false,
|
||||||
|
cursorPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedPart2 = "lang:TypeScript";
|
||||||
|
const expectedNewQuery = String.raw`${part1} ${expectedPart2} ${part3}`;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe([part1, expectedPart2].join(" ").length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completeSuggestions regex escapes suggestions when regexEscaped is true', () => {
|
||||||
|
const query = "repo:github";
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
suggestionQuery: "github",
|
||||||
|
suggestion: "github.com/sourcebot-dev/sourcebot",
|
||||||
|
trailingSpace: true,
|
||||||
|
regexEscaped: true,
|
||||||
|
cursorPosition: query.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedNewQuery = String.raw`repo:^github\.com/sourcebot\x2ddev/sourcebot$ `;
|
||||||
|
expect(newQuery).toEqual(expectedNewQuery);
|
||||||
|
expect(newCursorPosition).toBe(newQuery.length);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,453 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { isDefined } from "@/lib/utils";
|
||||||
|
import { CommitIcon, MixerVerticalIcon } from "@radix-ui/react-icons";
|
||||||
|
import { IconProps } from "@radix-ui/react-icons/dist/types";
|
||||||
|
import assert from "assert";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import escapeStringRegexp from "escape-string-regexp";
|
||||||
|
import Fuse from "fuse.js";
|
||||||
|
import { forwardRef, Ref, useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
archivedModeSuggestions,
|
||||||
|
caseModeSuggestions,
|
||||||
|
forkModeSuggestions,
|
||||||
|
publicModeSuggestions,
|
||||||
|
refineModeSuggestions,
|
||||||
|
suggestionModeMappings
|
||||||
|
} from "./constants";
|
||||||
|
|
||||||
|
type Icon = React.ForwardRefExoticComponent<IconProps & React.RefAttributes<SVGSVGElement>>;
|
||||||
|
|
||||||
|
export type Suggestion = {
|
||||||
|
value: string;
|
||||||
|
description?: string;
|
||||||
|
spotlight?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SuggestionMode =
|
||||||
|
"refine" |
|
||||||
|
"archived" |
|
||||||
|
"file" |
|
||||||
|
"language" |
|
||||||
|
"case" |
|
||||||
|
"fork" |
|
||||||
|
"public" |
|
||||||
|
"revision" |
|
||||||
|
"symbol" |
|
||||||
|
"content" |
|
||||||
|
"repo";
|
||||||
|
|
||||||
|
interface SearchSuggestionsBoxProps {
|
||||||
|
query: string;
|
||||||
|
onCompletion: (newQuery: string, newCursorPosition: number) => void,
|
||||||
|
isEnabled: boolean;
|
||||||
|
cursorPosition: number;
|
||||||
|
isFocused: boolean;
|
||||||
|
onFocus: () => void;
|
||||||
|
onBlur: () => void;
|
||||||
|
onReturnFocus: () => void;
|
||||||
|
|
||||||
|
data: {
|
||||||
|
repos: Suggestion[];
|
||||||
|
languages: Suggestion[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchSuggestionsBox = forwardRef(({
|
||||||
|
query,
|
||||||
|
onCompletion,
|
||||||
|
isEnabled,
|
||||||
|
data,
|
||||||
|
cursorPosition,
|
||||||
|
isFocused,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
onReturnFocus,
|
||||||
|
}: SearchSuggestionsBoxProps, ref: Ref<HTMLDivElement>) => {
|
||||||
|
|
||||||
|
const [highlightedSuggestionIndex, setHighlightedSuggestionIndex] = useState(0);
|
||||||
|
|
||||||
|
const { suggestionQuery, suggestionMode } = useMemo<{ suggestionQuery?: string, suggestionMode?: SuggestionMode }>(() => {
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, cursorPosition);
|
||||||
|
if (queryParts.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const part = queryParts[cursorIndex];
|
||||||
|
|
||||||
|
// Check if the query part starts with one of the
|
||||||
|
// prefixes. If it does, then we are in the corresponding
|
||||||
|
// suggestion mode for that prefix.
|
||||||
|
const suggestionMode = (() => {
|
||||||
|
for (const mapping of suggestionModeMappings) {
|
||||||
|
for (const prefix of mapping.prefixes) {
|
||||||
|
if (part.startsWith(prefix)) {
|
||||||
|
return mapping.suggestionMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (suggestionMode) {
|
||||||
|
const index = part.indexOf(":");
|
||||||
|
return {
|
||||||
|
suggestionQuery: part.substring(index + 1),
|
||||||
|
suggestionMode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to the refine suggestion mode
|
||||||
|
// if there was no match.
|
||||||
|
return {
|
||||||
|
suggestionQuery: part,
|
||||||
|
suggestionMode: "refine",
|
||||||
|
}
|
||||||
|
}, [cursorPosition, query]);
|
||||||
|
|
||||||
|
const { suggestions, isHighlightEnabled, Icon, onSuggestionClicked } = useMemo(() => {
|
||||||
|
if (!isDefined(suggestionQuery) || !isDefined(suggestionMode)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOnSuggestionClickedHandler = (params: { regexEscaped?: boolean, trailingSpace?: boolean } = {}) => {
|
||||||
|
const {
|
||||||
|
regexEscaped = false,
|
||||||
|
trailingSpace = true
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const onSuggestionClicked = (suggestion: string) => {
|
||||||
|
const { newQuery, newCursorPosition } = completeSuggestion({
|
||||||
|
query,
|
||||||
|
cursorPosition,
|
||||||
|
regexEscaped,
|
||||||
|
trailingSpace,
|
||||||
|
suggestion,
|
||||||
|
suggestionQuery,
|
||||||
|
});
|
||||||
|
|
||||||
|
onCompletion(newQuery, newCursorPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return onSuggestionClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
threshold = 0.5,
|
||||||
|
limit = 10,
|
||||||
|
list,
|
||||||
|
isHighlightEnabled = false,
|
||||||
|
isSpotlightEnabled = false,
|
||||||
|
onSuggestionClicked,
|
||||||
|
Icon,
|
||||||
|
} = ((): {
|
||||||
|
threshold?: number,
|
||||||
|
limit?: number,
|
||||||
|
list: Suggestion[],
|
||||||
|
isHighlightEnabled?: boolean,
|
||||||
|
isSpotlightEnabled?: boolean,
|
||||||
|
onSuggestionClicked: (value: string) => void,
|
||||||
|
Icon?: Icon
|
||||||
|
} => {
|
||||||
|
switch (suggestionMode) {
|
||||||
|
case "public":
|
||||||
|
return {
|
||||||
|
list: publicModeSuggestions,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
}
|
||||||
|
case "fork":
|
||||||
|
return {
|
||||||
|
list: forkModeSuggestions,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
}
|
||||||
|
case "case":
|
||||||
|
return {
|
||||||
|
list: caseModeSuggestions,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
}
|
||||||
|
case "archived":
|
||||||
|
return {
|
||||||
|
list: archivedModeSuggestions,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
}
|
||||||
|
case "repo":
|
||||||
|
return {
|
||||||
|
list: data.repos,
|
||||||
|
Icon: CommitIcon,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler({ regexEscaped: true }),
|
||||||
|
}
|
||||||
|
case "language": {
|
||||||
|
return {
|
||||||
|
list: data.languages,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
isSpotlightEnabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "refine":
|
||||||
|
return {
|
||||||
|
threshold: 0.1,
|
||||||
|
list: refineModeSuggestions,
|
||||||
|
isHighlightEnabled: true,
|
||||||
|
isSpotlightEnabled: true,
|
||||||
|
Icon: MixerVerticalIcon,
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler({ trailingSpace: false }),
|
||||||
|
}
|
||||||
|
case "file":
|
||||||
|
case "revision":
|
||||||
|
case "content":
|
||||||
|
case "symbol":
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
onSuggestionClicked: createOnSuggestionClickedHandler(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const fuse = new Fuse(list, {
|
||||||
|
threshold,
|
||||||
|
keys: ['value'],
|
||||||
|
isCaseSensitive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const suggestions = (() => {
|
||||||
|
if (suggestionQuery.length === 0) {
|
||||||
|
// If spotlight is enabled, get the suggestions that are
|
||||||
|
// flagged to be surfaced.
|
||||||
|
if (isSpotlightEnabled) {
|
||||||
|
const spotlightSuggestions = list.filter((suggestion) => suggestion.spotlight);
|
||||||
|
return spotlightSuggestions;
|
||||||
|
|
||||||
|
// Otherwise, just show the Nth first suggestions.
|
||||||
|
} else {
|
||||||
|
return list.slice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: don't show any suggestions if the query
|
||||||
|
// is the keyword "or".
|
||||||
|
if (suggestionQuery === "or") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fuse.search(suggestionQuery, {
|
||||||
|
limit,
|
||||||
|
}).map(result => result.item);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return {
|
||||||
|
suggestions,
|
||||||
|
isHighlightEnabled,
|
||||||
|
Icon,
|
||||||
|
onSuggestionClicked,
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [suggestionQuery, suggestionMode, onCompletion, cursorPosition, data.repos, data.languages, query]);
|
||||||
|
|
||||||
|
// When the list of suggestions change, reset the highlight index
|
||||||
|
useEffect(() => {
|
||||||
|
setHighlightedSuggestionIndex(0);
|
||||||
|
}, [suggestions]);
|
||||||
|
|
||||||
|
const suggestionModeText = useMemo(() => {
|
||||||
|
if (!suggestionMode) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
switch (suggestionMode) {
|
||||||
|
case "repo":
|
||||||
|
return "Repositories";
|
||||||
|
case "refine":
|
||||||
|
return "Refine search"
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}, [suggestionMode]);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isEnabled ||
|
||||||
|
!suggestions ||
|
||||||
|
suggestions.length === 0
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className="w-full absolute z-10 top-12 border rounded-md bg-background drop-shadow-2xl p-2"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.stopPropagation();
|
||||||
|
const value = suggestions[highlightedSuggestionIndex].value;
|
||||||
|
onSuggestionClicked(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.stopPropagation();
|
||||||
|
setHighlightedSuggestionIndex((curIndex) => {
|
||||||
|
return curIndex <= 0 ? suggestions.length - 1 : curIndex - 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.stopPropagation();
|
||||||
|
setHighlightedSuggestionIndex((curIndex) => {
|
||||||
|
return curIndex >= suggestions.length - 1 ? 0 : curIndex + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.stopPropagation();
|
||||||
|
onReturnFocus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
>
|
||||||
|
<p className="text-muted-foreground text-sm mb-1">
|
||||||
|
{suggestionModeText}
|
||||||
|
</p>
|
||||||
|
{suggestions.map((result, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={clsx("flex flex-row items-center font-mono text-sm hover:bg-muted rounded-md px-1 py-0.5 cursor-pointer", {
|
||||||
|
"bg-muted": isFocused && index === highlightedSuggestionIndex,
|
||||||
|
})}
|
||||||
|
tabIndex={-1}
|
||||||
|
onClick={() => {
|
||||||
|
onSuggestionClicked(result.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Icon && (
|
||||||
|
<Icon className="w-3 h-3 mr-2" />
|
||||||
|
)}
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<span
|
||||||
|
className={clsx('mr-2 flex-none', {
|
||||||
|
"text-highlight": isHighlightEnabled
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{result.value}
|
||||||
|
</span>
|
||||||
|
{result.description && (
|
||||||
|
<span className="text-muted-foreground font-light">
|
||||||
|
{result.description}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{isFocused && (
|
||||||
|
<div className="flex flex-row items-center justify-end mt-1">
|
||||||
|
<span className="text-muted-foreground text-xs">
|
||||||
|
Press <kbd className="font-mono text-xs font-bold">Enter</kbd> to select
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
SearchSuggestionsBox.displayName = "SearchSuggestionsBox";
|
||||||
|
export { SearchSuggestionsBox };
|
||||||
|
|
||||||
|
export const splitQuery = (query: string, cursorPos: number) => {
|
||||||
|
const queryParts = [];
|
||||||
|
const seperator = " ";
|
||||||
|
let cursorIndex = 0;
|
||||||
|
let accumulator = "";
|
||||||
|
let isInQuoteCapture = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < query.length; i++) {
|
||||||
|
if (i === cursorPos) {
|
||||||
|
cursorIndex = queryParts.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query[i] === "\"") {
|
||||||
|
isInQuoteCapture = !isInQuoteCapture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isInQuoteCapture && query[i] === seperator) {
|
||||||
|
queryParts.push(accumulator);
|
||||||
|
accumulator = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulator += query[i];
|
||||||
|
}
|
||||||
|
queryParts.push(accumulator);
|
||||||
|
|
||||||
|
// Edge case: if the cursor is at the end of the query, set the cursor index to the last query part
|
||||||
|
if (cursorPos === query.length) {
|
||||||
|
cursorIndex = queryParts.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @note: since we're guaranteed to have at least one query part, we can safely assume that the cursor position
|
||||||
|
// will be within bounds.
|
||||||
|
assert(cursorIndex >= 0 && cursorIndex < queryParts.length, "Cursor position is out of bounds");
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryParts,
|
||||||
|
cursorIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const completeSuggestion = (params: {
|
||||||
|
query: string,
|
||||||
|
suggestionQuery: string,
|
||||||
|
cursorPosition: number,
|
||||||
|
suggestion: string,
|
||||||
|
trailingSpace: boolean,
|
||||||
|
regexEscaped: boolean,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
suggestionQuery,
|
||||||
|
cursorPosition,
|
||||||
|
suggestion,
|
||||||
|
trailingSpace,
|
||||||
|
regexEscaped,
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const { queryParts, cursorIndex } = splitQuery(query, cursorPosition);
|
||||||
|
|
||||||
|
const start = queryParts.slice(0, cursorIndex).join(" ");
|
||||||
|
const end = queryParts.slice(cursorIndex + 1).join(" ");
|
||||||
|
|
||||||
|
let part = queryParts[cursorIndex];
|
||||||
|
|
||||||
|
// Remove whatever query we have in the suggestion so far (if any).
|
||||||
|
// For example, if our part is "repo:gith", then we want to remove "gith"
|
||||||
|
// from the part before we complete the suggestion.
|
||||||
|
if (suggestionQuery.length > 0) {
|
||||||
|
part = part.slice(0, -suggestionQuery.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regexEscaped) {
|
||||||
|
part = part + `^${escapeStringRegexp(suggestion)}$`;
|
||||||
|
} else if (suggestion.includes(" ")) {
|
||||||
|
part = part + `"${suggestion}"`;
|
||||||
|
} else {
|
||||||
|
part = part + suggestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a trailing space if we are at the end of the query
|
||||||
|
if (trailingSpace && cursorIndex === queryParts.length - 1) {
|
||||||
|
part += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
let newQuery = [
|
||||||
|
...(start.length > 0 ? [start] : []),
|
||||||
|
part,
|
||||||
|
].join(" ");
|
||||||
|
const newCursorPosition = newQuery.length;
|
||||||
|
|
||||||
|
newQuery = [
|
||||||
|
newQuery,
|
||||||
|
...(end.length > 0 ? [end] : []),
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
return {
|
||||||
|
newQuery,
|
||||||
|
newCursorPosition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { LanguageSupport, StreamLanguage } from "@codemirror/language";
|
||||||
|
import { tags as t } from '@lezer/highlight';
|
||||||
|
|
||||||
|
const zoektLanguage = StreamLanguage.define({
|
||||||
|
token: (stream) => {
|
||||||
|
if (stream.match(/-?(file|branch|revision|rev|case|repo|lang|content|sym|archived|fork|public):/)) {
|
||||||
|
return t.keyword.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.match(/\bor\b/)) {
|
||||||
|
return t.keyword.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.match(/(\(|\))/)) {
|
||||||
|
return t.paren.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.next();
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zoekt = () => {
|
||||||
|
return new LanguageSupport(zoektLanguage);
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,12 @@ import Image from "next/image";
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import logoDark from "../../public/sb_logo_dark_large.png";
|
import logoDark from "../../public/sb_logo_dark_large.png";
|
||||||
import logoLight from "../../public/sb_logo_light_large.png";
|
import logoLight from "../../public/sb_logo_light_large.png";
|
||||||
import { NavigationMenu } from "./navigationMenu";
|
import { NavigationMenu } from "./components/navigationMenu";
|
||||||
import { RepositoryCarousel } from "./repositoryCarousel";
|
import { RepositoryCarousel } from "./components/repositoryCarousel";
|
||||||
import { SearchBar } from "./searchBar";
|
import { SearchBar } from "./components/searchBar";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { SymbolIcon } from "@radix-ui/react-icons";
|
import { SymbolIcon } from "@radix-ui/react-icons";
|
||||||
import { UpgradeToast } from "./upgradeToast";
|
import { UpgradeToast } from "./components/upgradeToast";
|
||||||
|
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
|
|
@ -18,7 +18,7 @@ export default async function Home() {
|
||||||
<NavigationMenu />
|
<NavigationMenu />
|
||||||
<UpgradeToast />
|
<UpgradeToast />
|
||||||
|
|
||||||
<div className="flex flex-col justify-center items-center mt-8 mb-8 md:mt-18 max-w-[90%]">
|
<div className="flex flex-col justify-center items-center mt-8 mb-8 md:mt-18 w-full px-5">
|
||||||
<div className="max-h-44 w-auto">
|
<div className="max-h-44 w-auto">
|
||||||
<Image
|
<Image
|
||||||
src={logoDark}
|
src={logoDark}
|
||||||
|
|
@ -33,18 +33,17 @@ export default async function Home() {
|
||||||
priority={true}
|
priority={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-row mt-4">
|
|
||||||
<SearchBar
|
<SearchBar
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
className="mt-4 w-full max-w-[800px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<Suspense fallback={<div>...</div>}>
|
<Suspense fallback={<div>...</div>}>
|
||||||
<RepositoryList />
|
<RepositoryList />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="mt-5 mb-8" />
|
|
||||||
<div className="flex flex-col items-center w-fit gap-6">
|
<div className="flex flex-col items-center w-fit gap-6">
|
||||||
|
<Separator className="mt-5" />
|
||||||
<span className="font-semibold">How to search</span>
|
<span className="font-semibold">How to search</span>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||||
<HowToSection
|
<HowToSection
|
||||||
|
|
@ -76,7 +75,7 @@ export default async function Home() {
|
||||||
<Query query="lang:typescript"><Highlight>lang:</Highlight>typescript</Query> <QueryExplanation>(by language)</QueryExplanation>
|
<Query query="lang:typescript"><Highlight>lang:</Highlight>typescript</Query> <QueryExplanation>(by language)</QueryExplanation>
|
||||||
</QueryExample>
|
</QueryExample>
|
||||||
<QueryExample>
|
<QueryExample>
|
||||||
<Query query="revision:HEAD"><Highlight>revision:</Highlight>HEAD</Query> <QueryExplanation>(by branch or tag)</QueryExplanation>
|
<Query query="rev:HEAD"><Highlight>rev:</Highlight>HEAD</Query> <QueryExplanation>(by branch or tag)</QueryExplanation>
|
||||||
</QueryExample>
|
</QueryExample>
|
||||||
</HowToSection>
|
</HowToSection>
|
||||||
<HowToSection
|
<HowToSection
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import { NavigationMenu } from "../navigationMenu";
|
import { NavigationMenu } from "../components/navigationMenu";
|
||||||
import { RepositoryTable } from "./repositoryTable";
|
import { RepositoryTable } from "./repositoryTable";
|
||||||
|
|
||||||
export default function ReposPage() {
|
export default function ReposPage() {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ export const FilterPanel = ({
|
||||||
"Language",
|
"Language",
|
||||||
matches,
|
matches,
|
||||||
(key) => {
|
(key) => {
|
||||||
// @todo: Get language icons
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
displayName: key,
|
displayName: key,
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import logoDark from "../../../public/sb_logo_dark.png";
|
import logoDark from "../../../public/sb_logo_dark.png";
|
||||||
import logoLight from "../../../public/sb_logo_light.png";
|
import logoLight from "../../../public/sb_logo_light.png";
|
||||||
import { search } from "../api/(client)/client";
|
import { search } from "../api/(client)/client";
|
||||||
import { SearchBar } from "../searchBar";
|
import { SearchBar } from "../components/searchBar";
|
||||||
import { SettingsDropdown } from "../settingsDropdown";
|
import { SettingsDropdown } from "../components/settingsDropdown";
|
||||||
import { CodePreviewPanel } from "./components/codePreviewPanel";
|
import { CodePreviewPanel } from "./components/codePreviewPanel";
|
||||||
import { FilterPanel } from "./components/filterPanel";
|
import { FilterPanel } from "./components/filterPanel";
|
||||||
import { SearchResultsPanel } from "./components/searchResultsPanel";
|
import { SearchResultsPanel } from "./components/searchResultsPanel";
|
||||||
|
|
@ -109,7 +109,7 @@ export default function SearchPage() {
|
||||||
totalMatchCount: searchResponse.Result.MatchCount,
|
totalMatchCount: searchResponse.Result.MatchCount,
|
||||||
isBranchFilteringEnabled,
|
isBranchFilteringEnabled,
|
||||||
}
|
}
|
||||||
}, [searchResponse, searchQuery]);
|
}, [searchResponse]);
|
||||||
|
|
||||||
const isMoreResultsButtonVisible = useMemo(() => {
|
const isMoreResultsButtonVisible = useMemo(() => {
|
||||||
return totalMatchCount > maxMatchDisplayCount;
|
return totalMatchCount > maxMatchDisplayCount;
|
||||||
|
|
@ -161,6 +161,7 @@ export default function SearchPage() {
|
||||||
<SearchBar
|
<SearchBar
|
||||||
size="sm"
|
size="sm"
|
||||||
defaultQuery={searchQuery}
|
defaultQuery={searchQuery}
|
||||||
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SettingsDropdown
|
<SettingsDropdown
|
||||||
|
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useTailwind } from "@/hooks/useTailwind";
|
|
||||||
import { SearchQueryParams } from "@/lib/types";
|
|
||||||
import { cn, createPathWithQueryParams } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
cursorCharLeft,
|
|
||||||
cursorCharRight,
|
|
||||||
cursorDocEnd,
|
|
||||||
cursorDocStart,
|
|
||||||
cursorLineBoundaryBackward,
|
|
||||||
cursorLineBoundaryForward,
|
|
||||||
deleteCharBackward,
|
|
||||||
deleteCharForward,
|
|
||||||
deleteGroupBackward,
|
|
||||||
deleteGroupForward,
|
|
||||||
deleteLineBoundaryBackward,
|
|
||||||
deleteLineBoundaryForward,
|
|
||||||
history,
|
|
||||||
historyKeymap,
|
|
||||||
selectAll,
|
|
||||||
selectCharLeft,
|
|
||||||
selectCharRight,
|
|
||||||
selectDocEnd,
|
|
||||||
selectDocStart,
|
|
||||||
selectLineBoundaryBackward,
|
|
||||||
selectLineBoundaryForward
|
|
||||||
} from "@codemirror/commands";
|
|
||||||
import { LanguageSupport, StreamLanguage } from "@codemirror/language";
|
|
||||||
import { tags as t } from '@lezer/highlight';
|
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
|
||||||
import CodeMirror, { KeyBinding, keymap, ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
|
||||||
import { cva } from "class-variance-authority";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useMemo, useRef, useState } from "react";
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
|
||||||
|
|
||||||
interface SearchBarProps {
|
|
||||||
className?: string;
|
|
||||||
size?: "default" | "sm";
|
|
||||||
defaultQuery?: string;
|
|
||||||
autoFocus?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchBarKeymap: readonly KeyBinding[] = ([
|
|
||||||
{ key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
|
|
||||||
{ key: "ArrowRight", run: cursorCharRight, shift: selectCharRight, preventDefault: true },
|
|
||||||
|
|
||||||
{ key: "Home", run: cursorLineBoundaryBackward, shift: selectLineBoundaryBackward, preventDefault: true },
|
|
||||||
{ key: "Mod-Home", run: cursorDocStart, shift: selectDocStart },
|
|
||||||
|
|
||||||
{ key: "End", run: cursorLineBoundaryForward, shift: selectLineBoundaryForward, preventDefault: true },
|
|
||||||
{ key: "Mod-End", run: cursorDocEnd, shift: selectDocEnd },
|
|
||||||
|
|
||||||
{ key: "Mod-a", run: selectAll },
|
|
||||||
|
|
||||||
{ key: "Backspace", run: deleteCharBackward, shift: deleteCharBackward },
|
|
||||||
{ key: "Delete", run: deleteCharForward },
|
|
||||||
{ key: "Mod-Backspace", mac: "Alt-Backspace", run: deleteGroupBackward },
|
|
||||||
{ key: "Mod-Delete", mac: "Alt-Delete", run: deleteGroupForward },
|
|
||||||
{ mac: "Mod-Backspace", run: deleteLineBoundaryBackward },
|
|
||||||
{ mac: "Mod-Delete", run: deleteLineBoundaryForward }
|
|
||||||
] as KeyBinding[]).concat(historyKeymap);
|
|
||||||
|
|
||||||
const zoektLanguage = StreamLanguage.define({
|
|
||||||
token: (stream) => {
|
|
||||||
if (stream.match(/-?(file|branch|revision|rev|case|repo|lang|content|sym):/)) {
|
|
||||||
return t.keyword.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.match(/\bor\b/)) {
|
|
||||||
return t.keyword.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.next();
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const zoekt = () =>{
|
|
||||||
return new LanguageSupport(zoektLanguage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const extensions = [
|
|
||||||
keymap.of(searchBarKeymap),
|
|
||||||
history(),
|
|
||||||
zoekt()
|
|
||||||
];
|
|
||||||
|
|
||||||
const searchBarVariants = cva(
|
|
||||||
"flex items-center w-full p-0.5 border rounded-md",
|
|
||||||
{
|
|
||||||
variants: {
|
|
||||||
size: {
|
|
||||||
default: "h-10",
|
|
||||||
sm: "h-8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
size: "default",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const SearchBar = ({
|
|
||||||
className,
|
|
||||||
size,
|
|
||||||
defaultQuery,
|
|
||||||
autoFocus,
|
|
||||||
}: SearchBarProps) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const tailwind = useTailwind();
|
|
||||||
|
|
||||||
const theme = useMemo(() => {
|
|
||||||
return createTheme({
|
|
||||||
theme: 'light',
|
|
||||||
settings: {
|
|
||||||
background: tailwind.theme.colors.background,
|
|
||||||
foreground: tailwind.theme.colors.foreground,
|
|
||||||
caret: '#AEAFAD',
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{
|
|
||||||
tag: t.keyword,
|
|
||||||
color: tailwind.theme.colors.highlight,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}, [tailwind]);
|
|
||||||
|
|
||||||
const [query, setQuery] = useState(defaultQuery ?? "");
|
|
||||||
const editorRef = useRef<ReactCodeMirrorRef>(null);
|
|
||||||
|
|
||||||
useHotkeys('/', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
editorRef.current?.view?.focus();
|
|
||||||
if (editorRef.current?.view) {
|
|
||||||
cursorDocEnd({
|
|
||||||
state: editorRef.current.view.state,
|
|
||||||
dispatch: editorRef.current.view.dispatch,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = () => {
|
|
||||||
const url = createPathWithQueryParams('/search',
|
|
||||||
[SearchQueryParams.query, query],
|
|
||||||
)
|
|
||||||
router.push(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(searchBarVariants({ size, className }))}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
onSubmit();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CodeMirror
|
|
||||||
ref={editorRef}
|
|
||||||
className="grow"
|
|
||||||
placeholder={"Search..."}
|
|
||||||
value={query}
|
|
||||||
onChange={(value) => {
|
|
||||||
setQuery(value);
|
|
||||||
}}
|
|
||||||
theme={theme}
|
|
||||||
basicSetup={false}
|
|
||||||
extensions={extensions}
|
|
||||||
indentWithTab={false}
|
|
||||||
autoFocus={autoFocus ?? false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
24
packages/web/src/hooks/useClickListener.ts
Normal file
24
packages/web/src/hooks/useClickListener.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
export const useClickListener = (elementSelector: string, onClick: (elementClicked: boolean) => void) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClick = (event: MouseEvent) => {
|
||||||
|
const element = document.querySelector(elementSelector);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
const isElementClicked = element.contains(event.target as Node);
|
||||||
|
onClick(isElementClicked);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', handleClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleClick);
|
||||||
|
};
|
||||||
|
}, [onClick, elementSelector]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import tailwindConfig from '../../tailwind.config';
|
||||||
export const useTailwind = () => {
|
export const useTailwind = () => {
|
||||||
const tailwind = useMemo(() => {
|
const tailwind = useMemo(() => {
|
||||||
return resolveConfig(tailwindConfig);
|
return resolveConfig(tailwindConfig);
|
||||||
}, [tailwindConfig]);
|
}, []);
|
||||||
|
|
||||||
return tailwind;
|
return tailwind;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import escapeStringRegexp from "escape-string-regexp";
|
import escapeStringRegexp from "escape-string-regexp";
|
||||||
import { SHARD_MAX_MATCH_COUNT, TOTAL_MAX_MATCH_COUNT } from "../environment";
|
import { SHARD_MAX_MATCH_COUNT, TOTAL_MAX_MATCH_COUNT } from "../environment";
|
||||||
import { listRepositoriesResponseSchema, searchResponseSchema, zoektSearchResponseSchema } from "../schemas";
|
import { listRepositoriesResponseSchema, zoektSearchResponseSchema } from "../schemas";
|
||||||
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, SearchRequest, SearchResponse } from "../types";
|
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, SearchRequest, SearchResponse } from "../types";
|
||||||
import { fileNotFound, invalidZoektResponse, ServiceError, unexpectedError } from "../serviceError";
|
import { fileNotFound, invalidZoektResponse, ServiceError, unexpectedError } from "../serviceError";
|
||||||
import { isServiceError } from "../utils";
|
import { isServiceError } from "../utils";
|
||||||
|
|
|
||||||
|
|
@ -119,3 +119,8 @@ export const base64Decode = (base64: string): string => {
|
||||||
const binString = atob(base64);
|
const binString = atob(base64);
|
||||||
return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString();
|
return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @see: https://stackoverflow.com/a/65959350/23221295
|
||||||
|
export const isDefined = <T>(arg: T | null | undefined): arg is T extends null | undefined ? never : T => {
|
||||||
|
return arg !== null && arg !== undefined;
|
||||||
|
}
|
||||||
10
packages/web/vitest.config.mts
Normal file
10
packages/web/vitest.config.mts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [tsconfigPaths()],
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
watch: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
337
yarn.lock
337
yarn.lock
|
|
@ -1779,6 +1779,16 @@
|
||||||
chai "^5.1.2"
|
chai "^5.1.2"
|
||||||
tinyrainbow "^1.2.0"
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
|
"@vitest/expect@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.5.tgz#5a6afa6314cae7a61847927bb5bc038212ca7381"
|
||||||
|
integrity sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/spy" "2.1.5"
|
||||||
|
"@vitest/utils" "2.1.5"
|
||||||
|
chai "^5.1.2"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
"@vitest/mocker@2.1.4":
|
"@vitest/mocker@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.4.tgz#0dc07edb9114f7f080a0181fbcdb16cd4a2d855d"
|
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.4.tgz#0dc07edb9114f7f080a0181fbcdb16cd4a2d855d"
|
||||||
|
|
@ -1788,6 +1798,15 @@
|
||||||
estree-walker "^3.0.3"
|
estree-walker "^3.0.3"
|
||||||
magic-string "^0.30.12"
|
magic-string "^0.30.12"
|
||||||
|
|
||||||
|
"@vitest/mocker@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.5.tgz#54ee50648bc0bb606dfc58e13edfacb8b9208324"
|
||||||
|
integrity sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/spy" "2.1.5"
|
||||||
|
estree-walker "^3.0.3"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
|
||||||
"@vitest/pretty-format@2.1.4", "@vitest/pretty-format@^2.1.4":
|
"@vitest/pretty-format@2.1.4", "@vitest/pretty-format@^2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f"
|
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.4.tgz#fc31993bdc1ef5a6c1a4aa6844e7ba55658a4f9f"
|
||||||
|
|
@ -1795,6 +1814,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
tinyrainbow "^1.2.0"
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
|
"@vitest/pretty-format@2.1.5", "@vitest/pretty-format@^2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.5.tgz#bc79b8826d4a63dc04f2a75d2944694039fa50aa"
|
||||||
|
integrity sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
"@vitest/runner@2.1.4":
|
"@vitest/runner@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.4.tgz#f9346500bdd0be1c926daaac5d683bae87ceda2c"
|
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.4.tgz#f9346500bdd0be1c926daaac5d683bae87ceda2c"
|
||||||
|
|
@ -1803,6 +1829,14 @@
|
||||||
"@vitest/utils" "2.1.4"
|
"@vitest/utils" "2.1.4"
|
||||||
pathe "^1.1.2"
|
pathe "^1.1.2"
|
||||||
|
|
||||||
|
"@vitest/runner@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.5.tgz#4d5e2ba2dfc0af74e4b0f9f3f8be020559b26ea9"
|
||||||
|
integrity sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/utils" "2.1.5"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
|
||||||
"@vitest/snapshot@2.1.4":
|
"@vitest/snapshot@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.4.tgz#ef8c3f605fbc23a32773256d37d3fdfd9b23d353"
|
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.4.tgz#ef8c3f605fbc23a32773256d37d3fdfd9b23d353"
|
||||||
|
|
@ -1812,6 +1846,15 @@
|
||||||
magic-string "^0.30.12"
|
magic-string "^0.30.12"
|
||||||
pathe "^1.1.2"
|
pathe "^1.1.2"
|
||||||
|
|
||||||
|
"@vitest/snapshot@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.5.tgz#a09a8712547452a84e08b3ec97b270d9cc156b4f"
|
||||||
|
integrity sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/pretty-format" "2.1.5"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
|
||||||
"@vitest/spy@2.1.4":
|
"@vitest/spy@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.4.tgz#4e90f9783437c5841a27c80f8fd84d7289a6100a"
|
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.4.tgz#4e90f9783437c5841a27c80f8fd84d7289a6100a"
|
||||||
|
|
@ -1819,6 +1862,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
tinyspy "^3.0.2"
|
tinyspy "^3.0.2"
|
||||||
|
|
||||||
|
"@vitest/spy@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.5.tgz#f790d1394a5030644217ce73562e92465e83147e"
|
||||||
|
integrity sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==
|
||||||
|
dependencies:
|
||||||
|
tinyspy "^3.0.2"
|
||||||
|
|
||||||
"@vitest/utils@2.1.4":
|
"@vitest/utils@2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537"
|
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.4.tgz#6d67ac966647a21ce8bc497472ce230de3b64537"
|
||||||
|
|
@ -1828,6 +1878,15 @@
|
||||||
loupe "^3.1.2"
|
loupe "^3.1.2"
|
||||||
tinyrainbow "^1.2.0"
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
|
"@vitest/utils@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.5.tgz#0e19ce677c870830a1573d33ee86b0d6109e9546"
|
||||||
|
integrity sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/pretty-format" "2.1.5"
|
||||||
|
loupe "^3.1.2"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
abort-controller@^3.0.0:
|
abort-controller@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||||
|
|
@ -1845,6 +1904,13 @@ acorn@^8.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||||
|
|
||||||
|
agent-base@^7.0.2, agent-base@^7.1.0:
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
|
||||||
|
integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
ajv@^6.12.4:
|
ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
|
|
@ -2342,6 +2408,13 @@ cssesc@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||||
|
|
||||||
|
cssstyle@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.1.0.tgz#161faee382af1bafadb6d3867a92a19bcb4aea70"
|
||||||
|
integrity sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==
|
||||||
|
dependencies:
|
||||||
|
rrweb-cssom "^0.7.1"
|
||||||
|
|
||||||
csstype@^3.0.2:
|
csstype@^3.0.2:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||||
|
|
@ -2352,6 +2425,14 @@ damerau-levenshtein@^1.0.8:
|
||||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
||||||
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
||||||
|
|
||||||
|
data-urls@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde"
|
||||||
|
integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==
|
||||||
|
dependencies:
|
||||||
|
whatwg-mimetype "^4.0.0"
|
||||||
|
whatwg-url "^14.0.0"
|
||||||
|
|
||||||
data-view-buffer@^1.0.1:
|
data-view-buffer@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
|
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
|
||||||
|
|
@ -2379,6 +2460,13 @@ data-view-byte-offset@^1.0.0:
|
||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
is-data-view "^1.0.1"
|
is-data-view "^1.0.1"
|
||||||
|
|
||||||
|
debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7:
|
||||||
|
version "4.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||||
|
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||||
|
dependencies:
|
||||||
|
ms "^2.1.3"
|
||||||
|
|
||||||
debug@^3.2.7:
|
debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
|
|
@ -2386,12 +2474,10 @@ debug@^3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7:
|
decimal.js@^10.4.3:
|
||||||
version "4.3.7"
|
version "10.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
||||||
dependencies:
|
|
||||||
ms "^2.1.3"
|
|
||||||
|
|
||||||
deep-eql@^5.0.1:
|
deep-eql@^5.0.1:
|
||||||
version "5.0.2"
|
version "5.0.2"
|
||||||
|
|
@ -2552,6 +2638,11 @@ enhanced-resolve@^5.15.0:
|
||||||
graceful-fs "^4.2.4"
|
graceful-fs "^4.2.4"
|
||||||
tapable "^2.2.0"
|
tapable "^2.2.0"
|
||||||
|
|
||||||
|
entities@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||||
|
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||||
|
|
||||||
error-ex@^1.3.1:
|
error-ex@^1.3.1:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||||
|
|
@ -2658,6 +2749,11 @@ es-iterator-helpers@^1.0.19:
|
||||||
iterator.prototype "^1.1.2"
|
iterator.prototype "^1.1.2"
|
||||||
safe-array-concat "^1.1.2"
|
safe-array-concat "^1.1.2"
|
||||||
|
|
||||||
|
es-module-lexer@^1.5.4:
|
||||||
|
version "1.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
|
||||||
|
integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
|
||||||
|
|
||||||
es-object-atoms@^1.0.0:
|
es-object-atoms@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
|
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
|
||||||
|
|
@ -3276,6 +3372,11 @@ globby@^11.1.0:
|
||||||
merge2 "^1.4.1"
|
merge2 "^1.4.1"
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
|
|
||||||
|
globrex@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||||
|
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||||
|
|
||||||
gopd@^1.0.1:
|
gopd@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||||
|
|
@ -3344,11 +3445,41 @@ hosted-git-info@^2.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||||
|
|
||||||
|
html-encoding-sniffer@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448"
|
||||||
|
integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==
|
||||||
|
dependencies:
|
||||||
|
whatwg-encoding "^3.1.1"
|
||||||
|
|
||||||
|
http-proxy-agent@^7.0.2:
|
||||||
|
version "7.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
|
||||||
|
integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^7.1.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
http-status-codes@^2.3.0:
|
http-status-codes@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.3.0.tgz#987fefb28c69f92a43aecc77feec2866349a8bfc"
|
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.3.0.tgz#987fefb28c69f92a43aecc77feec2866349a8bfc"
|
||||||
integrity sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==
|
integrity sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==
|
||||||
|
|
||||||
|
https-proxy-agent@^7.0.5:
|
||||||
|
version "7.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
|
||||||
|
integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^7.0.2"
|
||||||
|
debug "4"
|
||||||
|
|
||||||
|
iconv-lite@0.6.3:
|
||||||
|
version "0.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||||
|
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||||
|
dependencies:
|
||||||
|
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||||
|
|
||||||
ieee754@^1.2.1:
|
ieee754@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
|
@ -3547,6 +3678,11 @@ is-path-inside@^3.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||||
|
|
||||||
|
is-potential-custom-element-name@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||||
|
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
|
||||||
|
|
||||||
is-regex@^1.1.4:
|
is-regex@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
||||||
|
|
@ -3669,6 +3805,33 @@ js-yaml@^4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse "^2.0.1"
|
argparse "^2.0.1"
|
||||||
|
|
||||||
|
jsdom@^25.0.1:
|
||||||
|
version "25.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef"
|
||||||
|
integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==
|
||||||
|
dependencies:
|
||||||
|
cssstyle "^4.1.0"
|
||||||
|
data-urls "^5.0.0"
|
||||||
|
decimal.js "^10.4.3"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
html-encoding-sniffer "^4.0.0"
|
||||||
|
http-proxy-agent "^7.0.2"
|
||||||
|
https-proxy-agent "^7.0.5"
|
||||||
|
is-potential-custom-element-name "^1.0.1"
|
||||||
|
nwsapi "^2.2.12"
|
||||||
|
parse5 "^7.1.2"
|
||||||
|
rrweb-cssom "^0.7.1"
|
||||||
|
saxes "^6.0.0"
|
||||||
|
symbol-tree "^3.2.4"
|
||||||
|
tough-cookie "^5.0.0"
|
||||||
|
w3c-xmlserializer "^5.0.0"
|
||||||
|
webidl-conversions "^7.0.0"
|
||||||
|
whatwg-encoding "^3.1.1"
|
||||||
|
whatwg-mimetype "^4.0.0"
|
||||||
|
whatwg-url "^14.0.0"
|
||||||
|
ws "^8.18.0"
|
||||||
|
xml-name-validator "^5.0.0"
|
||||||
|
|
||||||
json-buffer@3.0.1:
|
json-buffer@3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||||
|
|
@ -4013,6 +4176,11 @@ npm-run-all@^4.1.5:
|
||||||
shell-quote "^1.6.1"
|
shell-quote "^1.6.1"
|
||||||
string.prototype.padend "^3.0.0"
|
string.prototype.padend "^3.0.0"
|
||||||
|
|
||||||
|
nwsapi@^2.2.12:
|
||||||
|
version "2.2.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655"
|
||||||
|
integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==
|
||||||
|
|
||||||
object-assign@^4.0.1, object-assign@^4.1.1:
|
object-assign@^4.0.1, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
@ -4148,6 +4316,13 @@ parse-json@^4.0.0:
|
||||||
error-ex "^1.3.1"
|
error-ex "^1.3.1"
|
||||||
json-parse-better-errors "^1.0.1"
|
json-parse-better-errors "^1.0.1"
|
||||||
|
|
||||||
|
parse5@^7.1.2:
|
||||||
|
version "7.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a"
|
||||||
|
integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==
|
||||||
|
dependencies:
|
||||||
|
entities "^4.5.0"
|
||||||
|
|
||||||
path-exists@^4.0.0:
|
path-exists@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
|
|
@ -4389,7 +4564,7 @@ ps-tree@^1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
event-stream "=3.3.4"
|
event-stream "=3.3.4"
|
||||||
|
|
||||||
punycode@^2.1.0:
|
punycode@^2.1.0, punycode@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||||
|
|
@ -4612,6 +4787,11 @@ rollup@^4.20.0:
|
||||||
"@rollup/rollup-win32-x64-msvc" "4.25.0"
|
"@rollup/rollup-win32-x64-msvc" "4.25.0"
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
rrweb-cssom@^0.7.1:
|
||||||
|
version "0.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b"
|
||||||
|
integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==
|
||||||
|
|
||||||
run-parallel@^1.1.9:
|
run-parallel@^1.1.9:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||||
|
|
@ -4653,6 +4833,18 @@ safe-stable-stringify@^2.3.1:
|
||||||
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd"
|
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd"
|
||||||
integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==
|
integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==
|
||||||
|
|
||||||
|
"safer-buffer@>= 2.1.2 < 3.0.0":
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
|
saxes@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
|
||||||
|
integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
|
||||||
|
dependencies:
|
||||||
|
xmlchars "^2.2.0"
|
||||||
|
|
||||||
scheduler@^0.23.2:
|
scheduler@^0.23.2:
|
||||||
version "0.23.2"
|
version "0.23.2"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
|
||||||
|
|
@ -4849,7 +5041,7 @@ stackback@0.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
|
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
|
||||||
integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
|
integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
|
||||||
|
|
||||||
std-env@^3.7.0:
|
std-env@^3.7.0, std-env@^3.8.0:
|
||||||
version "3.8.0"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
|
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
|
||||||
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
|
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
|
||||||
|
|
@ -5054,6 +5246,11 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
symbol-tree@^3.2.4:
|
||||||
|
version "3.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
|
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
||||||
|
|
||||||
tailwind-merge@^2.5.2:
|
tailwind-merge@^2.5.2:
|
||||||
version "2.5.3"
|
version "2.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.3.tgz#579546e14ddda24462e0303acd8798c50f5511bb"
|
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.3.tgz#579546e14ddda24462e0303acd8798c50f5511bb"
|
||||||
|
|
@ -5156,6 +5353,18 @@ tinyspy@^3.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
|
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
|
||||||
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
|
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
|
||||||
|
|
||||||
|
tldts-core@^6.1.63:
|
||||||
|
version "6.1.63"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.63.tgz#094f2b05faf90cf1e228eda1caef658425c7c912"
|
||||||
|
integrity sha512-H1XCt54xY+QPbwhTgmxLkepX0MVHu3USfMmejiCOdkMbRcP22Pn2FVF127r/GWXVDmXTRezyF3Ckvhn4Fs6j7Q==
|
||||||
|
|
||||||
|
tldts@^6.1.32:
|
||||||
|
version "6.1.63"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.63.tgz#81a3898554ba1dbbdc6844ed4e68c574f09fed32"
|
||||||
|
integrity sha512-YWwhsjyn9sB/1rOkSRYxvkN/wl5LFM1QDv6F2pVR+pb/jFne4EOBxHfkKVWvDIBEAw9iGOwwubHtQTm0WRT5sQ==
|
||||||
|
dependencies:
|
||||||
|
tldts-core "^6.1.63"
|
||||||
|
|
||||||
to-regex-range@^5.0.1:
|
to-regex-range@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||||
|
|
@ -5163,6 +5372,20 @@ to-regex-range@^5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
is-number "^7.0.0"
|
||||||
|
|
||||||
|
tough-cookie@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.0.0.tgz#6b6518e2b5c070cf742d872ee0f4f92d69eac1af"
|
||||||
|
integrity sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==
|
||||||
|
dependencies:
|
||||||
|
tldts "^6.1.32"
|
||||||
|
|
||||||
|
tr46@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
|
||||||
|
integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==
|
||||||
|
dependencies:
|
||||||
|
punycode "^2.3.1"
|
||||||
|
|
||||||
tr46@~0.0.3:
|
tr46@~0.0.3:
|
||||||
version "0.0.3"
|
version "0.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||||
|
|
@ -5193,6 +5416,11 @@ tsc-watch@^6.2.0:
|
||||||
ps-tree "^1.2.0"
|
ps-tree "^1.2.0"
|
||||||
string-argv "^0.3.1"
|
string-argv "^0.3.1"
|
||||||
|
|
||||||
|
tsconfck@^3.0.3:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.4.tgz#de01a15334962e2feb526824339b51be26712229"
|
||||||
|
integrity sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==
|
||||||
|
|
||||||
tsconfig-paths@^3.15.0:
|
tsconfig-paths@^3.15.0:
|
||||||
version "3.15.0"
|
version "3.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
|
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
|
||||||
|
|
@ -5351,6 +5579,26 @@ vite-node@2.1.4:
|
||||||
pathe "^1.1.2"
|
pathe "^1.1.2"
|
||||||
vite "^5.0.0"
|
vite "^5.0.0"
|
||||||
|
|
||||||
|
vite-node@2.1.5:
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.5.tgz#cf28c637b2ebe65921f3118a165b7cf00a1cdf19"
|
||||||
|
integrity sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==
|
||||||
|
dependencies:
|
||||||
|
cac "^6.7.14"
|
||||||
|
debug "^4.3.7"
|
||||||
|
es-module-lexer "^1.5.4"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
vite "^5.0.0"
|
||||||
|
|
||||||
|
vite-tsconfig-paths@^5.1.3:
|
||||||
|
version "5.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.3.tgz#ffab28a9c2cb171e7685dd5cdcb93b132187cad5"
|
||||||
|
integrity sha512-0bz+PDlLpGfP2CigeSKL9NFTF1KtXkeHGZSSaGQSuPZH77GhoiQaA8IjYgOaynSuwlDTolSUEU0ErVvju3NURg==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.1.1"
|
||||||
|
globrex "^0.1.2"
|
||||||
|
tsconfck "^3.0.3"
|
||||||
|
|
||||||
vite@^5.0.0:
|
vite@^5.0.0:
|
||||||
version "5.4.11"
|
version "5.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
|
||||||
|
|
@ -5388,11 +5636,44 @@ vitest@^2.1.4:
|
||||||
vite-node "2.1.4"
|
vite-node "2.1.4"
|
||||||
why-is-node-running "^2.3.0"
|
why-is-node-running "^2.3.0"
|
||||||
|
|
||||||
|
vitest@^2.1.5:
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.5.tgz#a93b7b84a84650130727baae441354e6df118148"
|
||||||
|
integrity sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/expect" "2.1.5"
|
||||||
|
"@vitest/mocker" "2.1.5"
|
||||||
|
"@vitest/pretty-format" "^2.1.5"
|
||||||
|
"@vitest/runner" "2.1.5"
|
||||||
|
"@vitest/snapshot" "2.1.5"
|
||||||
|
"@vitest/spy" "2.1.5"
|
||||||
|
"@vitest/utils" "2.1.5"
|
||||||
|
chai "^5.1.2"
|
||||||
|
debug "^4.3.7"
|
||||||
|
expect-type "^1.1.0"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
std-env "^3.8.0"
|
||||||
|
tinybench "^2.9.0"
|
||||||
|
tinyexec "^0.3.1"
|
||||||
|
tinypool "^1.0.1"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
vite "^5.0.0"
|
||||||
|
vite-node "2.1.5"
|
||||||
|
why-is-node-running "^2.3.0"
|
||||||
|
|
||||||
w3c-keyname@^2.2.4:
|
w3c-keyname@^2.2.4:
|
||||||
version "2.2.8"
|
version "2.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
|
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
|
||||||
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
|
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
|
||||||
|
|
||||||
|
w3c-xmlserializer@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
|
||||||
|
integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==
|
||||||
|
dependencies:
|
||||||
|
xml-name-validator "^5.0.0"
|
||||||
|
|
||||||
web-vitals@^4.0.1:
|
web-vitals@^4.0.1:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7"
|
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7"
|
||||||
|
|
@ -5403,6 +5684,31 @@ webidl-conversions@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||||
|
|
||||||
|
webidl-conversions@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
|
||||||
|
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
|
||||||
|
|
||||||
|
whatwg-encoding@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5"
|
||||||
|
integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==
|
||||||
|
dependencies:
|
||||||
|
iconv-lite "0.6.3"
|
||||||
|
|
||||||
|
whatwg-mimetype@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a"
|
||||||
|
integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==
|
||||||
|
|
||||||
|
whatwg-url@^14.0.0:
|
||||||
|
version "14.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6"
|
||||||
|
integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==
|
||||||
|
dependencies:
|
||||||
|
tr46 "^5.0.0"
|
||||||
|
webidl-conversions "^7.0.0"
|
||||||
|
|
||||||
whatwg-url@^5.0.0:
|
whatwg-url@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
|
|
@ -5537,11 +5843,26 @@ wrappy@1:
|
||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||||
|
|
||||||
|
ws@^8.18.0:
|
||||||
|
version "8.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
|
||||||
|
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
|
||||||
|
|
||||||
xcase@^2.0.1:
|
xcase@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9"
|
resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9"
|
||||||
integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==
|
integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==
|
||||||
|
|
||||||
|
xml-name-validator@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673"
|
||||||
|
integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==
|
||||||
|
|
||||||
|
xmlchars@^2.2.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||||
|
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||||
|
|
||||||
yaml@^2.3.4:
|
yaml@^2.3.4:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue