@embedpdf/viewer-chrome 3.0.0-next.13 → 3.0.0-next.15

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.
Files changed (157) hide show
  1. package/README.md +175 -132
  2. package/dist/annotation-fonts-BqoLZ4Mp.js +80 -0
  3. package/dist/annotation-fonts-BqoLZ4Mp.js.map +1 -0
  4. package/dist/annotation-fonts-Dt0IQXhz.cjs +91 -0
  5. package/dist/annotation-fonts-Dt0IQXhz.cjs.map +1 -0
  6. package/dist/annotation-style--rPIZsuP.js +879 -0
  7. package/dist/annotation-style--rPIZsuP.js.map +1 -0
  8. package/dist/annotation-style-Bzvvp8-N.cjs +879 -0
  9. package/dist/annotation-style-Bzvvp8-N.cjs.map +1 -0
  10. package/dist/ar-D9XKAmDx.js +567 -0
  11. package/dist/ar-D9XKAmDx.js.map +1 -0
  12. package/dist/ar-DnEj5vbP.cjs +567 -0
  13. package/dist/ar-DnEj5vbP.cjs.map +1 -0
  14. package/dist/chrome.cjs +378 -221
  15. package/dist/chrome.cjs.map +1 -1
  16. package/dist/chrome.d.cts +5 -34
  17. package/dist/chrome.d.ts +5 -34
  18. package/dist/chrome.js +377 -218
  19. package/dist/chrome.js.map +1 -1
  20. package/dist/commands-BmgxhJ8B.js +953 -0
  21. package/dist/commands-BmgxhJ8B.js.map +1 -0
  22. package/dist/commands-CYt4SFUX.cjs +1060 -0
  23. package/dist/commands-CYt4SFUX.cjs.map +1 -0
  24. package/dist/commands.cjs +12 -595
  25. package/dist/commands.d.cts +29 -4
  26. package/dist/commands.d.ts +29 -4
  27. package/dist/commands.js +2 -593
  28. package/dist/comments-panel-MUVgLCBI.js +514 -0
  29. package/dist/comments-panel-MUVgLCBI.js.map +1 -0
  30. package/dist/comments-panel-pEcQg4Vs.cjs +514 -0
  31. package/dist/comments-panel-pEcQg4Vs.cjs.map +1 -0
  32. package/dist/de-Bdr4aWzm.js +566 -0
  33. package/dist/de-Bdr4aWzm.js.map +1 -0
  34. package/dist/de-Dv6zvbnd.cjs +566 -0
  35. package/dist/de-Dv6zvbnd.cjs.map +1 -0
  36. package/dist/dialog-B5GBisKv.js +55 -0
  37. package/dist/dialog-B5GBisKv.js.map +1 -0
  38. package/dist/dialog-t8F13BL_.cjs +60 -0
  39. package/dist/dialog-t8F13BL_.cjs.map +1 -0
  40. package/dist/es-BP09DR9w.cjs +566 -0
  41. package/dist/es-BP09DR9w.cjs.map +1 -0
  42. package/dist/es-BSXz5tBv.js +566 -0
  43. package/dist/es-BSXz5tBv.js.map +1 -0
  44. package/dist/form-design-panel-B5MXTZ96.cjs +418 -0
  45. package/dist/form-design-panel-B5MXTZ96.cjs.map +1 -0
  46. package/dist/form-design-panel-vWWU9uB-.js +418 -0
  47. package/dist/form-design-panel-vWWU9uB-.js.map +1 -0
  48. package/dist/fr-BBQQn7tJ.cjs +566 -0
  49. package/dist/fr-BBQQn7tJ.cjs.map +1 -0
  50. package/dist/fr-Ckn3K9v1.js +566 -0
  51. package/dist/fr-Ckn3K9v1.js.map +1 -0
  52. package/dist/he-DRsG7bH7.cjs +567 -0
  53. package/dist/he-DRsG7bH7.cjs.map +1 -0
  54. package/dist/he-wdGU5PkU.js +567 -0
  55. package/dist/he-wdGU5PkU.js.map +1 -0
  56. package/dist/icons-BrK5iVL8.js +856 -0
  57. package/dist/icons-BrK5iVL8.js.map +1 -0
  58. package/dist/icons-x0Tc05bs.cjs +1010 -0
  59. package/dist/icons-x0Tc05bs.cjs.map +1 -0
  60. package/dist/index.cjs +3162 -5106
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +636 -143
  63. package/dist/index.d.ts +636 -143
  64. package/dist/index.js +3083 -4975
  65. package/dist/index.js.map +1 -1
  66. package/dist/it-BuP4o0xQ.cjs +566 -0
  67. package/dist/it-BuP4o0xQ.cjs.map +1 -0
  68. package/dist/it-Us-VmvIu.js +566 -0
  69. package/dist/it-Us-VmvIu.js.map +1 -0
  70. package/dist/ja-Cizj0-5O.js +566 -0
  71. package/dist/ja-Cizj0-5O.js.map +1 -0
  72. package/dist/ja-DovEd6ts.cjs +566 -0
  73. package/dist/ja-DovEd6ts.cjs.map +1 -0
  74. package/dist/ko-B-qJEol2.cjs +566 -0
  75. package/dist/ko-B-qJEol2.cjs.map +1 -0
  76. package/dist/ko-DlrXliE1.js +566 -0
  77. package/dist/ko-DlrXliE1.js.map +1 -0
  78. package/dist/labels-BiM9wp9K.cjs +21 -0
  79. package/dist/labels-BiM9wp9K.cjs.map +1 -0
  80. package/dist/labels-C6_i87Oj.js +16 -0
  81. package/dist/labels-C6_i87Oj.js.map +1 -0
  82. package/dist/link-editor-CSjqPnj1.cjs +125 -0
  83. package/dist/link-editor-CSjqPnj1.cjs.map +1 -0
  84. package/dist/link-editor-SxM1nnJU.js +125 -0
  85. package/dist/link-editor-SxM1nnJU.js.map +1 -0
  86. package/dist/measurement-Bs61XWbJ.js +325 -0
  87. package/dist/measurement-Bs61XWbJ.js.map +1 -0
  88. package/dist/measurement-CcCsHPOX.cjs +326 -0
  89. package/dist/measurement-CcCsHPOX.cjs.map +1 -0
  90. package/dist/nl-0aF7M_0h.js +566 -0
  91. package/dist/nl-0aF7M_0h.js.map +1 -0
  92. package/dist/nl-D3KZDDiJ.cjs +566 -0
  93. package/dist/nl-D3KZDDiJ.cjs.map +1 -0
  94. package/dist/pl-BW--AaCI.js +566 -0
  95. package/dist/pl-BW--AaCI.js.map +1 -0
  96. package/dist/pl-DSFhAUqx.cjs +566 -0
  97. package/dist/pl-DSFhAUqx.cjs.map +1 -0
  98. package/dist/pt-C5S-8MC5.cjs +566 -0
  99. package/dist/pt-C5S-8MC5.cjs.map +1 -0
  100. package/dist/pt-DIhpU5pT.js +566 -0
  101. package/dist/pt-DIhpU5pT.js.map +1 -0
  102. package/dist/redact-confirm-B6j36TPw.js +83 -0
  103. package/dist/redact-confirm-B6j36TPw.js.map +1 -0
  104. package/dist/redact-confirm-CX_SY2ZN.cjs +83 -0
  105. package/dist/redact-confirm-CX_SY2ZN.cjs.map +1 -0
  106. package/dist/redaction-panel-BmoIl-Ll.cjs +96 -0
  107. package/dist/redaction-panel-BmoIl-Ll.cjs.map +1 -0
  108. package/dist/redaction-panel-IJDshSJI.js +96 -0
  109. package/dist/redaction-panel-IJDshSJI.js.map +1 -0
  110. package/dist/search-panel-HfIhJm1M.js +249 -0
  111. package/dist/search-panel-HfIhJm1M.js.map +1 -0
  112. package/dist/search-panel-s9IoiOtv.cjs +249 -0
  113. package/dist/search-panel-s9IoiOtv.cjs.map +1 -0
  114. package/dist/sheet-BDsa_Sup.cjs +442 -0
  115. package/dist/sheet-BDsa_Sup.cjs.map +1 -0
  116. package/dist/sheet-R6XDgyEf.js +365 -0
  117. package/dist/sheet-R6XDgyEf.js.map +1 -0
  118. package/dist/sign-dialog-CgnR7akj.cjs +155 -0
  119. package/dist/sign-dialog-CgnR7akj.cjs.map +1 -0
  120. package/dist/sign-dialog-GDqCoQhA.js +155 -0
  121. package/dist/sign-dialog-GDqCoQhA.js.map +1 -0
  122. package/dist/signature-inspector-DyO313Qf.cjs +166 -0
  123. package/dist/signature-inspector-DyO313Qf.cjs.map +1 -0
  124. package/dist/signature-inspector-okFz8do6.js +166 -0
  125. package/dist/signature-inspector-okFz8do6.js.map +1 -0
  126. package/dist/signature-maker-BhCmYyHD.js +393 -0
  127. package/dist/signature-maker-BhCmYyHD.js.map +1 -0
  128. package/dist/signature-maker-BoYa8vgA.cjs +393 -0
  129. package/dist/signature-maker-BoYa8vgA.cjs.map +1 -0
  130. package/dist/signatures-panel-B56PC6d9.cjs +300 -0
  131. package/dist/signatures-panel-B56PC6d9.cjs.map +1 -0
  132. package/dist/signatures-panel-CgnFfhFm.js +300 -0
  133. package/dist/signatures-panel-CgnFfhFm.js.map +1 -0
  134. package/dist/stamp-store-BHvRs-Do.cjs +147 -0
  135. package/dist/stamp-store-BHvRs-Do.cjs.map +1 -0
  136. package/dist/stamp-store-WuF-L3Jj.js +112 -0
  137. package/dist/stamp-store-WuF-L3Jj.js.map +1 -0
  138. package/dist/stamps-panel-B9NYGayn.cjs +231 -0
  139. package/dist/stamps-panel-B9NYGayn.cjs.map +1 -0
  140. package/dist/stamps-panel-CDO0FPyK.js +231 -0
  141. package/dist/stamps-panel-CDO0FPyK.js.map +1 -0
  142. package/dist/toolbar-CJu2wKTU.cjs +1659 -0
  143. package/dist/toolbar-CJu2wKTU.cjs.map +1 -0
  144. package/dist/toolbar-z9od3tKl.js +1564 -0
  145. package/dist/toolbar-z9od3tKl.js.map +1 -0
  146. package/dist/zh-cn-3tmMeHqv.cjs +566 -0
  147. package/dist/zh-cn-3tmMeHqv.cjs.map +1 -0
  148. package/dist/zh-cn-CxkUUBct.js +566 -0
  149. package/dist/zh-cn-CxkUUBct.js.map +1 -0
  150. package/package.json +6 -4
  151. package/src/styles.css +153 -89
  152. package/dist/commands.cjs.map +0 -1
  153. package/dist/commands.js.map +0 -1
  154. package/dist/es-DYwzfxyt.cjs +0 -293
  155. package/dist/es-DYwzfxyt.cjs.map +0 -1
  156. package/dist/es-sN6uqBgD.js +0 -293
  157. package/dist/es-sN6uqBgD.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-panel-HfIhJm1M.js","names":[],"sources":["../src/ui/search-panel.tsx"],"sourcesContent":["/**\n * The search panel — the search sidebar on the plugin-search capability. One\n * shape end to end: the flat `SearchQuery` {text, regex, matchCase, wholeWord}\n * is what the engine matches, what the plugin stores (document-scoped,\n * survives the sidebar closing), and what this box renders.\n * The panel keeps only a draft of it for keystroke/debounce echo.\n *\n * Data flow:\n * type / toggle → validateSearchQuery → (debounced) search(draftQuery)\n * useSearchState() → query / status / hitCount / activeHitIndex (reactive)\n * useSearchHits() → the streamed hit list\n * click a hit / prev / next → goTo/prev/next (capability reveals it on-page)\n *\n * The look: a magnifier input with a clear button, option checkboxes, a\n * results-found counter with prev/next, and a per-page grouped snippet list\n * with the match bolded and the active hit accented, in this app's semantic\n * tokens. matchDiacritics exists in the engine but is deliberately not\n * exposed here.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useSearch,\n useSearchHits,\n useSearchState,\n validateSearchQuery,\n} from '@embedpdf/react/search';\nimport type { SearchHit, SearchQuery, SearchSnippet } from '@embedpdf/react/search';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useSurfaceFocus } from '@embedpdf/react/shell';\nimport { Icon } from './icons';\nimport { useCloseOnPick } from './panel-frame';\nimport { buttonClass } from './toolbar';\n\nconst DEBOUNCE_MS = 300;\n\n/** Field-wise equality on the flat query — the mount/echo no-op guard. */\nconst sameQuery = (left: SearchQuery | null, right: SearchQuery) =>\n left != null &&\n left.text === right.text &&\n !!left.regex === !!right.regex &&\n !!left.matchCase === !!right.matchCase &&\n !!left.wholeWord === !!right.wholeWord;\n\n// ── option checkbox (a peer-checked custom box) ───────────────────────────────\nfunction Checkbox({\n label,\n checked,\n onChange,\n}: {\n label: string;\n checked: boolean;\n onChange: (checked: boolean) => void;\n}) {\n return (\n <label className=\"text-fg-secondary flex cursor-pointer select-none items-center gap-2 text-xs font-medium\">\n <span className=\"relative flex h-4 w-4 items-center justify-center\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(event) => onChange(event.target.checked)}\n className=\"border-border bg-surface checked:border-accent checked:bg-accent rounded-xs peer h-4 w-4 shrink-0 appearance-none border transition-colors\"\n />\n <svg\n viewBox=\"0 0 24 24\"\n className=\"text-on-accent pointer-events-none absolute h-3 w-3 opacity-0 peer-checked:opacity-100\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </span>\n {label}\n </label>\n );\n}\n\n// ── one result row: a snippet with the match bolded, active = accented ───────\nfunction renderSnippet(snippet: SearchSnippet | undefined) {\n if (!snippet) return null;\n return (\n <>\n {snippet.before}\n <span className=\"text-accent font-semibold\">{snippet.match}</span>\n {snippet.after}\n </>\n );\n}\n\nfunction HitLine({\n hit,\n active,\n onClick,\n}: {\n hit: SearchHit;\n active: boolean;\n onClick: () => void;\n}) {\n const ref = useRef<HTMLButtonElement>(null);\n // Keep the active hit visible as prev/next walks the list.\n useEffect(() => {\n if (active) ref.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }, [active]);\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={onClick}\n className={`w-full rounded border p-2 text-left text-sm transition-colors ${\n active\n ? 'border-accent bg-accent-light text-fg'\n : 'border-border-subtle bg-surface text-fg-secondary hover:bg-hover'\n }`}\n >\n <span>{renderSnippet(hit.snippet)}</span>\n </button>\n );\n}\n\n// ── the panel ────────────────────────────────────────────────────────────────\nexport function SearchPanel() {\n const t = useT();\n const search = useSearch();\n const { query, status, hitCount, activeHitIndex } = useSearchState();\n const hits = useSearchHits();\n // A sheet or an overlay closes once a match is picked, so you see it on its page.\n const closeOnPick = useCloseOnPick();\n\n // `query` is the document-scoped stored search (survives the sidebar\n // closing). The box is a controlled draft that starts from it; the panel is\n // keyed on the document (see panels.tsx), so this one-time seed is always\n // the right document's query — switching tabs remounts and reseeds.\n const [draft, setDraft] = useState(() => query?.text ?? '');\n const [matchCase, setMatchCase] = useState(() => query?.matchCase ?? false);\n const [wholeWord, setWholeWord] = useState(() => query?.wholeWord ?? false);\n const [regex, setRegex] = useState(() => query?.regex ?? false);\n const inputRef = useRef<HTMLInputElement>(null);\n // Find and the Search button ask for the cursor: it goes in the field, the text selected.\n useSurfaceFocus('search', inputRef, { select: true });\n\n // The draft is a SearchQuery — same shape the engine matches on.\n const draftQuery: SearchQuery = { text: draft, regex, matchCase, wholeWord };\n // Early feedback on keystroke: the same validator the engine enforces\n // (regex dialect). Invalid patterns never fire a query.\n const validation = draft && regex ? validateSearchQuery(draftQuery) : { ok: true as const };\n\n // Debounced query. The sameQuery guard makes the mount pass (and our own\n // echo coming back through state) a no-op — nothing re-runs until the user\n // actually changes the text or a toggle. Clearing the box clears results.\n useEffect(() => {\n // No-ops: invalid pattern, draft already the stored query (mount pass /\n // our own echo), or an empty box with nothing stored to clear.\n if (!validation.ok || sameQuery(query, draftQuery)) return;\n if (draft === '' && query === null) return;\n const id = setTimeout(() => search.search(draftQuery), DEBOUNCE_MS);\n return () => clearTimeout(id);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [draft, matchCase, wholeWord, regex, query, validation.ok, search]);\n\n // Group hits by page, preserving page order, for the sectioned list.\n const grouped = useMemo(() => {\n const map = new Map<number, { hit: SearchHit; index: number }[]>();\n hits.forEach((hit, index) => {\n const arr = map.get(hit.pageIndex);\n if (arr) arr.push({ hit, index });\n else map.set(hit.pageIndex, [{ hit, index }]);\n });\n return [...map.entries()].sort((left, right) => left[0] - right[0]);\n }, [hits]);\n\n const searching = status === 'searching';\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"p-3\">\n {/* input: magnifier + clear */}\n <div className=\"relative\">\n <div className=\"text-fg-muted pointer-events-none absolute inset-y-0 start-0 flex items-center ps-2.5\">\n <Icon name=\"search\" size={16} />\n </div>\n <input\n ref={inputRef}\n type=\"text\"\n value={draft}\n placeholder={t('demo.searchPlaceholder')}\n onChange={(event) => setDraft(event.target.value)}\n className={`bg-surface text-fg w-full rounded-md border py-1.5 pe-9 ps-8 text-sm outline-none focus:ring-1 ${\n validation.ok\n ? 'border-border focus:border-accent focus:ring-accent'\n : 'border-red-400 focus:border-red-400 focus:ring-red-400'\n }`}\n />\n {draft && (\n <button\n type=\"button\"\n onClick={() => {\n setDraft('');\n inputRef.current?.focus();\n }}\n className=\"text-fg-muted hover:text-fg-secondary absolute inset-y-0 end-0 flex items-center pe-2.5\"\n >\n <Icon name=\"x\" size={16} />\n </button>\n )}\n </div>\n\n {/* the invalid-pattern message, straight from the shared validator */}\n {!validation.ok && <p className=\"mt-1.5 text-xs text-red-500\">{validation.message}</p>}\n\n {/* options — the three VS Code toggles, all composable */}\n <div className=\"mt-3 flex flex-col gap-2\">\n <Checkbox\n label={t('demo.searchCaseSensitive')}\n checked={matchCase}\n onChange={setMatchCase}\n />\n <Checkbox label={t('demo.searchWholeWord')} checked={wholeWord} onChange={setWholeWord} />\n <Checkbox label={t('demo.searchRegex')} checked={regex} onChange={setRegex} />\n </div>\n\n <hr className=\"border-border-subtle mb-2 mt-4\" />\n\n {/* results counter + prev/next */}\n {status !== 'idle' && (\n <div className=\"flex h-8 items-center justify-between\">\n <div className=\"text-fg-muted text-xs\">\n {hitCount === 0\n ? t(searching ? 'demo.searchSearching' : 'demo.searchNoResults')\n : t('demo.searchResults', { params: { count: hitCount } })}\n </div>\n {hitCount > 1 && (\n <div className=\"flex items-center\">\n <button\n type=\"button\"\n title={t('demo.searchPrevious')}\n className={buttonClass(false)}\n onClick={() => search.previousHit()}\n >\n <Icon name=\"chevronLeft\" size={20} className=\"rtl:-scale-x-100\" />\n </button>\n <button\n type=\"button\"\n title={t('demo.searchNext')}\n className={buttonClass(false)}\n onClick={() => search.nextHit()}\n >\n <Icon name=\"chevronRight\" size={20} className=\"rtl:-scale-x-100\" />\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* per-page grouped results */}\n <div className=\"flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto px-3 pb-3\">\n {grouped.map(([pageIndex, items]) => (\n <div key={pageIndex} className=\"mt-2 first:mt-0\">\n <div className=\"bg-surface/80 text-fg-muted py-2 text-xs backdrop-blur\">\n {t('demo.searchPage', { params: { page: pageIndex + 1 } })}\n </div>\n <div className=\"flex flex-col gap-2\">\n {items.map(({ hit, index }) => (\n <HitLine\n key={index}\n hit={hit}\n active={index === activeHitIndex}\n onClick={() => {\n search.goToHit(index);\n closeOnPick();\n }}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,249 @@
