From 484ff5dd4bd2e5c2d5168437895d400a39f2bfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=83=BD=E5=AE=81?= Date: Thu, 18 May 2023 20:38:36 +0800 Subject: [PATCH 001/121] fix: hmr when `base` is set (#2375) --- src/client/app/router.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 2be95f09..d8b98168 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -303,6 +303,8 @@ function handleHMR(route: Route): void { function shouldHotReload(payload: PageDataPayload): boolean { const payloadPath = payload.path.replace(/(\bindex)?\.md$/, '') - const locationPath = location.pathname.replace(/(\bindex)?\.html$/, '') + const locationPath = location.pathname + .replace(/(\bindex)?\.html$/, '') + .slice(siteDataRef.value.base.length - 1) return payloadPath === locationPath } From 9a5735dd0e00f763bb02ab987a05209729a8abab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=83=BD=E5=AE=81?= Date: Thu, 18 May 2023 20:39:35 +0800 Subject: [PATCH 002/121] refactor(theme): simplify the import (#2374) --- src/client/theme-default/components/VPMenuLink.vue | 2 +- src/client/theme-default/components/VPNavBarMenuGroup.vue | 2 +- src/client/theme-default/components/VPNavBarMenuLink.vue | 2 +- src/client/theme-default/composables/prev-next.ts | 2 +- src/client/theme-default/composables/sidebar.ts | 2 +- src/client/theme-default/support/sidebar.ts | 3 ++- src/client/theme-default/support/utils.ts | 2 -- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/client/theme-default/components/VPMenuLink.vue b/src/client/theme-default/components/VPMenuLink.vue index c24de90b..f4f20985 100644 --- a/src/client/theme-default/components/VPMenuLink.vue +++ b/src/client/theme-default/components/VPMenuLink.vue @@ -1,7 +1,7 @@ diff --git a/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue b/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue index 216ddcf6..15efcef1 100644 --- a/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue +++ b/src/client/theme-default/components/VPNavScreenMenuGroupSection.vue @@ -14,8 +14,7 @@ defineProps<{ diff --git a/src/client/theme-default/components/VPNavScreenMenuLink.vue b/src/client/theme-default/components/VPNavScreenMenuLink.vue index 7c03b39f..85977bc1 100644 --- a/src/client/theme-default/components/VPNavScreenMenuLink.vue +++ b/src/client/theme-default/components/VPNavScreenMenuLink.vue @@ -1,18 +1,24 @@ From cb848fafafc00f495182a2a71b2cb9f415c918f2 Mon Sep 17 00:00:00 2001 From: Kia King Ishii Date: Mon, 22 May 2023 19:33:37 +0900 Subject: [PATCH 011/121] chore: update README status to `beta` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fbd334c2..d8ff0b85 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# VitePress (alpha) 📝💨 +# VitePress (beta) 📝💨 [![Test](https://github.com/vuejs/vitepress/workflows/Test/badge.svg)](https://github.com/vuejs/vitepress/actions) [![npm](https://img.shields.io/npm/v/vitepress)](https://www.npmjs.com/package/vitepress) @@ -8,7 +8,7 @@ VitePress is [VuePress](https://vuepress.vuejs.org)' spiritual successor, built on top of [vite](https://github.com/vitejs/vite). -Currently, it's in the `alpha` stage. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases. +Currently, it is in the `beta` stage. It is already suitable for out-of-the-box documentation use. There still might be issues, however, we do not plan to introduce any breaking changes from here on until the stable release. ## Documentation From b26ce48edc472b4e68601923d436bf7cc7cf7f60 Mon Sep 17 00:00:00 2001 From: Kia King Ishii Date: Mon, 22 May 2023 19:35:44 +0900 Subject: [PATCH 012/121] release: v1.0.0-beta.1 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c51f20d6..e1582cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# [1.0.0-beta.1](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.76...v1.0.0-beta.1) (2023-05-22) + + +### Bug Fixes + +* **config:** set scrollOffset to 0 is not effect ([#2395](https://github.com/vuejs/vitepress/issues/2395)) ([8153f23](https://github.com/vuejs/vitepress/commit/8153f23c901a6200661813e65f0d8eb602ad46da)) +* **theme:** features section layout is not consistent ([#2382](https://github.com/vuejs/vitepress/issues/2382)) ([26f21d9](https://github.com/vuejs/vitepress/commit/26f21d95dfbd671477d425e6b8ac5b0172a846ac)) +* **theme:** some global properties in localSearch are missing ([#2396](https://github.com/vuejs/vitepress/issues/2396)) ([4896811](https://github.com/vuejs/vitepress/commit/489681117f46a803704b6ec80546a5e787e19df2)) +* **theme:** custom target and rel in navbar links for mobile is missing ([#2400](https://github.com/vuejs/vitepress/issues/2400)) ([f364a5d](https://github.com/vuejs/vitepress/commit/f364a5d1d3c066c9728beb5d07576d6cb4b0640d)) + + + # [1.0.0-alpha.76](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.75...v1.0.0-alpha.76) (2023-05-18) diff --git a/package.json b/package.json index 267d0f41..770bfdd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitepress", - "version": "1.0.0-alpha.76", + "version": "1.0.0-beta.1", "description": "Vite & Vue powered static site generator", "type": "module", "packageManager": "pnpm@8.5.1", From 06c0fc5d5cd55e03b4eee14feac67b749e7283ed Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Thu, 25 May 2023 14:47:18 +0530 Subject: [PATCH 013/121] fix(theme): adjust z-index for active code group marker (#2413) --- src/client/theme-default/styles/components/vp-code-group.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/styles/components/vp-code-group.css b/src/client/theme-default/styles/components/vp-code-group.css index c0a9c092..79d02fec 100644 --- a/src/client/theme-default/styles/components/vp-code-group.css +++ b/src/client/theme-default/styles/components/vp-code-group.css @@ -56,7 +56,7 @@ right: 8px; bottom: -1px; left: 8px; - z-index: 10; + z-index: 1; height: 1px; content: ''; background-color: transparent; From c869ea64ae3c20aef60af1425b02e5797faa8d69 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Thu, 25 May 2023 16:56:44 +0530 Subject: [PATCH 014/121] fix(theme): use document !== undefined check for browser (#2417) localStorage is defined on server in Deno --- src/client/theme-default/components/VPSwitchAppearance.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/theme-default/components/VPSwitchAppearance.vue b/src/client/theme-default/components/VPSwitchAppearance.vue index 3a283545..1a9f06c4 100644 --- a/src/client/theme-default/components/VPSwitchAppearance.vue +++ b/src/client/theme-default/components/VPSwitchAppearance.vue @@ -1,14 +1,14 @@ @@ -45,11 +71,19 @@ const { hasSidebar } = useSidebar() display: flex; justify-content: space-between; align-items: center; + border-top: 1px solid var(--vp-c-gutter); border-bottom: 1px solid var(--vp-c-gutter); padding-top: var(--vp-layout-top-height, 0px); width: 100%; background-color: var(--vp-local-nav-bg-color); - transition: border-color 0.5s, background-color 0.5s; +} + +.VPLocalNav.fixed { + position: fixed; +} + +.VPLocalNav.reached-top { + border-top-color: transparent; } @media (min-width: 960px) { diff --git a/src/client/theme-default/components/VPLocalNavOutlineDropdown.vue b/src/client/theme-default/components/VPLocalNavOutlineDropdown.vue index 7ae9c4b9..a879699b 100644 --- a/src/client/theme-default/components/VPLocalNavOutlineDropdown.vue +++ b/src/client/theme-default/components/VPLocalNavOutlineDropdown.vue @@ -1,12 +1,16 @@ diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts index d4cde64c..3a37eb57 100644 --- a/types/default-theme.d.ts +++ b/types/default-theme.d.ts @@ -251,6 +251,7 @@ export namespace DefaultTheme { export interface SocialLink { icon: SocialLinkIcon link: string + ariaLabel?: string } export type SocialLinkIcon = From d62e6f6dd68e1ac31654e0da6102915a5a3709f0 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:22:54 +0530 Subject: [PATCH 030/121] perf(search): use dom apis instead of regex-based section parsing (#2486) --- .../components/VPLocalSearchBox.vue | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/client/theme-default/components/VPLocalSearchBox.vue b/src/client/theme-default/components/VPLocalSearchBox.vue index 94453e1d..69a2d81e 100644 --- a/src/client/theme-default/components/VPLocalSearchBox.vue +++ b/src/client/theme-default/components/VPLocalSearchBox.vue @@ -119,8 +119,6 @@ watchEffect(() => { const results: Ref<(SearchResult & Result)[]> = shallowRef([]) -const headingRegex = /.*?.*?<\/a><\/h\1>/gi - const enableNoResults = ref(false) watch(filterText, () => { @@ -158,10 +156,8 @@ debouncedWatch( const mapId = id.slice(0, id.indexOf('#')) let map = c.get(mapId) if (map) continue - else { - map = new Map() - c.set(mapId, map) - } + map = new Map() + c.set(mapId, map) const comp = mod.default ?? mod if (comp?.render) { const app = createApp(comp) @@ -182,14 +178,17 @@ debouncedWatch( }) const div = document.createElement('div') app.mount(div) - const sections = div.innerHTML.split(headingRegex) + const headings = div.querySelectorAll('h1, h2, h3, h4, h5, h6') + headings.forEach((el) => { + const href = el.querySelector('a')?.getAttribute('href') + const anchor = href?.startsWith('#') && href.slice(1) + if (!anchor) return + let html = '' + while ((el = el.nextElementSibling!) && !/^h[1-6]$/i.test(el.tagName)) + html += el.outerHTML + map!.set(anchor, html) + }) app.unmount() - sections.shift() - for (let i = 0; i < sections.length; i += 3) { - const anchor = sections[i + 1] - const html = sections[i + 2] - map.set(anchor, html) - } } if (canceled) return } From 80e734d67763fea449647b7b21dfde0bde1c360b Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:51:55 +0530 Subject: [PATCH 031/121] fix(search): detailed view not working when page contains script setup closes #2485 --- src/client/theme-default/components/VPLocalSearchBox.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/theme-default/components/VPLocalSearchBox.vue b/src/client/theme-default/components/VPLocalSearchBox.vue index 69a2d81e..139d4197 100644 --- a/src/client/theme-default/components/VPLocalSearchBox.vue +++ b/src/client/theme-default/components/VPLocalSearchBox.vue @@ -159,7 +159,7 @@ debouncedWatch( map = new Map() c.set(mapId, map) const comp = mod.default ?? mod - if (comp?.render) { + if (comp?.render || comp?.setup) { const app = createApp(comp) // Silence warnings about missing components app.config.warnHandler = () => {} From 90478b36cd4d161c2118a9e677384982805963b0 Mon Sep 17 00:00:00 2001 From: CHOYSEN Date: Sat, 10 Jun 2023 16:29:22 +0800 Subject: [PATCH 032/121] feat(build): support relative path for code snippet (#1894) Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> --- docs/guide/markdown.md | 7 ++++++- src/node/markdown/plugins/snippet.ts | 14 +++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md index a3466deb..37f500f6 100644 --- a/docs/guide/markdown.md +++ b/docs/guide/markdown.md @@ -566,7 +566,12 @@ It also supports [line highlighting](#line-highlighting-in-code-blocks): <<< @/snippets/snippet.js{2} ::: tip -The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured. +The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured. Alternatively, you can also import from relative paths: + +```md +<<< ../snippets/snippet.js +``` + ::: You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding) to only include the corresponding part of the code file. You can provide a custom region name after a `#` following the filepath: diff --git a/src/node/markdown/plugins/snippet.ts b/src/node/markdown/plugins/snippet.ts index 190fef83..480baf4a 100644 --- a/src/node/markdown/plugins/snippet.ts +++ b/src/node/markdown/plugins/snippet.ts @@ -2,6 +2,7 @@ import fs from 'fs-extra' import path from 'path' import type MarkdownIt from 'markdown-it' import type { RuleBlock } from 'markdown-it/lib/parser_block' +import type { MarkdownEnv } from '../env' export function dedent(text: string): string { const lines = text.split('\n') @@ -107,7 +108,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => { .trim() const [ - filename = '', + filepath = '', extension = '', region = '', lines = '', @@ -115,7 +116,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => { rawTitle = '' ] = (rawPathRegexp.exec(rawPath) || []).slice(1) - const title = rawTitle || filename.split('/').pop() || '' + const title = rawTitle || filepath.split('/').pop() || '' state.line = startLine + 1 @@ -124,8 +125,12 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => { title ? `[${title}]` : '' }` + const resolvedPath = path.resolve( + path.dirname((state.env as MarkdownEnv).path), + filepath + ) // @ts-ignore - token.src = path.resolve(filename) + region + token.src = [resolvedPath, region.slice(1)] token.markup = '```' token.map = [startLine, startLine + 1] @@ -138,8 +143,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => { const [tokens, idx, , { loader }] = args const token = tokens[idx] // @ts-ignore - const tokenSrc = token.src - const [src, regionName] = tokenSrc ? tokenSrc.split('#') : [''] + const [src, regionName] = token.src ?? [] if (src) { if (loader) { From 29a9647ee92efe8ea9a7c6698d5cacb22bf3e9ce Mon Sep 17 00:00:00 2001 From: donggua <37831399+donggua-nor@users.noreply.github.com> Date: Sat, 10 Jun 2023 16:51:31 +0800 Subject: [PATCH 033/121] feat(theme): allow prev/next links to be disabled globally (#2317) Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> --- docs/reference/default-theme-config.md | 6 +- .../theme-default/composables/prev-next.ts | 66 ++++++++++--------- types/default-theme.d.ts | 8 +-- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/docs/reference/default-theme-config.md b/docs/reference/default-theme-config.md index 386ecbb0..adf3db95 100644 --- a/docs/reference/default-theme-config.md +++ b/docs/reference/default-theme-config.md @@ -351,7 +351,7 @@ Learn more in [Default Theme: Carbon Ads](./default-theme-carbon-ads) - Type: `DocFooter` -Can be used to customize text appearing above previous and next links. Helpful if not writing docs in English. +Can be used to customize text appearing above previous and next links. Helpful if not writing docs in English. Also can be used to disable prev/next links globally. If you want to selectively enable/disable prev/next links, you can use [frontmatter](./default-theme-prev-next-links). ```js export default { @@ -366,8 +366,8 @@ export default { ```ts export interface DocFooter { - prev?: string - next?: string + prev?: string | false + next?: string | false } ``` diff --git a/src/client/theme-default/composables/prev-next.ts b/src/client/theme-default/composables/prev-next.ts index f2bfb765..6f967f73 100644 --- a/src/client/theme-default/composables/prev-next.ts +++ b/src/client/theme-default/composables/prev-next.ts @@ -14,37 +14,43 @@ export function usePrevNext() { return isActive(page.value.relativePath, link.link) }) + const hidePrev = + (theme.value.docFooter?.prev === false && !frontmatter.value.prev) || + frontmatter.value.prev === false + + const hideNext = + (theme.value.docFooter?.next === false && !frontmatter.value.next) || + frontmatter.value.next === false + return { - prev: - frontmatter.value.prev === false - ? undefined - : { - text: - (typeof frontmatter.value.prev === 'string' - ? frontmatter.value.prev - : typeof frontmatter.value.prev === 'object' - ? frontmatter.value.prev.text - : undefined) ?? candidates[index - 1]?.text, - link: - (typeof frontmatter.value.prev === 'object' - ? frontmatter.value.prev.link - : undefined) ?? candidates[index - 1]?.link - }, - next: - frontmatter.value.next === false - ? undefined - : { - text: - (typeof frontmatter.value.next === 'string' - ? frontmatter.value.next - : typeof frontmatter.value.next === 'object' - ? frontmatter.value.next.text - : undefined) ?? candidates[index + 1]?.text, - link: - (typeof frontmatter.value.next === 'object' - ? frontmatter.value.next.link - : undefined) ?? candidates[index + 1]?.link - } + prev: hidePrev + ? undefined + : { + text: + (typeof frontmatter.value.prev === 'string' + ? frontmatter.value.prev + : typeof frontmatter.value.prev === 'object' + ? frontmatter.value.prev.text + : undefined) ?? candidates[index - 1]?.text, + link: + (typeof frontmatter.value.prev === 'object' + ? frontmatter.value.prev.link + : undefined) ?? candidates[index - 1]?.link + }, + next: hideNext + ? undefined + : { + text: + (typeof frontmatter.value.next === 'string' + ? frontmatter.value.next + : typeof frontmatter.value.next === 'object' + ? frontmatter.value.next.text + : undefined) ?? candidates[index + 1]?.text, + link: + (typeof frontmatter.value.next === 'object' + ? frontmatter.value.next.link + : undefined) ?? candidates[index + 1]?.link + } } as { prev?: { text?: string; link?: string } next?: { text?: string; link?: string } diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts index 3a37eb57..e083ad8d 100644 --- a/types/default-theme.d.ts +++ b/types/default-theme.d.ts @@ -232,18 +232,18 @@ export namespace DefaultTheme { export interface DocFooter { /** - * Custom label for previous page button. + * Custom label for previous page button. Can be set to `false` to disable. * * @default 'Previous page' */ - prev?: string + prev?: string | boolean /** - * Custom label for next page button. + * Custom label for next page button. Can be set to `false` to disable. * * @default 'Next page' */ - next?: string + next?: string | boolean } // social link --------------------------------------------------------------- From c9a98ac6bb854a7a24c08cfc23e84ebc243ba347 Mon Sep 17 00:00:00 2001 From: JiZhi <471695625@qq.com> Date: Sat, 10 Jun 2023 16:59:00 +0800 Subject: [PATCH 034/121] fix(build): create markdown env for localSearchPlugin (#2322) --- src/node/plugins/localSearchPlugin.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index 30a7bf9f..5979da43 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -4,6 +4,7 @@ import MiniSearch from 'minisearch' import fs from 'fs-extra' import _debug from 'debug' import type { SiteConfig } from '../config' +import type { MarkdownEnv } from '../markdown' import { createMarkdownRenderer } from '../markdown' import { resolveSiteDataByRoute, slash } from '../shared' @@ -45,6 +46,16 @@ export async function localSearchPlugin( siteConfig.logger ) + function createMarkdownEnv(file: string): MarkdownEnv { + const { srcDir, cleanUrls = false } = siteConfig + const relativePath = slash(path.relative(srcDir, file)) + return { + path: file, + relativePath, + cleanUrls + } + } + const indexByLocales = new Map>() function getIndexByLocale(locale: string) { @@ -111,7 +122,7 @@ export async function localSearchPlugin( .map(async (file) => { const fileId = getDocId(file) const sections = splitPageIntoSections( - md.render(await fs.readFile(file, 'utf-8')) + md.render(await fs.readFile(file, 'utf-8'), createMarkdownEnv(file)) ) const locale = getLocaleForPath(file) let documents = documentsByLocale.get(locale) @@ -191,7 +202,10 @@ export async function localSearchPlugin( } const index = getIndexForPath(ctx.file) const sections = splitPageIntoSections( - md.render(await fs.readFile(ctx.file, 'utf-8')) + md.render( + await fs.readFile(ctx.file, 'utf-8'), + createMarkdownEnv(ctx.file) + ) ) for (const section of sections) { const id = `${fileId}#${section.anchor}` From 4ddb96fe508578893ee5a44621b5bac098bd4710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20S=C3=A1nchez?= Date: Sat, 10 Jun 2023 11:12:27 +0200 Subject: [PATCH 035/121] feat(theme): add semantic markup to local search dialog (#2325) --- .../components/VPLocalSearchBox.vue | 176 +++++++++++------- 1 file changed, 104 insertions(+), 72 deletions(-) diff --git a/src/client/theme-default/components/VPLocalSearchBox.vue b/src/client/theme-default/components/VPLocalSearchBox.vue index 139d4197..b89415c9 100644 --- a/src/client/theme-default/components/VPLocalSearchBox.vue +++ b/src/client/theme-default/components/VPLocalSearchBox.vue @@ -241,10 +241,12 @@ async function fetchExcerpt(id: string) { /* Search input focus */ const searchInput = ref() - -function focusSearchInput() { +const disableReset = computed(() => { + return filterText.value?.length <= 0 +}) +function focusSearchInput(select = true) { searchInput.value?.focus() - searchInput.value?.select() + select && searchInput.value?.select() } onMounted(() => { @@ -259,11 +261,11 @@ function onSearchBarClick(event: PointerEvent) { /* Search keyboard selection */ -const selectedIndex = ref(0) +const selectedIndex = ref(-1) const disableMouseOver = ref(false) -watch(results, () => { - selectedIndex.value = 0 +watch(results, (r) => { + selectedIndex.value = r.length ? 0 : -1 scrollToSelectedResult() }) @@ -360,6 +362,11 @@ onBeforeUnmount(() => { isLocked.value = false }) +function resetSearch() { + filterText.value = '' + nextTick().then(() => focusSearchInput(false)) +} + function formMarkRegex(terms: Set) { return new RegExp( [...terms] @@ -377,34 +384,44 @@ function formMarkRegex(terms: Set) {