@birdapi/velinstyle 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +135 -246
- package/README.md +134 -245
- package/cli/blueprints/onboarding.html +2 -2
- package/cli/cli-manifest.json +97 -0
- package/cli/docgen/extract-a11y.js +89 -0
- package/cli/docgen/extract-attributes.js +95 -0
- package/cli/docgen/extract-cli.js +50 -0
- package/cli/docgen/extract-components.js +175 -0
- package/cli/docgen/extract-meta.js +32 -0
- package/cli/docgen/extract-rules.js +76 -0
- package/cli/docgen/extract-tokens.js +79 -0
- package/cli/docgen/extract-utilities.js +75 -0
- package/cli/docgen/markdown.js +43 -0
- package/cli/docs-generate.js +196 -0
- package/cli/index.js +121 -5
- package/cli/meta.js +97 -0
- package/cli/perf-audit.js +174 -0
- package/cli/pii-scanner.js +134 -0
- package/cli/scanner-rules-data.js +62 -0
- package/cli/scanner.js +68 -1
- package/cli/search-index.js +163 -0
- package/cli/tokens-build.js +21 -1
- package/cli/tokens-validate.js +69 -0
- package/components/a11y-entry.js +42 -0
- package/components/a11y-utils.js +71 -0
- package/components/focus-manager.js +45 -8
- package/components/index.js +22 -12
- package/components/runtime/component-loaders.js +43 -0
- package/components/runtime/index.js +77 -0
- package/components/runtime-entry.js +10 -0
- package/components/sanitize.js +135 -54
- package/components/velin-accordion.js +12 -0
- package/components/velin-bottom-nav.js +19 -0
- package/components/velin-carousel.js +2 -2
- package/components/velin-code-block.js +142 -0
- package/components/velin-command.js +15 -0
- package/components/velin-copy.js +18 -5
- package/components/velin-counter.js +3 -0
- package/components/velin-dialog.js +9 -1
- package/components/velin-email.js +94 -0
- package/components/velin-icon.js +65 -22
- package/components/velin-lightbox.js +8 -3
- package/components/velin-live-dot.js +5 -0
- package/components/velin-modal.js +6 -3
- package/components/velin-popover.js +34 -2
- package/components/velin-reveal.js +7 -65
- package/components/velin-scroll-top.js +1 -1
- package/components/velin-search.js +267 -0
- package/components/velin-secure-field.js +99 -0
- package/components/velin-sparkline.js +27 -7
- package/components/{velin-stepper-wc.js → velin-stepper.js} +37 -8
- package/components/velin-theme-toggle.js +12 -5
- package/components/velin-toast.js +4 -0
- package/components/{velin-tooltip-wc.js → velin-tooltip.js} +22 -3
- package/core/a11y/component-contracts.json +221 -0
- package/core/attributes/index.js +6 -0
- package/core/attributes/registry.js +268 -0
- package/core/highlight/highlight.js +114 -0
- package/core/highlight/index.js +29 -0
- package/core/highlight/languages/_utils.js +52 -0
- package/core/highlight/languages/blade.js +16 -0
- package/core/highlight/languages/css.js +26 -0
- package/core/highlight/languages/html.js +17 -0
- package/core/highlight/languages/js.js +58 -0
- package/core/highlight/languages/json.js +14 -0
- package/core/highlight/languages/markdown.js +16 -0
- package/core/highlight/languages/php.js +39 -0
- package/core/highlight/languages/plain.js +7 -0
- package/core/highlight/languages/shell.js +33 -0
- package/core/highlight/languages/sql.js +20 -0
- package/core/highlight/languages/typescript.js +28 -0
- package/core/highlight/observe.js +53 -0
- package/core/highlight/registry.js +107 -0
- package/core/highlight/render.js +35 -0
- package/core/highlight/types.js +28 -0
- package/core/meta/build.js +293 -0
- package/core/meta/index.js +17 -0
- package/core/meta/schema.js +49 -0
- package/core/motion/effects.js +54 -0
- package/core/motion/index.js +76 -0
- package/core/motion/scheduler.js +72 -0
- package/core/motion/scroll.js +32 -0
- package/core/motion/stagger.js +24 -0
- package/core/search/docs-url.js +96 -0
- package/core/search/engine.js +139 -0
- package/core/search/highlight.js +48 -0
- package/core/search/index.js +126 -0
- package/core/search/providers.js +36 -0
- package/core/search/types.js +61 -0
- package/core/search/worker-client.js +56 -0
- package/core/search/worker.js +20 -0
- package/dist/chunks/a11y-utils-IRC7LOPF.js +16 -0
- package/dist/chunks/attributes-3BZLI7XF.js +364 -0
- package/dist/chunks/attributes-4N5JP4CG.js +364 -0
- package/dist/chunks/attributes-5Q2WXGRC.js +374 -0
- package/dist/chunks/attributes-AIR7SVLK.js +364 -0
- package/dist/chunks/attributes-BGRN5XZO.js +364 -0
- package/dist/chunks/attributes-BVSQAYLR.js +364 -0
- package/dist/chunks/attributes-CDYFUY7Y.js +364 -0
- package/dist/chunks/attributes-JMYJZBPE.js +364 -0
- package/dist/chunks/attributes-R5JY3X4O.js +374 -0
- package/dist/chunks/attributes-STYCS3O5.js +391 -0
- package/dist/chunks/attributes-TNSPQTHX.js +374 -0
- package/dist/chunks/attributes-TPANLMLF.js +391 -0
- package/dist/chunks/blade-LIQLVKZQ.js +19 -0
- package/dist/chunks/browser-MRKYZPEH.js +1149 -0
- package/dist/chunks/chunk-2354NEBP.js +109 -0
- package/dist/chunks/chunk-2VNA7G7S.js +109 -0
- package/dist/chunks/chunk-42VVPW6N.js +132 -0
- package/dist/chunks/chunk-7FFAMRFV.js +44 -0
- package/dist/chunks/chunk-7MCGPHVC.js +69 -0
- package/dist/chunks/chunk-AQLRJSGG.js +109 -0
- package/dist/chunks/chunk-EWPSDMQT.js +109 -0
- package/dist/chunks/chunk-F7OLHTGX.js +22 -0
- package/dist/chunks/chunk-J472ERN2.js +242 -0
- package/dist/chunks/chunk-LA7264K4.js +85 -0
- package/dist/chunks/chunk-M5CHQVP6.js +61 -0
- package/dist/chunks/chunk-MKZP2TBH.js +89 -0
- package/dist/chunks/chunk-QTJJ3DPO.js +109 -0
- package/dist/chunks/chunk-RDE3QE7Z.js +89 -0
- package/dist/chunks/chunk-RSMRR27R.js +109 -0
- package/dist/chunks/chunk-RZQVWFHW.js +109 -0
- package/dist/chunks/chunk-SRL6EWHP.js +109 -0
- package/dist/chunks/chunk-U4XHMZ7D.js +109 -0
- package/dist/chunks/chunk-V5YIUA6G.js +109 -0
- package/dist/chunks/chunk-XIET2VSZ.js +109 -0
- package/dist/chunks/chunk-XYLJ2GCB.js +205 -0
- package/dist/chunks/chunk-Z4JYQ2XX.js +51 -0
- package/dist/chunks/chunk-ZPSPKUYD.js +109 -0
- package/dist/chunks/css-MQ5YU77P.js +28 -0
- package/dist/chunks/highlight-GJHHV4KM.js +37 -0
- package/dist/chunks/highlight-XTHRKBA3.js +37 -0
- package/dist/chunks/html-ZODC4DLQ.js +20 -0
- package/dist/chunks/js-MNSNYTJU.js +7 -0
- package/dist/chunks/json-VEM2HFH3.js +17 -0
- package/dist/chunks/markdown-E7OPGS3S.js +19 -0
- package/dist/chunks/php-MLL7T3LT.js +41 -0
- package/dist/chunks/plain-LJJDC5TN.js +8 -0
- package/dist/chunks/runtime-entry.js +14 -0
- package/dist/chunks/shell-QTRK4U4E.js +35 -0
- package/dist/chunks/sql-PPWLHGJ2.js +23 -0
- package/dist/chunks/typescript-JT3CCYWL.js +30 -0
- package/dist/chunks/velin-accordion-FBIPAEXG.js +105 -0
- package/dist/chunks/velin-accordion-QAQO7BI7.js +117 -0
- package/dist/chunks/velin-announcer-PVTXXCWR.js +33 -0
- package/dist/chunks/velin-bottom-nav-2N2TYWOF.js +86 -0
- package/dist/chunks/velin-bottom-nav-7GEMPIM6.js +86 -0
- package/dist/chunks/velin-bottom-nav-CTU6FFZV.js +101 -0
- package/dist/chunks/velin-bottom-nav-N3DFQR66.js +86 -0
- package/dist/chunks/velin-carousel-Y5VCOABZ.js +213 -0
- package/dist/chunks/velin-carousel-ZTBANFQQ.js +213 -0
- package/dist/chunks/velin-code-block-ASUBTAQV.js +119 -0
- package/dist/chunks/velin-code-block-CIAURDRJ.js +132 -0
- package/dist/chunks/velin-code-block-TSUKPY4C.js +123 -0
- package/dist/chunks/velin-code-block-Y6CM726Q.js +119 -0
- package/dist/chunks/velin-collapse-5XYBUIID.js +80 -0
- package/dist/chunks/velin-combobox-2KDTCQ7N.js +155 -0
- package/dist/chunks/velin-combobox-NXS4QB4R.js +155 -0
- package/dist/chunks/velin-combobox-UGA56HP3.js +155 -0
- package/dist/chunks/velin-command-CKDNO4BW.js +148 -0
- package/dist/chunks/velin-command-M7RNHUPR.js +136 -0
- package/dist/chunks/velin-command-ONKO2A3J.js +136 -0
- package/dist/chunks/velin-command-TMLSRGIU.js +136 -0
- package/dist/chunks/velin-copy-G2TZ5YBB.js +81 -0
- package/dist/chunks/velin-copy-NYDBOKXT.js +81 -0
- package/dist/chunks/velin-copy-T5KDTDXP.js +81 -0
- package/dist/chunks/velin-countdown-4ZVDGK4R.js +101 -0
- package/dist/chunks/velin-countdown-5XSTN3BH.js +101 -0
- package/dist/chunks/velin-countdown-7AIY66CU.js +101 -0
- package/dist/chunks/velin-counter-ISCTEAHI.js +118 -0
- package/dist/chunks/velin-counter-L5FYJF7A.js +121 -0
- package/dist/chunks/velin-dialog-D2GUHSCD.js +144 -0
- package/dist/chunks/velin-dialog-ELFOZIXZ.js +152 -0
- package/dist/chunks/velin-dialog-GTTZI564.js +147 -0
- package/dist/chunks/velin-dialog-IJLFDXAJ.js +144 -0
- package/dist/chunks/velin-dialog-TNS3WXMB.js +144 -0
- package/dist/chunks/velin-drawer-BE6TYN3H.js +130 -0
- package/dist/chunks/velin-drawer-DLL2GLT6.js +130 -0
- package/dist/chunks/velin-drawer-MJS6WCF7.js +130 -0
- package/dist/chunks/velin-dropdown-PLUIFA5M.js +172 -0
- package/dist/chunks/velin-email-N5B5ZYTE.js +90 -0
- package/dist/chunks/velin-email-NTM6DG3V.js +90 -0
- package/dist/chunks/velin-email-RBX6ZR4S.js +90 -0
- package/dist/chunks/velin-haptic-4QQTM3RK.js +50 -0
- package/dist/chunks/velin-icon-7M76DOPB.js +164 -0
- package/dist/chunks/velin-icon-KZO3IEHG.js +164 -0
- package/dist/chunks/velin-icon-WTFPG2DD.js +191 -0
- package/dist/chunks/velin-icon-ZTSKUOVN.js +193 -0
- package/dist/chunks/velin-lightbox-6E7CAATP.js +149 -0
- package/dist/chunks/velin-lightbox-I75U6JF4.js +140 -0
- package/dist/chunks/velin-lightbox-QOTB4PRL.js +140 -0
- package/dist/chunks/velin-lightbox-UCD6N7HF.js +140 -0
- package/dist/chunks/velin-live-dot-RJHX2DGY.js +74 -0
- package/dist/chunks/velin-live-dot-XHUZCIDZ.js +67 -0
- package/dist/chunks/velin-menubar-I426AED2.js +86 -0
- package/dist/chunks/velin-menubar-J66JXRWW.js +86 -0
- package/dist/chunks/velin-menubar-SFEXJE56.js +86 -0
- package/dist/chunks/velin-modal-2HOBXE6F.js +176 -0
- package/dist/chunks/velin-modal-5HYNNNUI.js +176 -0
- package/dist/chunks/velin-modal-AEOTZ67O.js +177 -0
- package/dist/chunks/velin-modal-GLI2XZZB.js +176 -0
- package/dist/chunks/velin-persist-DFPL3QP4.js +110 -0
- package/dist/chunks/velin-popover-EXINRIGJ.js +145 -0
- package/dist/chunks/velin-popover-HQW6Q2Y6.js +172 -0
- package/dist/chunks/velin-popover-OXX7MRZ6.js +145 -0
- package/dist/chunks/velin-popover-T72HK3DF.js +151 -0
- package/dist/chunks/velin-popover-YKCCROJG.js +145 -0
- package/dist/chunks/velin-progress-ring-E4YRLZHH.js +66 -0
- package/dist/chunks/velin-rating-6CDBI6DG.js +91 -0
- package/dist/chunks/velin-rating-N4DI2XNL.js +91 -0
- package/dist/chunks/velin-rating-XRQJV4LO.js +91 -0
- package/dist/chunks/velin-scroll-top-F2FCZNYD.js +58 -0
- package/dist/chunks/velin-scroll-top-OHYVVHHY.js +58 -0
- package/dist/chunks/velin-scrollspy-OKCT4FDV.js +50 -0
- package/dist/chunks/velin-search-2XRQFBCS.js +556 -0
- package/dist/chunks/velin-search-4YHJWYSS.js +556 -0
- package/dist/chunks/velin-search-7B4KJ6TX.js +557 -0
- package/dist/chunks/velin-search-LWHY23Q5.js +474 -0
- package/dist/chunks/velin-secure-field-6PV7RZDJ.js +95 -0
- package/dist/chunks/velin-secure-field-H7FSBUJW.js +95 -0
- package/dist/chunks/velin-secure-field-OFNAXCDP.js +95 -0
- package/dist/chunks/velin-segmented-control-EKL4E3BL.js +109 -0
- package/dist/chunks/velin-segmented-control-GYD5BIXQ.js +109 -0
- package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +109 -0
- package/dist/chunks/velin-sheet-3OGI4HD7.js +119 -0
- package/dist/chunks/velin-sheet-OUK6572T.js +119 -0
- package/dist/chunks/velin-sheet-R2Y67X6H.js +119 -0
- package/dist/chunks/velin-sparkline-GVEANJVW.js +184 -0
- package/dist/chunks/velin-sparkline-KDA3GQAN.js +162 -0
- package/dist/chunks/velin-sparkline-XVTFC6GE.js +181 -0
- package/dist/chunks/velin-stepper-2ZIHLKGM.js +141 -0
- package/dist/chunks/velin-stepper-7ECRUYHD.js +148 -0
- package/dist/chunks/velin-stepper-FYEUEWE6.js +141 -0
- package/dist/chunks/velin-stepper-X54WGHDG.js +141 -0
- package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +133 -0
- package/dist/chunks/velin-tabs-5UQ5ZVMV.js +138 -0
- package/dist/chunks/velin-theme-toggle-32J2CBNC.js +291 -0
- package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +285 -0
- package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +285 -0
- package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +291 -0
- package/dist/chunks/velin-toast-OMMOA2DK.js +109 -0
- package/dist/chunks/velin-toast-RCZ4CSUC.js +109 -0
- package/dist/chunks/velin-toast-RSF2IHT3.js +113 -0
- package/dist/chunks/velin-toast-WEUBTJG6.js +109 -0
- package/dist/chunks/velin-tooltip-664IOHZA.js +112 -0
- package/dist/chunks/velin-tooltip-BDHGRTQB.js +112 -0
- package/dist/chunks/velin-tooltip-HVUOKNVX.js +112 -0
- package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +103 -0
- package/dist/chunks/worker-client-R6DBYPFT.js +46 -0
- package/dist/llms.test.txt +40 -0
- package/dist/llms.txt +40 -0
- package/dist/search-index.json +1773 -0
- package/dist/search-index.test.json +1773 -0
- package/dist/themes/brutalist.min.css +1 -1
- package/dist/themes/midnight.min.css +1 -1
- package/dist/themes/pastel.min.css +1 -1
- package/dist/themes/retro.min.css +1 -1
- package/dist/themes/sunset.min.css +1 -1
- package/dist/velin-agent.json +684 -0
- package/dist/velin-agent.test.json +684 -0
- package/dist/velinstyle-components.iife.js +7715 -3515
- package/dist/velinstyle-components.js +7087 -2859
- package/dist/velinstyle-components.min.js +196 -109
- package/dist/velinstyle.css +604 -65
- package/dist/velinstyle.d.ts +54 -0
- package/dist/velinstyle.min.css +1 -1
- package/examples/search-index.schema.json +33 -0
- package/package.json +56 -10
- package/src/a11y/authentication.css +37 -0
- package/src/a11y/consistent-help.css +22 -0
- package/src/a11y/dragging-alternatives.css +30 -0
- package/src/a11y/focus-appearance.css +20 -0
- package/src/a11y/high-contrast-aaa.css +16 -26
- package/src/a11y/skip-link.css +3 -1
- package/src/a11y/sr-only.css +1 -1
- package/src/base/reset.css +0 -36
- package/src/base/wc-placeholder.css +102 -0
- package/src/components/attributes.css +42 -0
- package/src/components/highlight.css +182 -0
- package/src/components/input-group.css +1 -2
- package/src/components/nav.css +2 -3
- package/src/components/search.css +95 -0
- package/src/components/stepper.css +2 -4
- package/src/components/timeline.css +1 -2
- package/src/components/tooltip.css +2 -4
- package/src/layout/container.css +5 -5
- package/src/layout/grid.css +8 -8
- package/src/layout/patterns.css +5 -5
- package/src/themes/brutalist.css +2 -11
- package/src/themes/corporate.css +0 -9
- package/src/themes/earth.css +0 -9
- package/src/themes/forest.css +0 -9
- package/src/themes/midnight.css +1 -10
- package/src/themes/neon.css +0 -9
- package/src/themes/nordic.css +0 -9
- package/src/themes/ocean.css +0 -9
- package/src/themes/pastel.css +1 -10
- package/src/themes/retro.css +2 -11
- package/src/themes/sharp.css +0 -7
- package/src/themes/soft.css +0 -7
- package/src/themes/sunset.css +1 -10
- package/src/tokens/color.css +45 -46
- package/src/tokens/fonts.css +10 -0
- package/src/tokens/typography.css +4 -5
- package/src/utilities/animation.css +2 -2
- package/src/utilities/display.css +3 -3
- package/src/utilities/divide.css +2 -6
- package/src/utilities/responsive.css +4 -4
- package/src/utilities/safe-area.css +1 -1
- package/src/utilities/scroll-animation.css +34 -0
- package/src/utilities/spacing.css +17 -8
- package/src/utilities/text.css +4 -1
- package/src/velinstyle.css +9 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const DOCS_MOUNT = '/docs/';
|
|
2
|
+
|
|
3
|
+
const DOC_ROOT_SEGMENTS = new Set([
|
|
4
|
+
'getting-started',
|
|
5
|
+
'extend',
|
|
6
|
+
'guides',
|
|
7
|
+
'utilities',
|
|
8
|
+
'components',
|
|
9
|
+
'forms',
|
|
10
|
+
'layout',
|
|
11
|
+
'content',
|
|
12
|
+
'customize',
|
|
13
|
+
'animations',
|
|
14
|
+
'about',
|
|
15
|
+
'helpers',
|
|
16
|
+
'generated',
|
|
17
|
+
'migration',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Docs root URL (…/docs/) — stable even when <base> mis-resolves the script under …/components/assets/.
|
|
22
|
+
*/
|
|
23
|
+
export function getDocsBaseUrl() {
|
|
24
|
+
if (typeof document === 'undefined') {
|
|
25
|
+
return 'https://example.invalid/docs/';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const script = document.querySelector('script[src*="doc-search"]');
|
|
29
|
+
if (script?.src) {
|
|
30
|
+
const u = new URL(script.src);
|
|
31
|
+
const i = u.pathname.indexOf(DOCS_MOUNT);
|
|
32
|
+
if (i !== -1) {
|
|
33
|
+
return u.origin + u.pathname.slice(0, i + DOCS_MOUNT.length);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const path = window.location.pathname.replace(/\\/g, '/');
|
|
38
|
+
const idx = path.indexOf(DOCS_MOUNT);
|
|
39
|
+
if (idx >= 0) {
|
|
40
|
+
return new URL(path.slice(0, idx + DOCS_MOUNT.length), window.location.origin).href;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return new URL('./', window.location.href).href;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function splitUrlHash(url) {
|
|
47
|
+
const i = url.indexOf('#');
|
|
48
|
+
if (i === -1) return { path: url, hash: '' };
|
|
49
|
+
return { path: url.slice(0, i), hash: url.slice(i) };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Strip a mistaken current-section prefix (e.g. components/getting-started/…). */
|
|
53
|
+
export function relativizeDocsPathname(pathname) {
|
|
54
|
+
const docsIdx = pathname.indexOf(DOCS_MOUNT);
|
|
55
|
+
if (docsIdx < 0) return null;
|
|
56
|
+
const rest = pathname.slice(docsIdx + DOCS_MOUNT.length);
|
|
57
|
+
const parts = rest.split('/').filter(Boolean);
|
|
58
|
+
if (
|
|
59
|
+
parts.length >= 2 &&
|
|
60
|
+
DOC_ROOT_SEGMENTS.has(parts[0]) &&
|
|
61
|
+
DOC_ROOT_SEGMENTS.has(parts[1]) &&
|
|
62
|
+
parts[0] !== parts[1]
|
|
63
|
+
) {
|
|
64
|
+
return parts.slice(1).join('/');
|
|
65
|
+
}
|
|
66
|
+
return rest;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resolve a search-index URL to a full navigable URL under the docs root.
|
|
71
|
+
* @param {string} url
|
|
72
|
+
*/
|
|
73
|
+
export function resolveDocsSearchUrl(url) {
|
|
74
|
+
if (!url || url === '#') return '';
|
|
75
|
+
if (typeof window === 'undefined') return url;
|
|
76
|
+
|
|
77
|
+
const { path, hash } = splitUrlHash(url);
|
|
78
|
+
if (!path) return hash || '';
|
|
79
|
+
|
|
80
|
+
let rel = path;
|
|
81
|
+
if (/^https?:\/\//i.test(path)) {
|
|
82
|
+
try {
|
|
83
|
+
const fixed = relativizeDocsPathname(new URL(path).pathname);
|
|
84
|
+
rel = fixed ?? path;
|
|
85
|
+
} catch {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const target = new URL(rel || '', getDocsBaseUrl());
|
|
92
|
+
return target.href + (hash && !target.href.includes('#') ? hash : '');
|
|
93
|
+
} catch {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { CATEGORY_BOOST, normalizeEntry } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight fuzzy/substring search engine (no external deps).
|
|
5
|
+
*/
|
|
6
|
+
export class VelinSearchEngine {
|
|
7
|
+
constructor() {
|
|
8
|
+
/** @type {import('./types.js').SearchEntry[]} */
|
|
9
|
+
this._entries = [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** @param {import('./types.js').SearchEntry[]} entries */
|
|
13
|
+
setEntries(entries) {
|
|
14
|
+
this._entries = entries.map((e) => normalizeEntry(e)).filter(Boolean);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** @param {import('./types.js').SearchEntry[]} entries */
|
|
18
|
+
addEntries(entries) {
|
|
19
|
+
const next = entries.map((e) => normalizeEntry(e)).filter(Boolean);
|
|
20
|
+
const ids = new Set(this._entries.map((e) => e.id));
|
|
21
|
+
for (const e of next) {
|
|
22
|
+
if (!ids.has(e.id)) {
|
|
23
|
+
this._entries.push(e);
|
|
24
|
+
ids.add(e.id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} query
|
|
31
|
+
* @param {object} [opts]
|
|
32
|
+
* @param {number} [opts.limit]
|
|
33
|
+
* @param {number} [opts.minChars]
|
|
34
|
+
* @param {number} [opts.fuzzy] 0–1 threshold for typo tolerance
|
|
35
|
+
* @param {import('./types.js').SearchCategory[]} [opts.categories]
|
|
36
|
+
*/
|
|
37
|
+
query(query, opts = {}) {
|
|
38
|
+
const q = String(query || '').trim().toLowerCase();
|
|
39
|
+
const minChars = opts.minChars ?? 2;
|
|
40
|
+
const limit = opts.limit ?? 12;
|
|
41
|
+
const fuzzy = opts.fuzzy ?? 0.2;
|
|
42
|
+
const categories = opts.categories;
|
|
43
|
+
|
|
44
|
+
if (q.length < minChars) {
|
|
45
|
+
return { results: [], groups: {} };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const scored = [];
|
|
49
|
+
for (const entry of this._entries) {
|
|
50
|
+
if (categories && categories.length && !categories.includes(entry.category)) continue;
|
|
51
|
+
const s = scoreEntry(entry, q, fuzzy);
|
|
52
|
+
if (s > 0) scored.push({ entry, score: s });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
scored.sort((a, b) => b.score - a.score);
|
|
56
|
+
const results = scored.slice(0, limit).map((x) => ({ ...x.entry, _score: x.score }));
|
|
57
|
+
|
|
58
|
+
/** @type {Record<string, typeof results>} */
|
|
59
|
+
const groups = {};
|
|
60
|
+
for (const r of results) {
|
|
61
|
+
if (!groups[r.category]) groups[r.category] = [];
|
|
62
|
+
groups[r.category].push(r);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { results, groups };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('./types.js').SearchEntry} entry
|
|
71
|
+
* @param {string} q
|
|
72
|
+
* @param {number} fuzzy
|
|
73
|
+
*/
|
|
74
|
+
function scoreEntry(entry, q, fuzzy) {
|
|
75
|
+
const title = entry.title.toLowerCase();
|
|
76
|
+
const excerpt = (entry.excerpt || '').toLowerCase();
|
|
77
|
+
const keywords = (entry.keywords || []).join(' ').toLowerCase();
|
|
78
|
+
const boost = (CATEGORY_BOOST[entry.category] || 1) * (entry.weight || 1);
|
|
79
|
+
|
|
80
|
+
let score = 0;
|
|
81
|
+
|
|
82
|
+
if (title === q) score = 100;
|
|
83
|
+
else if (title.startsWith(q)) score = 70;
|
|
84
|
+
else if (title.includes(q)) score = 50;
|
|
85
|
+
else if (keywords.includes(q)) score = 35;
|
|
86
|
+
else if (excerpt.includes(q)) score = 25;
|
|
87
|
+
else if (fuzzy > 0 && fuzzyMatch(title, q, fuzzy)) score = 40;
|
|
88
|
+
else if (fuzzy > 0 && fuzzyMatch(keywords, q, fuzzy)) score = 28;
|
|
89
|
+
else if (fuzzy > 0 && fuzzyMatch(excerpt, q, fuzzy)) score = 18;
|
|
90
|
+
else if (subsequenceMatch(title, q)) score = 22;
|
|
91
|
+
else if (fuzzy > 0) {
|
|
92
|
+
for (const word of title.split(/[^a-z0-9]+/)) {
|
|
93
|
+
if (word.length >= 3 && fuzzyMatch(word, q, fuzzy)) {
|
|
94
|
+
score = 32;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return score > 0 ? score * boost : 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function subsequenceMatch(haystack, needle) {
|
|
104
|
+
let j = 0;
|
|
105
|
+
for (let i = 0; i < haystack.length && j < needle.length; i++) {
|
|
106
|
+
if (haystack[i] === needle[j]) j++;
|
|
107
|
+
}
|
|
108
|
+
return j === needle.length;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Allow ~1 edit per 4 chars when fuzzy threshold met */
|
|
112
|
+
function fuzzyMatch(text, query, threshold) {
|
|
113
|
+
if (!text || !query) return false;
|
|
114
|
+
if (text.includes(query)) return true;
|
|
115
|
+
if (subsequenceMatch(text, query)) return true;
|
|
116
|
+
const maxDist = Math.max(1, Math.floor(query.length * threshold * 2));
|
|
117
|
+
return levenshtein(text.slice(0, Math.min(text.length, query.length + 8)), query) <= maxDist;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function levenshtein(a, b) {
|
|
121
|
+
const m = a.length;
|
|
122
|
+
const n = b.length;
|
|
123
|
+
if (m === 0) return n;
|
|
124
|
+
if (n === 0) return m;
|
|
125
|
+
const dp = new Uint16Array((n + 1) * (m + 1));
|
|
126
|
+
for (let j = 0; j <= n; j++) dp[j] = j;
|
|
127
|
+
for (let i = 1; i <= m; i++) {
|
|
128
|
+
dp[i * (n + 1)] = i;
|
|
129
|
+
for (let j = 1; j <= n; j++) {
|
|
130
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
131
|
+
dp[i * (n + 1) + j] = Math.min(
|
|
132
|
+
dp[(i - 1) * (n + 1) + j] + 1,
|
|
133
|
+
dp[i * (n + 1) + (j - 1)] + 1,
|
|
134
|
+
dp[(i - 1) * (n + 1) + (j - 1)] + cost,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return dp[m * (n + 1) + n];
|
|
139
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape HTML and wrap query matches in <mark class="velin-search-hit">.
|
|
3
|
+
* @param {string} text
|
|
4
|
+
* @param {string} query
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function highlightHtml(text, query) {
|
|
8
|
+
const raw = String(text || '');
|
|
9
|
+
const q = String(query || '').trim();
|
|
10
|
+
if (!q || q.length < 2) return escapeHtml(raw);
|
|
11
|
+
const lower = raw.toLowerCase();
|
|
12
|
+
const ql = q.toLowerCase();
|
|
13
|
+
let start = lower.indexOf(ql);
|
|
14
|
+
let len = q.length;
|
|
15
|
+
if (start === -1) {
|
|
16
|
+
start = fuzzySubsequenceIndex(lower, ql);
|
|
17
|
+
if (start === -1) return escapeHtml(raw);
|
|
18
|
+
len = Math.min(q.length, raw.length - start);
|
|
19
|
+
}
|
|
20
|
+
return (
|
|
21
|
+
escapeHtml(raw.slice(0, start)) +
|
|
22
|
+
'<mark class="velin-search-hit">' +
|
|
23
|
+
escapeHtml(raw.slice(start, start + len)) +
|
|
24
|
+
'</mark>' +
|
|
25
|
+
escapeHtml(raw.slice(start + len))
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function escapeHtml(s) {
|
|
30
|
+
return s
|
|
31
|
+
.replace(/&/g, '&')
|
|
32
|
+
.replace(/</g, '<')
|
|
33
|
+
.replace(/>/g, '>')
|
|
34
|
+
.replace(/"/g, '"');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @returns {number} start index of subsequence or -1 */
|
|
38
|
+
function fuzzySubsequenceIndex(haystack, needle) {
|
|
39
|
+
let j = 0;
|
|
40
|
+
let start = -1;
|
|
41
|
+
for (let i = 0; i < haystack.length && j < needle.length; i++) {
|
|
42
|
+
if (haystack[i] === needle[j]) {
|
|
43
|
+
if (j === 0) start = i;
|
|
44
|
+
j++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return j === needle.length ? start : -1;
|
|
48
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { VelinSearchEngine } from './engine.js';
|
|
2
|
+
import { collectProviderEntries } from './providers.js';
|
|
3
|
+
import { normalizeEntry, SEARCH_CATEGORIES } from './types.js';
|
|
4
|
+
|
|
5
|
+
export { VelinSearchEngine } from './engine.js';
|
|
6
|
+
export { highlightHtml } from './highlight.js';
|
|
7
|
+
export { registerSearchProvider, unregisterSearchProvider, listSearchProviders } from './providers.js';
|
|
8
|
+
export { SEARCH_CATEGORIES, CATEGORY_BOOST, normalizeEntry } from './types.js';
|
|
9
|
+
export { getDocsBaseUrl, resolveDocsSearchUrl, relativizeDocsPathname } from './docs-url.js';
|
|
10
|
+
|
|
11
|
+
const defaultEngine = new VelinSearchEngine();
|
|
12
|
+
let indexLoaded = false;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {import('./types.js').SearchEntry[]|{ entries?: import('./types.js').SearchEntry[] }} data
|
|
16
|
+
*/
|
|
17
|
+
function parseIndexPayload(data) {
|
|
18
|
+
if (Array.isArray(data)) return data;
|
|
19
|
+
if (data && Array.isArray(data.entries)) return data.entries;
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Singleton search API.
|
|
25
|
+
*/
|
|
26
|
+
export const velinSearch = {
|
|
27
|
+
engine: defaultEngine,
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load JSON index from URL (offline after fetch) or pass entries directly.
|
|
31
|
+
* @param {string|import('./types.js').SearchEntry[]} source
|
|
32
|
+
*/
|
|
33
|
+
async loadIndex(source) {
|
|
34
|
+
let entries = [];
|
|
35
|
+
if (typeof source === 'string') {
|
|
36
|
+
const res = await fetch(source);
|
|
37
|
+
if (!res.ok) throw new Error(`Failed to load search index: ${res.status}`);
|
|
38
|
+
entries = parseIndexPayload(await res.json());
|
|
39
|
+
} else if (Array.isArray(source)) {
|
|
40
|
+
entries = source;
|
|
41
|
+
}
|
|
42
|
+
defaultEngine.setEntries(entries);
|
|
43
|
+
const providerEntries = await collectProviderEntries();
|
|
44
|
+
if (providerEntries.length) defaultEngine.addEntries(providerEntries);
|
|
45
|
+
indexLoaded = true;
|
|
46
|
+
return entries.length;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Merge entries without replacing the full index.
|
|
51
|
+
* @param {import('./types.js').SearchEntry[]} entries
|
|
52
|
+
*/
|
|
53
|
+
addEntries(entries) {
|
|
54
|
+
defaultEngine.addEntries(entries);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Refresh entries from registered providers.
|
|
59
|
+
*/
|
|
60
|
+
async refreshProviders() {
|
|
61
|
+
const providerEntries = await collectProviderEntries();
|
|
62
|
+
if (providerEntries.length) defaultEngine.addEntries(providerEntries);
|
|
63
|
+
return providerEntries.length;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} query
|
|
68
|
+
* @param {object} [options]
|
|
69
|
+
*/
|
|
70
|
+
async query(query, options = {}) {
|
|
71
|
+
if (!indexLoaded && defaultEngine._entries.length === 0) {
|
|
72
|
+
await this.refreshProviders();
|
|
73
|
+
}
|
|
74
|
+
return defaultEngine.query(query, options);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
isReady() {
|
|
78
|
+
return indexLoaded || defaultEngine._entries.length > 0;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create an isolated search instance (for multiple widgets).
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* @param {{ worker?: boolean, workerUrl?: string }} [config]
|
|
87
|
+
*/
|
|
88
|
+
export function createSearch(config = {}) {
|
|
89
|
+
const engine = new VelinSearchEngine();
|
|
90
|
+
const wantWorker = config.worker === true;
|
|
91
|
+
let workerClient = null;
|
|
92
|
+
|
|
93
|
+
async function getWorker() {
|
|
94
|
+
if (!wantWorker || workerClient) return workerClient;
|
|
95
|
+
if (typeof Worker === 'undefined') return null;
|
|
96
|
+
const { createSearchWorker } = await import('./worker-client.js');
|
|
97
|
+
workerClient = createSearchWorker(config.workerUrl);
|
|
98
|
+
return workerClient;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
engine,
|
|
103
|
+
async loadIndex(source) {
|
|
104
|
+
let entries = [];
|
|
105
|
+
if (typeof source === 'string') {
|
|
106
|
+
const res = await fetch(source);
|
|
107
|
+
entries = parseIndexPayload(await res.json());
|
|
108
|
+
} else if (Array.isArray(source)) {
|
|
109
|
+
entries = source;
|
|
110
|
+
}
|
|
111
|
+
engine.setEntries(entries);
|
|
112
|
+
const w = await getWorker();
|
|
113
|
+
if (w) await w.setEntries(engine._entries);
|
|
114
|
+
return entries.length;
|
|
115
|
+
},
|
|
116
|
+
async query(q, opts) {
|
|
117
|
+
const w = await getWorker();
|
|
118
|
+
if (w) return w.query(q, opts);
|
|
119
|
+
return engine.query(q, opts);
|
|
120
|
+
},
|
|
121
|
+
addEntries(entries) {
|
|
122
|
+
engine.addEntries(entries);
|
|
123
|
+
void getWorker().then((w) => w?.setEntries(engine._entries));
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** @typedef {() => import('./types.js').SearchEntry[]|Promise<import('./types.js').SearchEntry[]>} SearchProviderFn */
|
|
2
|
+
|
|
3
|
+
/** @type {Map<string, SearchProviderFn>} */
|
|
4
|
+
const providers = new Map();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register a custom search content provider (API hook for developers).
|
|
8
|
+
* @param {string} id
|
|
9
|
+
* @param {SearchProviderFn} fn
|
|
10
|
+
*/
|
|
11
|
+
export function registerSearchProvider(id, fn) {
|
|
12
|
+
if (!id || typeof fn !== 'function') {
|
|
13
|
+
throw new Error('registerSearchProvider(id, fn) requires a non-empty id and function');
|
|
14
|
+
}
|
|
15
|
+
providers.set(id, fn);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function unregisterSearchProvider(id) {
|
|
19
|
+
providers.delete(id);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function listSearchProviders() {
|
|
23
|
+
return [...providers.keys()];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @returns {Promise<import('./types.js').SearchEntry[]>}
|
|
28
|
+
*/
|
|
29
|
+
export async function collectProviderEntries() {
|
|
30
|
+
const all = [];
|
|
31
|
+
for (const fn of providers.values()) {
|
|
32
|
+
const chunk = await fn();
|
|
33
|
+
if (Array.isArray(chunk)) all.push(...chunk);
|
|
34
|
+
}
|
|
35
|
+
return all;
|
|
36
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { sanitizeSearchUrl } from '../../components/sanitize.js';
|
|
2
|
+
|
|
3
|
+
/** @typedef {'docs'|'components'|'api'|'examples'} SearchCategory */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} SearchEntry
|
|
7
|
+
* @property {string} id
|
|
8
|
+
* @property {string} title
|
|
9
|
+
* @property {string} [excerpt]
|
|
10
|
+
* @property {string} url
|
|
11
|
+
* @property {SearchCategory} category
|
|
12
|
+
* @property {string[]} [keywords]
|
|
13
|
+
* @property {number} [weight]
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const SEARCH_CATEGORIES = /** @type {const} */ (['docs', 'components', 'api', 'examples']);
|
|
17
|
+
|
|
18
|
+
export const CATEGORY_BOOST = {
|
|
19
|
+
components: 1.25,
|
|
20
|
+
api: 1.15,
|
|
21
|
+
docs: 1,
|
|
22
|
+
examples: 0.9,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {unknown} entry
|
|
27
|
+
* @returns {SearchEntry|null}
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeEntry(entry) {
|
|
30
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
31
|
+
const e = /** @type {Record<string, unknown>} */ (entry);
|
|
32
|
+
const id = String(e.id || e.url || e.title || '').trim();
|
|
33
|
+
const title = String(e.title || '').trim();
|
|
34
|
+
const url = String(e.url || '').trim();
|
|
35
|
+
if (!id || !title) return null;
|
|
36
|
+
const keywords = Array.isArray(e.keywords)
|
|
37
|
+
? e.keywords.map((k) => String(k))
|
|
38
|
+
: typeof e.keywords === 'string'
|
|
39
|
+
? e.keywords.split(/\s+/).filter(Boolean)
|
|
40
|
+
: [];
|
|
41
|
+
let category = /** @type {SearchCategory} */ (
|
|
42
|
+
SEARCH_CATEGORIES.includes(/** @type {SearchCategory} */ (e.category))
|
|
43
|
+
? e.category
|
|
44
|
+
: 'docs'
|
|
45
|
+
);
|
|
46
|
+
if (!e.category && typeof e.url === 'string') {
|
|
47
|
+
if (e.url.includes('/components/')) category = 'components';
|
|
48
|
+
else if (e.url.includes('/cli/') || e.url.includes('/api/')) category = 'api';
|
|
49
|
+
else if (e.url.includes('samples/') || e.url.includes('examples/')) category = 'examples';
|
|
50
|
+
}
|
|
51
|
+
const safeUrl = url ? sanitizeSearchUrl(url) : `#${id}`;
|
|
52
|
+
return {
|
|
53
|
+
id,
|
|
54
|
+
title,
|
|
55
|
+
excerpt: String(e.excerpt || e.section || '').slice(0, 200),
|
|
56
|
+
url: safeUrl,
|
|
57
|
+
category: /** @type {SearchCategory} */ (category),
|
|
58
|
+
keywords,
|
|
59
|
+
weight: typeof e.weight === 'number' ? e.weight : 1,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
let _nextId = 0;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} [workerUrl]
|
|
5
|
+
*/
|
|
6
|
+
export function createSearchWorker(workerUrl) {
|
|
7
|
+
if (typeof Worker === 'undefined') return null;
|
|
8
|
+
|
|
9
|
+
const url =
|
|
10
|
+
workerUrl ||
|
|
11
|
+
new URL('./worker.js', import.meta.url).href;
|
|
12
|
+
|
|
13
|
+
let worker;
|
|
14
|
+
try {
|
|
15
|
+
worker = new Worker(url, { type: 'module' });
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** @type {Map<number, { resolve: Function, reject: Function }>} */
|
|
21
|
+
const pending = new Map();
|
|
22
|
+
|
|
23
|
+
worker.onmessage = (e) => {
|
|
24
|
+
const { id, ok, result, error } = e.data || {};
|
|
25
|
+
const p = pending.get(id);
|
|
26
|
+
if (!p) return;
|
|
27
|
+
pending.delete(id);
|
|
28
|
+
if (ok) p.resolve(result);
|
|
29
|
+
else p.reject(new Error(error || 'Worker error'));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
worker.onerror = () => {
|
|
33
|
+
for (const p of pending.values()) p.reject(new Error('Worker failed'));
|
|
34
|
+
pending.clear();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function send(type, payload) {
|
|
38
|
+
const id = ++_nextId;
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
pending.set(id, { resolve, reject });
|
|
41
|
+
worker.postMessage({ id, type, payload });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
setEntries(entries) {
|
|
47
|
+
return send('setEntries', { entries });
|
|
48
|
+
},
|
|
49
|
+
query(query, options) {
|
|
50
|
+
return send('query', { query, options });
|
|
51
|
+
},
|
|
52
|
+
terminate() {
|
|
53
|
+
worker.terminate();
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { VelinSearchEngine } from './engine.js';
|
|
2
|
+
|
|
3
|
+
const engine = new VelinSearchEngine();
|
|
4
|
+
|
|
5
|
+
self.onmessage = (event) => {
|
|
6
|
+
const { id, type, payload } = event.data || {};
|
|
7
|
+
try {
|
|
8
|
+
if (type === 'setEntries') {
|
|
9
|
+
engine.setEntries(payload.entries || []);
|
|
10
|
+
self.postMessage({ id, ok: true });
|
|
11
|
+
} else if (type === 'query') {
|
|
12
|
+
const result = engine.query(payload.query, payload.options || {});
|
|
13
|
+
self.postMessage({ id, ok: true, result });
|
|
14
|
+
} else {
|
|
15
|
+
self.postMessage({ id, ok: false, error: 'Unknown message type' });
|
|
16
|
+
}
|
|
17
|
+
} catch (err) {
|
|
18
|
+
self.postMessage({ id, ok: false, error: String(err?.message || err) });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
announce,
|
|
3
|
+
ensureAriaLabel,
|
|
4
|
+
getAnnouncer,
|
|
5
|
+
liveDotLabel,
|
|
6
|
+
respectReducedMotion,
|
|
7
|
+
syncExpanded
|
|
8
|
+
} from "./chunk-Z4JYQ2XX.js";
|
|
9
|
+
export {
|
|
10
|
+
announce,
|
|
11
|
+
ensureAriaLabel,
|
|
12
|
+
getAnnouncer,
|
|
13
|
+
liveDotLabel,
|
|
14
|
+
respectReducedMotion,
|
|
15
|
+
syncExpanded
|
|
16
|
+
};
|