1
+ const require_icons = require("./icons-x0Tc05bs.cjs");
2
+ const require_toolbar = require("./toolbar-CJu2wKTU.cjs");
3
+ const require_sheet = require("./sheet-BDsa_Sup.cjs");
4
+ let react = require("react");
5
+ let _embedpdf_react_search = require("@embedpdf/react/search");
6
+ let _embedpdf_react_i18n = require("@embedpdf/react/i18n");
7
+ let _embedpdf_react_shell = require("@embedpdf/react/shell");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/ui/search-panel.tsx
10
+ /**
11
+ * The search panel — the search sidebar on the plugin-search capability. One
12
+ * shape end to end: the flat `SearchQuery` {text, regex, matchCase, wholeWord}
13
+ * is what the engine matches, what the plugin stores (document-scoped,
14
+ * survives the sidebar closing), and what this box renders.
15
+ * The panel keeps only a draft of it for keystroke/debounce echo.
16
+ *
17
+ * Data flow:
18
+ * type / toggle → validateSearchQuery → (debounced) search(draftQuery)
19
+ * useSearchState() → query / status / hitCount / activeHitIndex (reactive)
20
+ * useSearchHits() → the streamed hit list
21
+ * click a hit / prev / next → goTo/prev/next (capability reveals it on-page)
22
+ *
23
+ * The look: a magnifier input with a clear button, option checkboxes, a
24
+ * results-found counter with prev/next, and a per-page grouped snippet list
25
+ * with the match bolded and the active hit accented, in this app's semantic
26
+ * tokens. matchDiacritics exists in the engine but is deliberately not
27
+ * exposed here.
28
+ */
29
+ const DEBOUNCE_MS = 300;
30
+ /** Field-wise equality on the flat query — the mount/echo no-op guard. */
31
+ const sameQuery = (left, right) => left != null && left.text === right.text && !!left.regex === !!right.regex && !!left.matchCase === !!right.matchCase && !!left.wholeWord === !!right.wholeWord;
32
+ function Checkbox({ label, checked, onChange }) {
33
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
34
+ className: "text-fg-secondary flex cursor-pointer select-none items-center gap-2 text-xs font-medium",
35
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
36
+ className: "relative flex h-4 w-4 items-center justify-center",
37
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
38
+ type: "checkbox",
39
+ checked,
40
+ onChange: (event) => onChange(event.target.checked),
41
+ className: "border-border bg-surface checked:border-accent checked:bg-accent rounded-xs peer h-4 w-4 shrink-0 appearance-none border transition-colors"
42
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
43
+ viewBox: "0 0 24 24",
44
+ className: "text-on-accent pointer-events-none absolute h-3 w-3 opacity-0 peer-checked:opacity-100",
45
+ fill: "none",
46
+ stroke: "currentColor",
47
+ strokeWidth: 3,
48
+ strokeLinecap: "round",
49
+ strokeLinejoin: "round",
50
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
51
+ })]
52
+ }), label]
53
+ });
54
+ }
55
+ function renderSnippet(snippet) {
56
+ if (!snippet) return null;
57
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
58
+ snippet.before,
59
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
60
+ className: "text-accent font-semibold",
61
+ children: snippet.match
62
+ }),
63
+ snippet.after
64
+ ] });
65
+ }
66
+ function HitLine({ hit, active, onClick }) {
67
+ const ref = (0, react.useRef)(null);
68
+ (0, react.useEffect)(() => {
69
+ if (active) ref.current?.scrollIntoView({
70
+ behavior: "smooth",
71
+ block: "nearest"
72
+ });
73
+ }, [active]);
74
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
75
+ ref,
76
+ type: "button",
77
+ onClick,
78
+ className: `w-full rounded border p-2 text-left text-sm transition-colors ${active ? "border-accent bg-accent-light text-fg" : "border-border-subtle bg-surface text-fg-secondary hover:bg-hover"}`,
79
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: renderSnippet(hit.snippet) })
80
+ });
81
+ }
82
+ function SearchPanel() {
83
+ const t = (0, _embedpdf_react_i18n.useT)();
84
+ const search = (0, _embedpdf_react_search.useSearch)();
85
+ const { query, status, hitCount, activeHitIndex } = (0, _embedpdf_react_search.useSearchState)();
86
+ const hits = (0, _embedpdf_react_search.useSearchHits)();
87
+ const closeOnPick = require_sheet.useCloseOnPick();
88
+ const [draft, setDraft] = (0, react.useState)(() => query?.text ?? "");
89
+ const [matchCase, setMatchCase] = (0, react.useState)(() => query?.matchCase ?? false);
90
+ const [wholeWord, setWholeWord] = (0, react.useState)(() => query?.wholeWord ?? false);
91
+ const [regex, setRegex] = (0, react.useState)(() => query?.regex ?? false);
92
+ const inputRef = (0, react.useRef)(null);
93
+ (0, _embedpdf_react_shell.useSurfaceFocus)("search", inputRef, { select: true });
94
+ const draftQuery = {
95
+ text: draft,
96
+ regex,
97
+ matchCase,
98
+ wholeWord
99
+ };
100
+ const validation = draft && regex ? (0, _embedpdf_react_search.validateSearchQuery)(draftQuery) : { ok: true };
101
+ (0, react.useEffect)(() => {
102
+ if (!validation.ok || sameQuery(query, draftQuery)) return;
103
+ if (draft === "" && query === null) return;
104
+ const id = setTimeout(() => search.search(draftQuery), DEBOUNCE_MS);
105
+ return () => clearTimeout(id);
106
+ }, [
107
+ draft,
108
+ matchCase,
109
+ wholeWord,
110
+ regex,
111
+ query,
112
+ validation.ok,
113
+ search
114
+ ]);
115
+ const grouped = (0, react.useMemo)(() => {
116
+ const map = /* @__PURE__ */ new Map();
117
+ hits.forEach((hit, index) => {
118
+ const arr = map.get(hit.pageIndex);
119
+ if (arr) arr.push({
120
+ hit,
121
+ index
122
+ });
123
+ else map.set(hit.pageIndex, [{
124
+ hit,
125
+ index
126
+ }]);
127
+ });
128
+ return [...map.entries()].sort((left, right) => left[0] - right[0]);
129
+ }, [hits]);
130
+ const searching = status === "searching";
131
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
132
+ className: "flex min-h-0 flex-1 flex-col",
133
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
134
+ className: "p-3",
135
+ children: [
136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
137
+ className: "relative",
138
+ children: [
139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
140
+ className: "text-fg-muted pointer-events-none absolute inset-y-0 start-0 flex items-center ps-2.5",
141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
142
+ name: "search",
143
+ size: 16
144
+ })
145
+ }),
146
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
147
+ ref: inputRef,
148
+ type: "text",
149
+ value: draft,
150
+ placeholder: t("demo.searchPlaceholder"),
151
+ onChange: (event) => setDraft(event.target.value),
152
+ className: `bg-surface text-fg w-full rounded-md border py-1.5 pe-9 ps-8 text-sm outline-none focus:ring-1 ${validation.ok ? "border-border focus:border-accent focus:ring-accent" : "border-red-400 focus:border-red-400 focus:ring-red-400"}`
153
+ }),
154
+ draft && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
155
+ type: "button",
156
+ onClick: () => {
157
+ setDraft("");
158
+ inputRef.current?.focus();
159
+ },
160
+ className: "text-fg-muted hover:text-fg-secondary absolute inset-y-0 end-0 flex items-center pe-2.5",
161
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
162
+ name: "x",
163
+ size: 16
164
+ })
165
+ })
166
+ ]
167
+ }),
168
+ !validation.ok && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
169
+ className: "mt-1.5 text-xs text-red-500",
170
+ children: validation.message
171
+ }),
172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
173
+ className: "mt-3 flex flex-col gap-2",
174
+ children: [
175
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Checkbox, {
176
+ label: t("demo.searchCaseSensitive"),
177
+ checked: matchCase,
178
+ onChange: setMatchCase
179
+ }),
180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Checkbox, {
181
+ label: t("demo.searchWholeWord"),
182
+ checked: wholeWord,
183
+ onChange: setWholeWord
184
+ }),
185
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Checkbox, {
186
+ label: t("demo.searchRegex"),
187
+ checked: regex,
188
+ onChange: setRegex
189
+ })
190
+ ]
191
+ }),
192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { className: "border-border-subtle mb-2 mt-4" }),
193
+ status !== "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
194
+ className: "flex h-8 items-center justify-between",
195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
196
+ className: "text-fg-muted text-xs",
197
+ children: hitCount === 0 ? t(searching ? "demo.searchSearching" : "demo.searchNoResults") : t("demo.searchResults", { params: { count: hitCount } })
198
+ }), hitCount > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
199
+ className: "flex items-center",
200
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
201
+ type: "button",
202
+ title: t("demo.searchPrevious"),
203
+ className: require_toolbar.buttonClass(false),
204
+ onClick: () => search.previousHit(),
205
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
206
+ name: "chevronLeft",
207
+ size: 20,
208
+ className: "rtl:-scale-x-100"
209
+ })
210
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
211
+ type: "button",
212
+ title: t("demo.searchNext"),
213
+ className: require_toolbar.buttonClass(false),
214
+ onClick: () => search.nextHit(),
215
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons.Icon, {
216
+ name: "chevronRight",
217
+ size: 20,
218
+ className: "rtl:-scale-x-100"
219
+ })
220
+ })]
221
+ })]
222
+ })
223
+ ]
224
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
225
+ className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto px-3 pb-3",
226
+ children: grouped.map(([pageIndex, items]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
227
+ className: "mt-2 first:mt-0",
228
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
229
+ className: "bg-surface/80 text-fg-muted py-2 text-xs backdrop-blur",
230
+ children: t("demo.searchPage", { params: { page: pageIndex + 1 } })
231
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
232
+ className: "flex flex-col gap-2",
233
+ children: items.map(({ hit, index }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HitLine, {
234
+ hit,
235
+ active: index === activeHitIndex,
236
+ onClick: () => {
237
+ search.goToHit(index);
238
+ closeOnPick();
239
+ }
240
+ }, index))
241
+ })]
242
+ }, pageIndex))
243
+ })]
244
+ });
245
+ }
246
+ //#endregion
247
+ exports.SearchPanel = SearchPanel;
248
+
249
+ //# sourceMappingURL=search-panel-s9IoiOtv.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-panel-s9IoiOtv.cjs","names":["useCloseOnPick","Icon","buttonClass"],"sources":["../src/ui/search-panel.tsx"],"sourcesContent":["/**\n * The search panel — the search sidebar on the plugin-search capability. One\n * shape end to end: the flat `SearchQuery` {text, regex, matchCase, wholeWord}\n * is what the engine matches, what the plugin stores (document-scoped,\n * survives the sidebar closing), and what this box renders.\n * The panel keeps only a draft of it for keystroke/debounce echo.\n *\n * Data flow:\n * type / toggle → validateSearchQuery → (debounced) search(draftQuery)\n * useSearchState() → query / status / hitCount / activeHitIndex (reactive)\n * useSearchHits() → the streamed hit list\n * click a hit / prev / next → goTo/prev/next (capability reveals it on-page)\n *\n * The look: a magnifier input with a clear button, option checkboxes, a\n * results-found counter with prev/next, and a per-page grouped snippet list\n * with the match bolded and the active hit accented, in this app's semantic\n * tokens. matchDiacritics exists in the engine but is deliberately not\n * exposed here.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useSearch,\n useSearchHits,\n useSearchState,\n validateSearchQuery,\n} from '@embedpdf/react/search';\nimport type { SearchHit, SearchQuery, SearchSnippet } from '@embedpdf/react/search';\nimport { useT } from '@embedpdf/react/i18n';\nimport { useSurfaceFocus } from '@embedpdf/react/shell';\nimport { Icon } from './icons';\nimport { useCloseOnPick } from './panel-frame';\nimport { buttonClass } from './toolbar';\n\nconst DEBOUNCE_MS = 300;\n\n/** Field-wise equality on the flat query — the mount/echo no-op guard. */\nconst sameQuery = (left: SearchQuery | null, right: SearchQuery) =>\n left != null &&\n left.text === right.text &&\n !!left.regex === !!right.regex &&\n !!left.matchCase === !!right.matchCase &&\n !!left.wholeWord === !!right.wholeWord;\n\n// ── option checkbox (a peer-checked custom box) ───────────────────────────────\nfunction Checkbox({\n label,\n checked,\n onChange,\n}: {\n label: string;\n checked: boolean;\n onChange: (checked: boolean) => void;\n}) {\n return (\n <label className=\"text-fg-secondary flex cursor-pointer select-none items-center gap-2 text-xs font-medium\">\n <span className=\"relative flex h-4 w-4 items-center justify-center\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(event) => onChange(event.target.checked)}\n className=\"border-border bg-surface checked:border-accent checked:bg-accent rounded-xs peer h-4 w-4 shrink-0 appearance-none border transition-colors\"\n />\n <svg\n viewBox=\"0 0 24 24\"\n className=\"text-on-accent pointer-events-none absolute h-3 w-3 opacity-0 peer-checked:opacity-100\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </span>\n {label}\n </label>\n );\n}\n\n// ── one result row: a snippet with the match bolded, active = accented ───────\nfunction renderSnippet(snippet: SearchSnippet | undefined) {\n if (!snippet) return null;\n return (\n <>\n {snippet.before}\n <span className=\"text-accent font-semibold\">{snippet.match}</span>\n {snippet.after}\n </>\n );\n}\n\nfunction HitLine({\n hit,\n active,\n onClick,\n}: {\n hit: SearchHit;\n active: boolean;\n onClick: () => void;\n}) {\n const ref = useRef<HTMLButtonElement>(null);\n // Keep the active hit visible as prev/next walks the list.\n useEffect(() => {\n if (active) ref.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }, [active]);\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={onClick}\n className={`w-full rounded border p-2 text-left text-sm transition-colors ${\n active\n ? 'border-accent bg-accent-light text-fg'\n : 'border-border-subtle bg-surface text-fg-secondary hover:bg-hover'\n }`}\n >\n <span>{renderSnippet(hit.snippet)}</span>\n </button>\n );\n}\n\n// ── the panel ────────────────────────────────────────────────────────────────\nexport function SearchPanel() {\n const t = useT();\n const search = useSearch();\n const { query, status, hitCount, activeHitIndex } = useSearchState();\n const hits = useSearchHits();\n // A sheet or an overlay closes once a match is picked, so you see it on its page.\n const closeOnPick = useCloseOnPick();\n\n // `query` is the document-scoped stored search (survives the sidebar\n // closing). The box is a controlled draft that starts from it; the panel is\n // keyed on the document (see panels.tsx), so this one-time seed is always\n // the right document's query — switching tabs remounts and reseeds.\n const [draft, setDraft] = useState(() => query?.text ?? '');\n const [matchCase, setMatchCase] = useState(() => query?.matchCase ?? false);\n const [wholeWord, setWholeWord] = useState(() => query?.wholeWord ?? false);\n const [regex, setRegex] = useState(() => query?.regex ?? false);\n const inputRef = useRef<HTMLInputElement>(null);\n // Find and the Search button ask for the cursor: it goes in the field, the text selected.\n useSurfaceFocus('search', inputRef, { select: true });\n\n // The draft is a SearchQuery — same shape the engine matches on.\n const draftQuery: SearchQuery = { text: draft, regex, matchCase, wholeWord };\n // Early feedback on keystroke: the same validator the engine enforces\n // (regex dialect). Invalid patterns never fire a query.\n const validation = draft && regex ? validateSearchQuery(draftQuery) : { ok: true as const };\n\n // Debounced query. The sameQuery guard makes the mount pass (and our own\n // echo coming back through state) a no-op — nothing re-runs until the user\n // actually changes the text or a toggle. Clearing the box clears results.\n useEffect(() => {\n // No-ops: invalid pattern, draft already the stored query (mount pass /\n // our own echo), or an empty box with nothing stored to clear.\n if (!validation.ok || sameQuery(query, draftQuery)) return;\n if (draft === '' && query === null) return;\n const id = setTimeout(() => search.search(draftQuery), DEBOUNCE_MS);\n return () => clearTimeout(id);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [draft, matchCase, wholeWord, regex, query, validation.ok, search]);\n\n // Group hits by page, preserving page order, for the sectioned list.\n const grouped = useMemo(() => {\n const map = new Map<number, { hit: SearchHit; index: number }[]>();\n hits.forEach((hit, index) => {\n const arr = map.get(hit.pageIndex);\n if (arr) arr.push({ hit, index });\n else map.set(hit.pageIndex, [{ hit, index }]);\n });\n return [...map.entries()].sort((left, right) => left[0] - right[0]);\n }, [hits]);\n\n const searching = status === 'searching';\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"p-3\">\n {/* input: magnifier + clear */}\n <div className=\"relative\">\n <div className=\"text-fg-muted pointer-events-none absolute inset-y-0 start-0 flex items-center ps-2.5\">\n <Icon name=\"search\" size={16} />\n </div>\n <input\n ref={inputRef}\n type=\"text\"\n value={draft}\n placeholder={t('demo.searchPlaceholder')}\n onChange={(event) => setDraft(event.target.value)}\n className={`bg-surface text-fg w-full rounded-md border py-1.5 pe-9 ps-8 text-sm outline-none focus:ring-1 ${\n validation.ok\n ? 'border-border focus:border-accent focus:ring-accent'\n : 'border-red-400 focus:border-red-400 focus:ring-red-400'\n }`}\n />\n {draft && (\n <button\n type=\"button\"\n onClick={() => {\n setDraft('');\n inputRef.current?.focus();\n }}\n className=\"text-fg-muted hover:text-fg-secondary absolute inset-y-0 end-0 flex items-center pe-2.5\"\n >\n <Icon name=\"x\" size={16} />\n </button>\n )}\n </div>\n\n {/* the invalid-pattern message, straight from the shared validator */}\n {!validation.ok && <p className=\"mt-1.5 text-xs text-red-500\">{validation.message}</p>}\n\n {/* options — the three VS Code toggles, all composable */}\n <div className=\"mt-3 flex flex-col gap-2\">\n <Checkbox\n label={t('demo.searchCaseSensitive')}\n checked={matchCase}\n onChange={setMatchCase}\n />\n <Checkbox label={t('demo.searchWholeWord')} checked={wholeWord} onChange={setWholeWord} />\n <Checkbox label={t('demo.searchRegex')} checked={regex} onChange={setRegex} />\n </div>\n\n <hr className=\"border-border-subtle mb-2 mt-4\" />\n\n {/* results counter + prev/next */}\n {status !== 'idle' && (\n <div className=\"flex h-8 items-center justify-between\">\n <div className=\"text-fg-muted text-xs\">\n {hitCount === 0\n ? t(searching ? 'demo.searchSearching' : 'demo.searchNoResults')\n : t('demo.searchResults', { params: { count: hitCount } })}\n </div>\n {hitCount > 1 && (\n <div className=\"flex items-center\">\n <button\n type=\"button\"\n title={t('demo.searchPrevious')}\n className={buttonClass(false)}\n onClick={() => search.previousHit()}\n >\n <Icon name=\"chevronLeft\" size={20} className=\"rtl:-scale-x-100\" />\n </button>\n <button\n type=\"button\"\n title={t('demo.searchNext')}\n className={buttonClass(false)}\n onClick={() => search.nextHit()}\n >\n <Icon name=\"chevronRight\" size={20} className=\"rtl:-scale-x-100\" />\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* per-page grouped results */}\n <div className=\"flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto px-3 pb-3\">\n {grouped.map(([pageIndex, items]) => (\n <div key={pageIndex} className=\"mt-2 first:mt-0\">\n <div className=\"bg-surface/80 text-fg-muted py-2 text-xs backdrop-blur\">\n {t('demo.searchPage', { params: { page: pageIndex + 1 } })}\n </div>\n <div className=\"flex flex-col gap-2\">\n {items.map(({ hit, index }) => (\n <HitLine\n key={index}\n hit={hit}\n active={index === activeHitIndex}\n onClick={() => {\n search.goToHit(index);\n closeOnPick();\n }}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAM,cAAc;;AAGpB,MAAM,aAAa,MAA0B,UAC3C,QAAQ,QACR,KAAK,SAAS,MAAM,QACpB,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,MAAM,SACzB,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,MAAM,aAC7B,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,MAAM;AAG/B,SAAS,SAAS,EAChB,OACA,SACA,YAKC;CACD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;EAAO,WAAU;EAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;IACE,MAAK;IACI;IACT,WAAW,UAAU,SAAS,MAAM,OAAO,OAAO;IAClD,WAAU;GACX,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IACE,SAAQ;IACR,WAAU;IACV,MAAK;IACL,QAAO;IACP,aAAa;IACb,eAAc;IACd,gBAAe;IAEf,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD,EAAU,QAAO,iBAAkB,CAAA;GAChC,CAAA,CACD;EACL,CAAA,GAAA,KACI;;AAEX;AAGA,SAAS,cAAc,SAAoC;CACzD,IAAI,CAAC,SAAS,OAAO;CACrB,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACG,QAAQ;EACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,WAAU;GAA6B,UAAA,QAAQ;EAAY,CAAA;EAChE,QAAQ;CACT,EAAA,CAAA;AAEN;AAEA,SAAS,QAAQ,EACf,KACA,QACA,WAKC;CACD,MAAM,OAAA,GAAA,MAAA,OAAA,CAAgC,IAAI;CAE1C,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,QAAQ,IAAI,SAAS,eAAe;GAAE,UAAU;GAAU,OAAO;EAAU,CAAC;CAClF,GAAG,CAAC,MAAM,CAAC;CACX,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EACO;EACL,MAAK;EACI;EACT,WAAW,iEACT,SACI,0CACA;EAGN,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,cAAc,IAAI,OAAO,EAAQ,CAAA;CAClC,CAAA;AAEZ;AAGA,SAAgB,cAAc;CAC5B,MAAM,KAAA,GAAA,qBAAA,KAAA,CAAS;CACf,MAAM,UAAA,GAAA,uBAAA,UAAA,CAAmB;CACzB,MAAM,EAAE,OAAO,QAAQ,UAAU,oBAAA,GAAA,uBAAA,eAAA,CAAkC;CACnE,MAAM,QAAA,GAAA,uBAAA,cAAA,CAAqB;CAE3B,MAAM,cAAcA,cAAAA,eAAe;CAMnC,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,SAAA,OAA2B,OAAO,QAAQ,EAAE;CAC1D,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,OAA+B,OAAO,aAAa,KAAK;CAC1E,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,OAA+B,OAAO,aAAa,KAAK;CAC1E,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,SAAA,OAA2B,OAAO,SAAS,KAAK;CAC9D,MAAM,YAAA,GAAA,MAAA,OAAA,CAAoC,IAAI;CAE9C,CAAA,GAAA,sBAAA,gBAAA,CAAgB,UAAU,UAAU,EAAE,QAAQ,KAAK,CAAC;CAGpD,MAAM,aAA0B;EAAE,MAAM;EAAO;EAAO;EAAW;CAAU;CAG3E,MAAM,aAAa,SAAS,SAAA,GAAA,uBAAA,oBAAA,CAA4B,UAAU,IAAI,EAAE,IAAI,KAAc;CAK1F,CAAA,GAAA,MAAA,UAAA,OAAgB;EAGd,IAAI,CAAC,WAAW,MAAM,UAAU,OAAO,UAAU,GAAG;EACpD,IAAI,UAAU,MAAM,UAAU,MAAM;EACpC,MAAM,KAAK,iBAAiB,OAAO,OAAO,UAAU,GAAG,WAAW;EAClE,aAAa,aAAa,EAAE;CAE9B,GAAG;EAAC;EAAO;EAAW;EAAW;EAAO;EAAO,WAAW;EAAI;CAAM,CAAC;CAGrE,MAAM,WAAA,GAAA,MAAA,QAAA,OAAwB;EAC5B,MAAM,sBAAM,IAAI,IAAiD;EACjE,KAAK,SAAS,KAAK,UAAU;GAC3B,MAAM,MAAM,IAAI,IAAI,IAAI,SAAS;GACjC,IAAI,KAAK,IAAI,KAAK;IAAE;IAAK;GAAM,CAAC;QAC3B,IAAI,IAAI,IAAI,WAAW,CAAC;IAAE;IAAK;GAAM,CAAC,CAAC;EAC9C,CAAC;EACD,OAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,MAAM,UAAU,KAAK,KAAK,MAAM,EAAE;CACpE,GAAG,CAAC,IAAI,CAAC;CAET,MAAM,YAAY,WAAW;CAE7B,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IAEE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OACb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,MAAD;QAAM,MAAK;QAAS,MAAM;OAAK,CAAA;MAC5B,CAAA;MACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;OACE,KAAK;OACL,MAAK;OACL,OAAO;OACP,aAAa,EAAE,wBAAwB;OACvC,WAAW,UAAU,SAAS,MAAM,OAAO,KAAK;OAChD,WAAW,kGACT,WAAW,KACP,wDACA;MAEP,CAAA;MACA,SACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,MAAK;OACL,eAAe;QACb,SAAS,EAAE;QACX,SAAS,SAAS,MAAM;OAC1B;OACA,WAAU;OAEV,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACA,cAAAA,MAAD;QAAM,MAAK;QAAI,MAAM;OAAK,CAAA;MACpB,CAAA;KAEP;;IAGJ,CAAC,WAAW,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;KAAG,WAAU;KAA+B,UAAA,WAAW;IAAW,CAAA;IAGrF,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,OAAO,EAAE,0BAA0B;OACnC,SAAS;OACT,UAAU;MACX,CAAA;MACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAU,OAAO,EAAE,sBAAsB;OAAG,SAAS;OAAW,UAAU;MAAe,CAAA;MACzF,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAU,OAAO,EAAE,kBAAkB;OAAG,SAAS;OAAO,UAAU;MAAW,CAAA;KAC1E;;IAEL,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAI,WAAU,iCAAkC,CAAA;IAG/C,WAAW,UACV,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACZ,UAAA,aAAa,IACV,EAAE,YAAY,yBAAyB,sBAAsB,IAC7D,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,SAAS,EAAE,CAAC;KACxD,CAAA,GACJ,WAAW,KACV,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,MAAK;OACL,OAAO,EAAE,qBAAqB;OAC9B,WAAWC,gBAAAA,YAAY,KAAK;OAC5B,eAAe,OAAO,YAAY;OAElC,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACD,cAAAA,MAAD;QAAM,MAAK;QAAc,MAAM;QAAI,WAAU;OAAoB,CAAA;MAC3D,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,MAAK;OACL,OAAO,EAAE,iBAAiB;OAC1B,WAAWC,gBAAAA,YAAY,KAAK;OAC5B,eAAe,OAAO,QAAQ;OAE9B,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACD,cAAAA,MAAD;QAAM,MAAK;QAAe,MAAM;QAAI,WAAU;OAAoB,CAAA;MAC5D,CAAA,CACL;KAEJ,CAAA,CAAA;;GAEJ;EAGL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;GAAK,WAAU;GACZ,UAAA,QAAQ,KAAK,CAAC,WAAW,WACxB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAqB,WAAU;IAA/B,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KACZ,UAAA,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,YAAY,EAAE,EAAE,CAAC;IACtD,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KACZ,UAAA,MAAM,KAAK,EAAE,KAAK,YACjB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MAEO;MACL,QAAQ,UAAU;MAClB,eAAe;OACb,OAAO,QAAQ,KAAK;OACpB,YAAY;MACd;KACD,GAPM,KAON,CACF;IACE,CAAA,CACF;GAjBK,GAAA,SAiBL,CACN;EACE,CAAA,CACF;;AAET"}