hunter-mate 0.1.8 → 0.1.11

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 (81) hide show
  1. package/dist/assets/extension/background/service-worker.js +88 -8
  2. package/dist/assets/extension/build-info.json +1 -1
  3. package/dist/assets/extension/content/index.js +1 -1
  4. package/dist/assets/extension/manifest.json +1 -1
  5. package/dist/assets/extension/offscreen/offscreen.js +1 -1
  6. package/dist/assets/extension/sidepanel/index.css +1 -1
  7. package/dist/assets/extension/sidepanel/index.js +7 -7
  8. package/dist/assets/server/build-info.json +1 -1
  9. package/dist/assets/server/extension/extension-control.controller.d.ts +5 -1
  10. package/dist/assets/server/extension/extension-control.controller.js +29 -13
  11. package/dist/assets/server/extension/extension-control.controller.js.map +1 -1
  12. package/dist/assets/server/extension/extension.module.js +2 -1
  13. package/dist/assets/server/extension/extension.module.js.map +1 -1
  14. package/dist/assets/server/extraction/extraction.service.js +12 -9
  15. package/dist/assets/server/extraction/extraction.service.js.map +1 -1
  16. package/dist/assets/server/extraction/script-rule.js +10 -2
  17. package/dist/assets/server/extraction/script-rule.js.map +1 -1
  18. package/dist/assets/server/governance/governance.command-recorder.d.ts +17 -0
  19. package/dist/assets/server/governance/governance.command-recorder.js +98 -0
  20. package/dist/assets/server/governance/governance.command-recorder.js.map +1 -0
  21. package/dist/assets/server/governance/governance.ingester.d.ts +1 -0
  22. package/dist/assets/server/governance/governance.ingester.js +6 -3
  23. package/dist/assets/server/governance/governance.ingester.js.map +1 -1
  24. package/dist/assets/server/governance/governance.module.js +3 -2
  25. package/dist/assets/server/governance/governance.module.js.map +1 -1
  26. package/dist/assets/server/governance/governance.service.d.ts +8 -0
  27. package/dist/assets/server/governance/governance.service.js +42 -0
  28. package/dist/assets/server/governance/governance.service.js.map +1 -1
  29. package/dist/assets/server/recording/recording.gateway.js +4 -0
  30. package/dist/assets/server/recording/recording.gateway.js.map +1 -1
  31. package/dist/commands/extension.js +3 -1
  32. package/dist/commands/extras.d.ts +2 -6
  33. package/dist/commands/extras.js +90 -42
  34. package/dist/commands/install.d.ts +5 -0
  35. package/dist/commands/install.js +22 -42
  36. package/dist/commands/login.js +27 -1
  37. package/dist/commands/server.d.ts +5 -0
  38. package/dist/commands/server.js +142 -16
  39. package/dist/extension-sync.d.ts +35 -0
  40. package/dist/extension-sync.js +159 -0
  41. package/dist/index.js +3 -0
  42. package/dist/postinstall.d.ts +1 -0
  43. package/dist/postinstall.js +31 -0
  44. package/dist/utils/artifact-checksum.d.ts +4 -0
  45. package/dist/utils/artifact-checksum.js +40 -0
  46. package/dist/utils/local-http.d.ts +7 -0
  47. package/dist/utils/local-http.js +30 -0
  48. package/dist/version.js +53 -41
  49. package/dist/watchdog.d.ts +8 -0
  50. package/dist/watchdog.js +46 -6
  51. package/dist/web/dist/404.html +1 -1
  52. package/dist/web/dist/_next/static/chunks/{main-app-80b038a312917d90.js → main-app-0af848156a5b13fc.js} +1 -1
  53. package/dist/web/dist/_next/static/chunks/pages/{_app-da7d628f49271ece.js → _app-3d844792ec91e4d1.js} +2 -2
  54. package/dist/web/dist/auth/verify.html +1 -1
  55. package/dist/web/dist/auth/verify.txt +1 -1
  56. package/dist/web/dist/companies.html +1 -1
  57. package/dist/web/dist/companies.txt +1 -1
  58. package/dist/web/dist/entity.html +1 -1
  59. package/dist/web/dist/entity.txt +1 -1
  60. package/dist/web/dist/index.html +1 -1
  61. package/dist/web/dist/index.txt +1 -1
  62. package/dist/web/dist/jobs.html +1 -1
  63. package/dist/web/dist/jobs.txt +1 -1
  64. package/dist/web/dist/logs.html +1 -1
  65. package/dist/web/dist/logs.txt +1 -1
  66. package/dist/web/dist/matching/jobs/placeholder/talents.html +1 -1
  67. package/dist/web/dist/matching/jobs/placeholder/talents.txt +1 -1
  68. package/dist/web/dist/matching/portraits/placeholder/jobs.html +1 -1
  69. package/dist/web/dist/matching/portraits/placeholder/jobs.txt +1 -1
  70. package/dist/web/dist/parselog.html +1 -1
  71. package/dist/web/dist/parselog.txt +1 -1
  72. package/dist/web/dist/recordings.html +1 -1
  73. package/dist/web/dist/recordings.txt +1 -1
  74. package/dist/web/dist/snapshots/placeholder/agent-prompt.html +1 -1
  75. package/dist/web/dist/snapshots/placeholder/agent-prompt.txt +1 -1
  76. package/dist/web/dist/talents.html +1 -1
  77. package/dist/web/dist/talents.txt +1 -1
  78. package/package.json +5 -3
  79. package/scripts/postinstall-run.mjs +14 -0
  80. /package/dist/web/dist/_next/static/{WdJr3NhHjlu5xN0qOap1k → 3bVkK4pAgbbmp5r0ThyBL}/_buildManifest.js +0 -0
  81. /package/dist/web/dist/_next/static/{WdJr3NhHjlu5xN0qOap1k → 3bVkK4pAgbbmp5r0ThyBL}/_ssgManifest.js +0 -0
@@ -1,5 +1,5 @@
1
1
  // src/shared/messaging.ts
2
- var SERVER_PORT = 34570;
2
+ var SERVER_PORT = 34571;
3
3
  var WS_URL = `ws://localhost:${SERVER_PORT}/ws`;
4
4
  var SERVER_HTTP_URL = `http://localhost:${SERVER_PORT}`;
5
5
 
@@ -505,6 +505,19 @@ function runControl(command, args) {
505
505
  }
506
506
  function goto(gotoArgs) {
507
507
  if (!gotoArgs.url) return { error: "url_required" };
508
+ let sameUrl = false;
509
+ try {
510
+ sameUrl = new URL(gotoArgs.url, window.location.href).href === window.location.href;
511
+ } catch {
512
+ sameUrl = false;
513
+ }
514
+ if (sameUrl) {
515
+ if (gotoArgs.reload === true) {
516
+ window.location.reload();
517
+ return { navigated: true, reloaded: true, url: gotoArgs.url };
518
+ }
519
+ return { navigated: false, reused: true, url: gotoArgs.url };
520
+ }
508
521
  window.location.href = gotoArgs.url;
509
522
  return { navigated: true, url: gotoArgs.url };
510
523
  }
@@ -986,7 +999,7 @@ globalThis.__cpRunControl = runControl;
986
999
  globalThis.__cpRuntimeVersion = RUNTIME_VERSION;
987
1000
 
988
1001
  // src/generated/control-runtime-source.ts
989
- var control_runtime_source_default = '"use strict";\n(() => {\n // src/shared/control-runtime.ts\n function getRefStore() {\n const g = globalThis;\n if (!g.__cpRefs) {\n g.__cpRefs = { counter: 1, refs: /* @__PURE__ */ new Map(), cssPaths: /* @__PURE__ */ new Map() };\n }\n return g.__cpRefs;\n }\n function allocRef(el) {\n const store = getRefStore();\n for (const [ref2, existing] of store.refs) {\n if (existing === el) return ref2;\n }\n const ref = store.counter++;\n store.refs.set(ref, el);\n store.cssPaths.set(ref, uniqueCssPath(el));\n return ref;\n }\n function uniqueCssPath(el) {\n if (el.id) return `#${CSS.escape(el.id)}`;\n const parts = [];\n let cur = el;\n while (cur && cur !== document.body && cur !== document.documentElement) {\n let sel = cur.tagName.toLowerCase();\n if (cur.id) {\n parts.unshift(`#${CSS.escape(cur.id)}`);\n break;\n }\n const parent = cur.parentElement;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);\n if (sameTag.length > 1) {\n const idx = sameTag.indexOf(cur) + 1;\n sel += `:nth-of-type(${idx})`;\n }\n }\n parts.unshift(sel);\n cur = parent;\n }\n return parts.join(" > ");\n }\n function err(code, message, extra) {\n return { error: { code, message, ...extra || {} } };\n }\n var ERROR_CODES = {\n NOT_FOUND: "not_found",\n STALE_REF: "stale_ref",\n INVALID_SELECTOR: "invalid_selector",\n SELECTOR_NOT_FOUND: "selector_not_found",\n SELECTOR_AMBIGUOUS: "selector_ambiguous",\n SELECTOR_NTH_OUT_OF_RANGE: "selector_nth_out_of_range",\n OPTION_NOT_FOUND: "option_not_found",\n NOT_A_SELECT: "not_a_select"\n };\n var IMPLICIT_ROLE = {\n a: "link",\n button: "button",\n select: "combobox",\n textarea: "textbox",\n h1: "heading",\n h2: "heading",\n h3: "heading",\n h4: "heading",\n h5: "heading",\n h6: "heading",\n img: "img",\n nav: "navigation",\n ul: "list",\n ol: "list",\n li: "listitem",\n main: "main",\n article: "article",\n aside: "complementary",\n header: "banner",\n footer: "contentinfo",\n form: "form",\n table: "table",\n summary: "button",\n option: "option",\n output: "status"\n };\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit.split(/\\s+/)[0];\n const tag = el.tagName.toLowerCase();\n if (tag === "input") {\n const type = el.type || "text";\n if (type === "hidden") return null;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "button" || type === "submit" || type === "reset") return "button";\n if (type === "range") return "slider";\n return "textbox";\n }\n if (tag === "progress") return "progressbar";\n if (tag === "meter") return "meter";\n return IMPLICIT_ROLE[tag] ?? null;\n }\n function accessibleName(el) {\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const parts = labelledby.split(/\\s+/).map((id2) => document.getElementById(id2)?.textContent?.trim() || "");\n const joined = parts.filter(Boolean).join(" ");\n if (joined) return joined.slice(0, 120);\n }\n const id = el.getAttribute("id");\n if (id) {\n const label = document.querySelector(`label[for="${CSS.escape(id)}"]`);\n if (label?.textContent?.trim()) return label.textContent.trim().slice(0, 120);\n }\n if (el.tagName.toLowerCase() === "input") {\n const type = el.type;\n if (type === "button" || type === "submit" || type === "reset") {\n const v = el.value;\n if (v) return v;\n }\n }\n const alt = el.getAttribute("alt");\n if (alt) return alt;\n const title = el.getAttribute("title");\n if (title) return title;\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder;\n return (el.textContent || "").replace(/\\s+/g, " ").trim().slice(0, 120);\n }\n function isVisible(el) {\n if (!(el instanceof HTMLElement) && !(el instanceof SVGElement)) return false;\n const html = el;\n if (html.hasAttribute("hidden")) return false;\n if (html.closest("[hidden]")) return false;\n const style = window.getComputedStyle(html);\n if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;\n if (el instanceof HTMLElement && el.offsetParent === null) {\n const pos = style.position;\n if (pos !== "fixed" && pos !== "sticky") return false;\n }\n return true;\n }\n var INTERACTIVE_SELECTOR = "a[href], button, input, textarea, select, summary, [role], [contenteditable=\'\'], [contenteditable=\'true\'], [onclick], [tabindex]";\n function isInteractive(el) {\n return el.matches(INTERACTIVE_SELECTOR);\n }\n function testidSelector(testid) {\n return `[data-testid="${CSS.escape(testid)}"], [data-test-id="${CSS.escape(testid)}"], [data-test="${CSS.escape(testid)}"], [data-cy="${CSS.escape(testid)}"]`;\n }\n function resolveTargetRich(sel) {\n const targetDesc = sel.ref != null ? String(sel.ref) : sel.css ?? (sel.role || sel.name || sel.label || sel.testid || sel.text ? `${sel.role ? `role=${sel.role} ` : ""}${sel.name ? `name=${sel.name} ` : ""}${sel.label ? `label=${sel.label} ` : ""}${sel.testid ? `testid=${sel.testid} ` : ""}${sel.text ? `text=${sel.text}` : ""}`.trim() : "");\n if (sel.ref != null) {\n const store = getRefStore();\n const el = store.refs.get(sel.ref);\n if (el && document.contains(el) && isVisible(el)) {\n return { el, matches_n: 1, match_level: "exact", target: targetDesc };\n }\n if (el && !document.contains(el)) {\n const cssPath = store.cssPaths.get(sel.ref);\n if (cssPath) {\n const re = document.querySelector(cssPath);\n if (re && isVisible(re)) {\n store.refs.set(sel.ref, re);\n return { el: re, matches_n: 1, match_level: "reidentified", target: targetDesc };\n }\n }\n return { errorEnvelope: err(ERROR_CODES.STALE_REF, "Numeric ref is no longer in the DOM. Re-run state.") };\n }\n return { errorEnvelope: err(ERROR_CODES.NOT_FOUND, "Numeric ref is no longer in the DOM. Re-run state.") };\n }\n let candidates = [];\n if (sel.css) {\n try {\n candidates = Array.from(document.querySelectorAll(sel.css));\n } catch {\n return { errorEnvelope: err(ERROR_CODES.INVALID_SELECTOR, "CSS was rejected by querySelectorAll.", { selector: sel.css }) };\n }\n if (candidates.length === 0) {\n return { errorEnvelope: err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: sel.css, candidates: suggestCandidates() }) };\n }\n } else if (sel.testid) {\n try {\n candidates = Array.from(document.querySelectorAll(testidSelector(sel.testid)));\n } catch {\n return { errorEnvelope: err(ERROR_CODES.INVALID_SELECTOR, "Invalid testid.", { testid: sel.testid }) };\n }\n } else if (sel.role || sel.name || sel.label || sel.text) {\n const wantRole = sel.role ? sel.role.toLowerCase() : null;\n const wantName = sel.name ? sel.name.toLowerCase() : null;\n const wantText = sel.text ? sel.text.toLowerCase() : null;\n let labelTargets = [];\n if (sel.label) {\n const lower = sel.label.toLowerCase();\n labelTargets = Array.from(document.querySelectorAll("label")).filter((l) => (l.textContent || "").trim().toLowerCase().includes(lower)).map((l) => {\n const forId = l.getAttribute("for");\n if (forId) return document.getElementById(forId);\n return l.querySelector("input, select, textarea");\n }).filter((x) => !!x);\n }\n const all = Array.from(document.querySelectorAll("*"));\n candidates = all.filter((el) => {\n if (!isVisible(el)) return false;\n const role = roleOf(el);\n if (wantRole && (!role || role.toLowerCase() !== wantRole)) return false;\n if (wantName) {\n const name = accessibleName(el).toLowerCase();\n if (!name.includes(wantName)) return false;\n }\n if (wantText) {\n const t = (el.textContent || "").replace(/\\s+/g, " ").trim().toLowerCase();\n if (!t.includes(wantText)) return false;\n }\n return true;\n });\n if (labelTargets.length) {\n const set = new Set(labelTargets);\n candidates = labelTargets.concat(candidates.filter((c) => !set.has(c)));\n }\n if (candidates.length === 0) {\n return {\n errorEnvelope: err(\n ERROR_CODES.SELECTOR_NOT_FOUND,\n "Semantic locator matches 0 elements.",\n { role: sel.role, name: sel.name, label: sel.label, text: sel.text, candidates: suggestCandidates() }\n )\n };\n }\n } else {\n return { errorEnvelope: err("no_target", "Provide a numeric ref, CSS selector, or semantic locator (--role/--name/--label/--text/--testid).") };\n }\n const visible = candidates.filter(isVisible);\n const pool = visible.length ? visible : candidates;\n if (sel.nth != null) {\n if (sel.nth < 0 || sel.nth >= pool.length) {\n return { errorEnvelope: err(ERROR_CODES.SELECTOR_NTH_OUT_OF_RANGE, `--nth ${sel.nth} beyond match count ${pool.length}.`) };\n }\n const el = pool[sel.nth];\n return { el, matches_n: pool.length, match_level: "reidentified", target: targetDesc };\n }\n if (pool.length > 1) {\n return {\n errorEnvelope: err(ERROR_CODES.SELECTOR_AMBIGUOUS, `CSS matches ${pool.length} elements and no --nth given.`, {\n matches_n: pool.length,\n candidates: pool.slice(0, 5).map((el) => uniqueCssPath(el))\n })\n };\n }\n return { el: pool[0], matches_n: 1, match_level: "reidentified", target: targetDesc };\n }\n function suggestCandidates() {\n const store = getRefStore();\n const recent = Array.from(store.refs.entries()).slice(-5).map(([, el]) => uniqueCssPath(el));\n return recent;\n }\n function compoundOf(el) {\n const tag = el.tagName.toLowerCase();\n if (tag === "input") {\n const input = el;\n const type = input.type;\n if (["date", "time", "datetime-local", "month", "week"].includes(type)) {\n const formatMap = {\n date: "YYYY-MM-DD",\n time: "HH:mm",\n "datetime-local": "YYYY-MM-DDTHH:mm",\n month: "YYYY-MM",\n week: "YYYY-Www"\n };\n return {\n control: type,\n format: formatMap[type] ?? type,\n current: input.value || "",\n min: input.min || null,\n max: input.max || null\n };\n }\n if (type === "file") {\n const current = Array.from(input.files ?? []).map((f) => f.name);\n return { control: "file", multiple: input.multiple, current, accept: input.accept || null };\n }\n return null;\n }\n if (tag === "select") {\n const select = el;\n const options = Array.from(select.options).slice(0, 50).map((o) => ({\n label: (o.textContent || "").trim(),\n value: o.value,\n selected: o.selected\n }));\n return {\n control: "select",\n multiple: select.multiple,\n current: select.selectedOptions[0] ? (select.selectedOptions[0].textContent || "").trim() : "",\n options,\n options_total: select.options.length\n };\n }\n return null;\n }\n var SKIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "head", "base", "template", "path", "circle", "rect", "g", "defs", "polygon", "line"]);\n function describeElement(el, ref) {\n const tag = el.tagName.toLowerCase();\n const role = el.getAttribute("role") ?? null;\n const name = accessibleName(el);\n const parts = [];\n if (ref != null) parts.push(`[${ref}]`);\n parts.push(tag);\n if (role && role !== IMPLICIT_ROLE[tag]) parts.push(`role=${role}`);\n if (tag === "input") {\n const input = el;\n if (input.type && input.type !== "text") parts.push(`[${input.type}]`);\n if (input.disabled) parts.push("(disabled)");\n }\n if (name) parts.push(JSON.stringify(name.length > 60 ? name.slice(0, 57) + "\\u2026" : name));\n if (tag === "a") {\n const href = el.getAttribute("href");\n if (href) parts.push(`\\u2192 ${href.slice(0, 50)}`);\n }\n if (el.disabled) parts.push("(disabled)");\n return parts.join(" ");\n }\n function buildStateTree() {\n const lines = [];\n const compounds = {};\n const budget = { nodes: 0, chars: 0, truncated: false };\n const MAX_NODES = 400;\n const MAX_CHARS = 9e3;\n let hiddenInteractive = 0;\n function emit(depth, text) {\n const line = " ".repeat(Math.min(depth, 12)) + text;\n lines.push(line);\n budget.chars += line.length;\n }\n function walk(el, depth) {\n if (budget.nodes >= MAX_NODES || budget.chars >= MAX_CHARS) {\n budget.truncated = true;\n return;\n }\n const tag = el.tagName.toLowerCase();\n if (SKIP_TAGS.has(tag)) return;\n budget.nodes++;\n const visible = isVisible(el);\n const interactive = isInteractive(el);\n if (interactive) {\n if (visible) {\n const ref = allocRef(el);\n emit(depth, describeElement(el, ref));\n const compound = compoundOf(el);\n if (compound) compounds[String(ref)] = compound;\n } else {\n hiddenInteractive++;\n return;\n }\n } else if (visible) {\n if (/^h[1-6]$/.test(tag)) {\n const t = accessibleName(el);\n if (t) emit(depth, `${"#".repeat(Number(tag[1]))} ${t}`);\n } else if (tag === "p" || tag === "li" || tag === "td" || tag === "th" || tag === "figcaption" || tag === "blockquote") {\n const ownText = ownTextOf(el);\n if (ownText) emit(depth, ownText.slice(0, 100));\n } else if (tag === "img") {\n const alt = el.getAttribute("alt") || "";\n if (alt) emit(depth, `img ${JSON.stringify(alt.slice(0, 60))}`);\n }\n }\n if (depth < 14) {\n for (const child of Array.from(el.children)) {\n walk(child, depth + 1);\n if (budget.truncated) return;\n }\n }\n }\n function ownTextOf(el) {\n let text = "";\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE) text += node.textContent || "";\n }\n return text.replace(/\\s+/g, " ").trim();\n }\n const root = document.querySelector("main") || document.querySelector("#root") || document.body;\n emit(0, `# ${document.title}`);\n walk(root, 0);\n const scrollable = document.documentElement.scrollHeight > window.innerHeight + 200;\n if (scrollable) {\n emit(0, "\\u2195 page is scrollable \\u2014 run cp.scroll down to reveal more");\n }\n if (hiddenInteractive > 0) {\n emit(0, `\\u2139 ${hiddenInteractive} hidden interactive element(s) not tagged`);\n }\n if (budget.truncated) {\n emit(0, "\\u2026 (snapshot truncated by budget \\u2014 narrow with a selector or scroll)");\n }\n return {\n url: location.href,\n title: document.title,\n text: lines.join("\\n"),\n compounds,\n hints: { scroll: scrollable, hidden_interactive: hiddenInteractive },\n truncated: budget.truncated\n };\n }\n function runControl(command, args) {\n const a = args || {};\n function getVisibleText(el) {\n return (el.textContent || "").trim().replace(/\\s+/g, " ").slice(0, 120);\n }\n function getAttrs(el) {\n const attrs = {};\n for (const name of ["id", "class", "name", "type", "href", "src", "placeholder", "value", "role", "aria-label", "data-testid"]) {\n attrs[name] = el.getAttribute(name);\n }\n return attrs;\n }\n function sel() {\n return {\n ref: a.ref,\n css: a.css,\n nth: a.nth,\n role: a.role,\n name: a.name,\n label: a.label,\n text: a.text,\n testid: a.testid\n };\n }\n function findElements(findArgs) {\n const limit = findArgs.limit ?? 20;\n const textMax = findArgs["text-max"] ?? 120;\n let elements = [];\n if (findArgs.css) {\n try {\n elements = Array.from(document.querySelectorAll(findArgs.css));\n } catch {\n return err(ERROR_CODES.INVALID_SELECTOR, "CSS was rejected by querySelectorAll.", { selector: findArgs.css });\n }\n } else if (findArgs.testid) {\n try {\n elements = Array.from(document.querySelectorAll(testidSelector(findArgs.testid)));\n } catch {\n return err(ERROR_CODES.INVALID_SELECTOR, "Invalid testid.");\n }\n } else if (findArgs.role || findArgs.name || findArgs.label) {\n const r = resolveTargetRich({ role: findArgs.role, name: findArgs.name, label: findArgs.label, text: findArgs.text });\n if ("errorEnvelope" in r) return r.errorEnvelope;\n elements = document.querySelectorAll("*").length ? semanticCandidates(findArgs) : [];\n } else if (findArgs.text) {\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);\n const lower = findArgs.text.toLowerCase();\n const seen = /* @__PURE__ */ new Set();\n let node;\n while (node = walker.nextNode()) {\n if (node.textContent?.toLowerCase().includes(lower)) {\n const el = node.parentElement;\n if (el && !seen.has(el)) {\n seen.add(el);\n elements.push(el);\n if (elements.length >= limit) break;\n }\n }\n }\n } else {\n elements = Array.from(document.querySelectorAll(INTERACTIVE_SELECTOR));\n }\n const out = elements.slice(0, limit).map((el, i) => {\n const ref = allocRef(el);\n return {\n nth: i,\n ref,\n tag: el.tagName.toLowerCase(),\n role: roleOf(el),\n text: getVisibleText(el).slice(0, textMax),\n attrs: getAttrs(el),\n visible: isVisible(el),\n compound: compoundOf(el) ?? void 0\n };\n });\n return out;\n }\n function semanticCandidates(findArgs) {\n const wantRole = findArgs.role ? findArgs.role.toLowerCase() : null;\n const wantName = findArgs.name ? findArgs.name.toLowerCase() : null;\n const wantText = findArgs.text ? findArgs.text.toLowerCase() : null;\n const all = Array.from(document.querySelectorAll("*"));\n return all.filter((el) => {\n if (!isVisible(el)) return false;\n if (wantRole) {\n const role = roleOf(el);\n if (!role || role.toLowerCase() !== wantRole) return false;\n }\n if (wantName && !accessibleName(el).toLowerCase().includes(wantName)) return false;\n if (wantText && !(el.textContent || "").toLowerCase().includes(wantText)) return false;\n return true;\n });\n }\n function pageState() {\n return {\n url: location.href,\n title: document.title,\n width: window.innerWidth,\n height: window.innerHeight,\n devicePixelRatio: window.devicePixelRatio\n };\n }\n function goto(gotoArgs) {\n if (!gotoArgs.url) return { error: "url_required" };\n window.location.href = gotoArgs.url;\n return { navigated: true, url: gotoArgs.url };\n }\n function evalScript(evalArgs) {\n if (!evalArgs.script) return { error: "script_required" };\n try {\n const fn = new Function(evalArgs.script);\n const result = fn();\n return { result };\n } catch (e) {\n return { error: String(e) };\n }\n }\n function fillElement(fArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable) {\n const isContentEditable = el.isContentEditable;\n if (isContentEditable) {\n el.innerText = fArgs.text ?? "";\n } else {\n input.value = fArgs.text ?? "";\n }\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n const actual = isContentEditable ? el.innerText : input.value;\n return actual === (fArgs.text ?? "") ? { filled: true, verified: true, text: fArgs.text, actual, target, matches_n, match_level } : err("verify_failed", "Input value diverged after fill (controlled component?).", { actual });\n }\n return err("invalid_type", "fill only supports input, textarea, and contenteditable.");\n }\n function typeElement(tArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n el.focus({ preventScroll: true });\n input.value = tArgs.text ?? "";\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n let autocomplete = false;\n try {\n autocomplete = !!document.querySelector("[role=\'listbox\'], [role=\'combobox\'] [role=\'listbox\'], datalist, .ant-select-dropdown:not([style*=\'hidden\']), [id^=\'listbox\']");\n } catch {\n }\n return { typed: true, text: tArgs.text, target, matches_n, match_level, autocomplete };\n }\n function clickElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.scrollIntoView({ block: "center" });\n el.click();\n return { clicked: true, target, matches_n, match_level, tag: el.tagName.toLowerCase(), text: getVisibleText(el) };\n }\n function selectElement(sArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n if (el.tagName !== "SELECT") {\n return err(ERROR_CODES.NOT_A_SELECT, "select was called on a non-<select> element.", { tag: el.tagName.toLowerCase() });\n }\n const select = el;\n const want = sArgs.label ?? sArgs.value ?? sArgs.option;\n if (!want) return err("option_required", "Provide label or value.");\n const byLabel = Array.from(select.options).find((o) => (o.textContent || "").trim() === want);\n const byValue = Array.from(select.options).find((o) => o.value === want);\n const opt = byLabel ?? byValue;\n if (!opt) {\n const available = Array.from(select.options).slice(0, 50).map((o) => (o.textContent || "").trim());\n return err(ERROR_CODES.OPTION_NOT_FOUND, "Option not found.", { available });\n }\n select.value = opt.value;\n select.dispatchEvent(new Event("input", { bubbles: true }));\n select.dispatchEvent(new Event("change", { bubbles: true }));\n return { selected: true, value: select.value, target, matches_n, match_level };\n }\n function pressKeys(kArgs) {\n const keyCombo = kArgs.keys ?? kArgs.key ?? "";\n if (!keyCombo) return { error: "keys_required" };\n const comboParts = keyCombo.split("+").map((s) => s.trim());\n const main = comboParts[comboParts.length - 1];\n const ctrl = comboParts.includes("Control") || comboParts.includes("Ctrl") || comboParts.includes("Meta") || comboParts.includes("Cmd");\n const alt = comboParts.includes("Alt");\n const shift = comboParts.includes("Shift");\n const target = document.activeElement ?? document.body;\n const opts = { key: main, bubbles: true, cancelable: true, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: comboParts.includes("Meta") || comboParts.includes("Cmd") };\n target.dispatchEvent(new KeyboardEvent("keydown", opts));\n target.dispatchEvent(new KeyboardEvent("keypress", opts));\n target.dispatchEvent(new KeyboardEvent("keyup", opts));\n return { pressed: true, key: keyCombo };\n }\n function hoverElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const rect = el.getBoundingClientRect();\n const opts = { bubbles: true, cancelable: true, clientX: rect.x + rect.width / 2, clientY: rect.y + rect.height / 2 };\n el.dispatchEvent(new MouseEvent("mouseover", opts));\n el.dispatchEvent(new MouseEvent("mousemove", opts));\n return { hovered: true, target, matches_n, match_level };\n }\n function focusElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.focus({ preventScroll: true });\n return { focused: true, target, matches_n, match_level };\n }\n function dblclickElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.dispatchEvent(new MouseEvent("dblclick", { bubbles: true, cancelable: true }));\n return { dblclicked: true, target, matches_n, match_level };\n }\n function setChecked(want) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n const isAria = input.type !== "checkbox" && input.type !== "radio" && el.getAttribute("aria-checked") != null;\n if (!isAria && input.type !== "checkbox" && input.type !== "radio") {\n return err("invalid_type", "Target is not a checkbox/radio/aria-checked control.", { tag: el.tagName.toLowerCase() });\n }\n if (isAria) {\n el.setAttribute("aria-checked", String(want));\n el.dispatchEvent(new Event("change", { bubbles: true }));\n return { [want ? "checked" : "unchecked"]: true, changed: true, target, matches_n, match_level, kind: "aria" };\n }\n if (input.type === "radio" && !want) {\n return err("invalid_type", "Radio buttons cannot be unchecked directly; select another radio in the group.");\n }\n const changed = input.checked !== want;\n input.checked = want;\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n return { [want ? "checked" : "unchecked"]: true, changed, target, matches_n, match_level, kind: input.type };\n }\n function scrollElement(sArgs) {\n const dir = sArgs.direction ?? "down";\n const amount = sArgs.amount ?? 500;\n if (dir !== "up" && dir !== "down") return { error: "invalid_direction" };\n const dy = dir === "down" ? amount : -amount;\n window.scrollBy({ top: dy, behavior: "instant" });\n return {\n scrolled: true,\n direction: dir,\n amount,\n scrollY: Math.round(window.scrollY),\n scrollHeight: document.documentElement.scrollHeight\n };\n }\n function backNav() {\n history.back();\n return { back: true };\n }\n function htmlJsonTree(el, depth, maxDepth, childrenMax, textMax, truncated) {\n const children = Array.from(el.children);\n const ownText = (el.textContent || "").replace(/\\s+/g, " ").trim();\n const node = {\n tag: el.tagName.toLowerCase(),\n attrs: getAttrs(el),\n text: ownText.length > textMax ? (ownText.slice(0, textMax), truncated.text_truncated = (truncated.text_truncated || 0) + 1, ownText.slice(0, textMax)) : ownText,\n children: [],\n compound: compoundOf(el) ?? void 0\n };\n if (depth < maxDepth) {\n let kids = children;\n if (children.length > childrenMax) {\n truncated.children_dropped = (truncated.children_dropped || 0) + (children.length - childrenMax);\n kids = children.slice(0, childrenMax);\n }\n node.children = kids.map((c) => htmlJsonTree(c, depth + 1, maxDepth, childrenMax, textMax, truncated));\n } else if (children.length) {\n truncated.depth = (truncated.depth || 0) + 1;\n }\n return node;\n }\n function getElement(gArgs) {\n const what = gArgs.what ?? "text";\n if (what === "title") return { value: document.title, matches_n: 1, match_level: "exact" };\n if (what === "url") return { value: location.href, matches_n: 1, match_level: "exact" };\n if (what === "html") {\n if (gArgs["as"] === "json") {\n const root2 = gArgs.css ? document.querySelector(gArgs.css) : document.body;\n if (!root2) return err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: gArgs.css });\n const truncated = {};\n const tree = htmlJsonTree(root2, 0, gArgs.depth ?? 6, gArgs["children-max"] ?? 20, gArgs["text-max"] ?? 200, truncated);\n return {\n value: tree,\n truncated: Object.keys(truncated).length ? truncated : void 0\n };\n }\n const root = gArgs.css ? document.querySelector(gArgs.css) : document.body;\n if (!root) return err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: gArgs.css });\n let html = root.outerHTML;\n if (gArgs.max && html.length > gArgs.max) html = html.slice(0, gArgs.max) + "\\u2026";\n return { value: html };\n }\n const r = resolveTargetRich(gArgs);\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level } = r;\n switch (what) {\n case "text": {\n const t = getVisibleText(el);\n return { value: t.length > 2e3 ? t.slice(0, 2e3) : t, matches_n, match_level };\n }\n case "value":\n return { value: el.value ?? el.innerText ?? null, matches_n, match_level };\n case "attributes":\n return { value: getAttrs(el), matches_n, match_level };\n default:\n return { error: `unknown_what: ${what}` };\n }\n }\n function extractMarkdown(eArgs) {\n const chunkSize = eArgs["chunk-size"] ?? 0;\n const start = eArgs.start ?? 0;\n const scope = eArgs.selector ? document.querySelector(eArgs.selector) : document.querySelector("main") || document.querySelector("article") || document.body;\n if (!scope) return { error: "scope_not_found" };\n const parts = [];\n const walker = document.createTreeWalker(scope, NodeFilter.SHOW_ELEMENT);\n let node;\n while (node = walker.nextNode()) {\n const el = node;\n const tag = el.tagName.toLowerCase();\n const text = getVisibleText(el);\n if (!text) continue;\n if (tag === "h1") parts.push(`# ${text}`);\n else if (tag === "h2") parts.push(`## ${text}`);\n else if (tag === "h3") parts.push(`### ${text}`);\n else if (tag === "p" || tag === "li") parts.push(text);\n else if (tag === "a") parts.push(`[${text}](${el.getAttribute("href") ?? ""})`);\n }\n const full = parts.join("\\n\\n");\n const sliced = chunkSize > 0 ? full.slice(start, start + chunkSize) : full.slice(start);\n return {\n url: location.href,\n title: document.title,\n selector: eArgs.selector ?? "auto",\n total_chars: full.length,\n chunk_size: chunkSize,\n start,\n end: start + sliced.length,\n next_start_char: start + sliced.length < full.length ? start + sliced.length : null,\n content: sliced\n };\n }\n function listFrames() {\n const frames = Array.from(document.querySelectorAll("iframe")).map((f, index) => {\n const src = f.getAttribute("src") ?? "";\n let crossOrigin = false;\n try {\n if (src && /^https?:/.test(src)) crossOrigin = new URL(src, location.href).origin !== location.origin;\n } catch {\n }\n return { index, url: src, name: f.getAttribute("name") ?? "", cross_origin: crossOrigin };\n });\n return { frames };\n }\n function screenshotMeta() {\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n url: location.href,\n title: document.title\n };\n }\n function startTitleMarquee(tArgs) {\n const g = globalThis;\n if (g.__cpMarquee) {\n clearInterval(g.__cpMarquee.interval);\n } else {\n g.__cpMarquee = { original: document.title, interval: 0 };\n }\n const prefix = tArgs.text ?? "\\u{1F916}";\n const base = g.__cpMarquee.original;\n const full = `${prefix} ${base}`;\n const speed = Math.max(100, Math.min(2e3, tArgs.speed ?? 300));\n let pos = 0;\n const tick = () => {\n const display = full.slice(pos) + " " + full.slice(0, pos);\n document.title = display.slice(0, 60);\n pos = (pos + 1) % (full.length + 1 || 1);\n };\n tick();\n g.__cpMarquee.interval = window.setInterval(tick, speed);\n return { started: true, original: base, prefix, speed };\n }\n function stopTitleMarquee() {\n const g = globalThis;\n const state = g.__cpMarquee;\n if (!state) return { restored: false, reason: "no_active_marquee" };\n clearInterval(state.interval);\n document.title = state.original;\n delete g.__cpMarquee;\n return { restored: true, original: state.original };\n }\n function networkEntriesLegacy(nArgs) {\n const all = performance.getEntriesByType("resource").map((e) => ({\n url: e.name,\n type: e.initiatorType ?? "unknown",\n duration: Math.round(e.duration)\n }));\n let entries = all;\n if (nArgs.filter) {\n const keywords = nArgs.filter.split(",").map((s) => s.trim().toLowerCase());\n entries = all.filter((e) => keywords.some((kw) => e.url.toLowerCase().includes(kw)));\n }\n return { entries, count: entries.length, source: "performance-api" };\n }\n async function waitForCondition(wArgs) {\n const timeout = wArgs.timeout ?? 1e4;\n if (wArgs.time != null) {\n await new Promise((res) => setTimeout(res, Math.min(wArgs.time * 1e3, 3e4)));\n return { waited: true, kind: "time", seconds: wArgs.time };\n }\n const deadline = Date.now() + timeout;\n const pollInterval = 200;\n if (wArgs.selector) {\n while (Date.now() < deadline) {\n if (document.querySelector(wArgs.selector)) return { waited: true, kind: "selector", selector: wArgs.selector, elapsedMs: timeout - (deadline - Date.now()) };\n await new Promise((res) => setTimeout(res, pollInterval));\n }\n return err("wait_timeout", `Selector not found within ${timeout}ms.`, { selector: wArgs.selector });\n }\n if (wArgs.text) {\n const lower = wArgs.text.toLowerCase();\n while (Date.now() < deadline) {\n if ((document.body.textContent || "").toLowerCase().includes(lower)) {\n return { waited: true, kind: "text", text: wArgs.text, elapsedMs: timeout - (deadline - Date.now()) };\n }\n await new Promise((res) => setTimeout(res, pollInterval));\n }\n return err("wait_timeout", `Text not found within ${timeout}ms.`, { text: wArgs.text });\n }\n return { error: "no_condition" };\n }\n function uploadFiles(uArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n if (input.type !== "file") return err("invalid_type", "Target is not an input[type=file].", { tag: el.tagName.toLowerCase() });\n try {\n const dt = new DataTransfer();\n for (const name of uArgs.files ?? []) {\n dt.items.add(new File([""], name.split("/").pop() || name));\n }\n input.files = dt.files;\n } catch {\n }\n input.dispatchEvent(new Event("change", { bubbles: true }));\n return {\n uploaded: true,\n files: uArgs.files ?? [],\n file_names: (uArgs.files ?? []).map((f) => f.split("/").pop()),\n target,\n matches_n,\n match_level,\n multiple: input.multiple,\n accept: input.accept || null\n };\n }\n function dragElements(dArgs) {\n const r1 = resolveTargetRich(sel());\n if ("errorEnvelope" in r1) return r1.errorEnvelope;\n const r2 = resolveTargetRich({ ref: dArgs.ref2, css: dArgs.css2 });\n if ("errorEnvelope" in r2) return r2.errorEnvelope;\n const src = r1.el;\n const tgt = r2.el;\n const make = (type) => {\n const r1rect = src.getBoundingClientRect();\n return new DragEvent(type, { bubbles: true, cancelable: true, clientX: r1rect.x + r1rect.width / 2, clientY: r1rect.y + r1rect.height / 2 });\n };\n src.dispatchEvent(make("dragstart"));\n tgt.dispatchEvent(new DragEvent("dragenter", { bubbles: true }));\n tgt.dispatchEvent(new DragEvent("dragover", { bubbles: true, cancelable: true }));\n tgt.dispatchEvent(new DragEvent("drop", { bubbles: true, cancelable: true }));\n src.dispatchEvent(new DragEvent("dragend", { bubbles: true }));\n return { dragged: true, source: String(sel().ref ?? sel().css ?? ""), target: String(dArgs.ref2 ?? dArgs.css2 ?? ""), source_matches_n: r1.matches_n, target_matches_n: r2.matches_n };\n }\n switch (command) {\n case "cp.state":\n return a.tree === false ? pageState() : buildStateTree();\n case "cp.snapshot":\n return buildStateTree();\n case "cp.find":\n return findElements({\n css: a.css,\n text: a.text,\n limit: a.limit,\n role: a.role,\n name: a.name,\n label: a.label,\n testid: a.testid,\n "text-max": a["text-max"]\n });\n case "cp.click":\n return clickElement();\n case "cp.type":\n return typeElement({ text: a.text });\n case "cp.goto":\n return goto({ url: a.url });\n case "cp.eval":\n return evalScript({ script: a.script });\n case "cp.fill":\n return fillElement({ text: a.text });\n case "cp.select":\n return selectElement({ label: a.label, value: a.value, option: a.option });\n case "cp.keys":\n return pressKeys({ keys: a.keys, key: a.key });\n case "cp.hover":\n return hoverElement();\n case "cp.focus":\n return focusElement();\n case "cp.dblclick":\n return dblclickElement();\n case "cp.check":\n return setChecked(true);\n case "cp.uncheck":\n return setChecked(false);\n case "cp.scroll":\n return scrollElement({ direction: a.direction, amount: a.amount });\n case "cp.back":\n return backNav();\n case "cp.get":\n return getElement({\n what: a.what,\n css: a.css,\n ref: a.ref,\n nth: a.nth,\n "as": a["as"],\n depth: a.depth,\n "children-max": a["children-max"],\n "text-max": a["text-max"],\n max: a.max,\n role: a.role,\n name: a.name,\n label: a.label,\n text: a.text,\n testid: a.testid\n });\n case "cp.extract":\n return extractMarkdown({\n "chunk-size": a["chunk-size"],\n start: a.start,\n selector: a.selector\n });\n case "cp.frames":\n return listFrames();\n case "cp.screenshot":\n return screenshotMeta();\n case "cp.network":\n return networkEntriesLegacy({ filter: a.filter });\n case "cp.wait":\n return waitForCondition({\n selector: a.selector,\n text: a.text,\n time: a.time,\n timeout: a.timeout\n });\n case "cp.upload":\n return uploadFiles({ files: a.files });\n case "cp.drag":\n return dragElements({ ref2: a.ref2, css2: a.css2 });\n case "cp.title.marquee":\n return startTitleMarquee({ text: a.text, speed: a.speed });\n case "cp.title.restore":\n return stopTitleMarquee();\n default:\n return { error: `unknown_command: ${command}` };\n }\n }\n var RUNTIME_VERSION = "0.3.0-opencli-parity";\n globalThis.__cpRunControl = runControl;\n globalThis.__cpRuntimeVersion = RUNTIME_VERSION;\n})();\n';
1002
+ var control_runtime_source_default = '"use strict";\n(() => {\n // src/shared/control-runtime.ts\n function getRefStore() {\n const g = globalThis;\n if (!g.__cpRefs) {\n g.__cpRefs = { counter: 1, refs: /* @__PURE__ */ new Map(), cssPaths: /* @__PURE__ */ new Map() };\n }\n return g.__cpRefs;\n }\n function allocRef(el) {\n const store = getRefStore();\n for (const [ref2, existing] of store.refs) {\n if (existing === el) return ref2;\n }\n const ref = store.counter++;\n store.refs.set(ref, el);\n store.cssPaths.set(ref, uniqueCssPath(el));\n return ref;\n }\n function uniqueCssPath(el) {\n if (el.id) return `#${CSS.escape(el.id)}`;\n const parts = [];\n let cur = el;\n while (cur && cur !== document.body && cur !== document.documentElement) {\n let sel = cur.tagName.toLowerCase();\n if (cur.id) {\n parts.unshift(`#${CSS.escape(cur.id)}`);\n break;\n }\n const parent = cur.parentElement;\n if (parent) {\n const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);\n if (sameTag.length > 1) {\n const idx = sameTag.indexOf(cur) + 1;\n sel += `:nth-of-type(${idx})`;\n }\n }\n parts.unshift(sel);\n cur = parent;\n }\n return parts.join(" > ");\n }\n function err(code, message, extra) {\n return { error: { code, message, ...extra || {} } };\n }\n var ERROR_CODES = {\n NOT_FOUND: "not_found",\n STALE_REF: "stale_ref",\n INVALID_SELECTOR: "invalid_selector",\n SELECTOR_NOT_FOUND: "selector_not_found",\n SELECTOR_AMBIGUOUS: "selector_ambiguous",\n SELECTOR_NTH_OUT_OF_RANGE: "selector_nth_out_of_range",\n OPTION_NOT_FOUND: "option_not_found",\n NOT_A_SELECT: "not_a_select"\n };\n var IMPLICIT_ROLE = {\n a: "link",\n button: "button",\n select: "combobox",\n textarea: "textbox",\n h1: "heading",\n h2: "heading",\n h3: "heading",\n h4: "heading",\n h5: "heading",\n h6: "heading",\n img: "img",\n nav: "navigation",\n ul: "list",\n ol: "list",\n li: "listitem",\n main: "main",\n article: "article",\n aside: "complementary",\n header: "banner",\n footer: "contentinfo",\n form: "form",\n table: "table",\n summary: "button",\n option: "option",\n output: "status"\n };\n function roleOf(el) {\n const explicit = el.getAttribute("role");\n if (explicit) return explicit.split(/\\s+/)[0];\n const tag = el.tagName.toLowerCase();\n if (tag === "input") {\n const type = el.type || "text";\n if (type === "hidden") return null;\n if (type === "checkbox") return "checkbox";\n if (type === "radio") return "radio";\n if (type === "button" || type === "submit" || type === "reset") return "button";\n if (type === "range") return "slider";\n return "textbox";\n }\n if (tag === "progress") return "progressbar";\n if (tag === "meter") return "meter";\n return IMPLICIT_ROLE[tag] ?? null;\n }\n function accessibleName(el) {\n const aria = el.getAttribute("aria-label");\n if (aria) return aria.trim();\n const labelledby = el.getAttribute("aria-labelledby");\n if (labelledby) {\n const parts = labelledby.split(/\\s+/).map((id2) => document.getElementById(id2)?.textContent?.trim() || "");\n const joined = parts.filter(Boolean).join(" ");\n if (joined) return joined.slice(0, 120);\n }\n const id = el.getAttribute("id");\n if (id) {\n const label = document.querySelector(`label[for="${CSS.escape(id)}"]`);\n if (label?.textContent?.trim()) return label.textContent.trim().slice(0, 120);\n }\n if (el.tagName.toLowerCase() === "input") {\n const type = el.type;\n if (type === "button" || type === "submit" || type === "reset") {\n const v = el.value;\n if (v) return v;\n }\n }\n const alt = el.getAttribute("alt");\n if (alt) return alt;\n const title = el.getAttribute("title");\n if (title) return title;\n const placeholder = el.getAttribute("placeholder");\n if (placeholder) return placeholder;\n return (el.textContent || "").replace(/\\s+/g, " ").trim().slice(0, 120);\n }\n function isVisible(el) {\n if (!(el instanceof HTMLElement) && !(el instanceof SVGElement)) return false;\n const html = el;\n if (html.hasAttribute("hidden")) return false;\n if (html.closest("[hidden]")) return false;\n const style = window.getComputedStyle(html);\n if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;\n if (el instanceof HTMLElement && el.offsetParent === null) {\n const pos = style.position;\n if (pos !== "fixed" && pos !== "sticky") return false;\n }\n return true;\n }\n var INTERACTIVE_SELECTOR = "a[href], button, input, textarea, select, summary, [role], [contenteditable=\'\'], [contenteditable=\'true\'], [onclick], [tabindex]";\n function isInteractive(el) {\n return el.matches(INTERACTIVE_SELECTOR);\n }\n function testidSelector(testid) {\n return `[data-testid="${CSS.escape(testid)}"], [data-test-id="${CSS.escape(testid)}"], [data-test="${CSS.escape(testid)}"], [data-cy="${CSS.escape(testid)}"]`;\n }\n function resolveTargetRich(sel) {\n const targetDesc = sel.ref != null ? String(sel.ref) : sel.css ?? (sel.role || sel.name || sel.label || sel.testid || sel.text ? `${sel.role ? `role=${sel.role} ` : ""}${sel.name ? `name=${sel.name} ` : ""}${sel.label ? `label=${sel.label} ` : ""}${sel.testid ? `testid=${sel.testid} ` : ""}${sel.text ? `text=${sel.text}` : ""}`.trim() : "");\n if (sel.ref != null) {\n const store = getRefStore();\n const el = store.refs.get(sel.ref);\n if (el && document.contains(el) && isVisible(el)) {\n return { el, matches_n: 1, match_level: "exact", target: targetDesc };\n }\n if (el && !document.contains(el)) {\n const cssPath = store.cssPaths.get(sel.ref);\n if (cssPath) {\n const re = document.querySelector(cssPath);\n if (re && isVisible(re)) {\n store.refs.set(sel.ref, re);\n return { el: re, matches_n: 1, match_level: "reidentified", target: targetDesc };\n }\n }\n return { errorEnvelope: err(ERROR_CODES.STALE_REF, "Numeric ref is no longer in the DOM. Re-run state.") };\n }\n return { errorEnvelope: err(ERROR_CODES.NOT_FOUND, "Numeric ref is no longer in the DOM. Re-run state.") };\n }\n let candidates = [];\n if (sel.css) {\n try {\n candidates = Array.from(document.querySelectorAll(sel.css));\n } catch {\n return { errorEnvelope: err(ERROR_CODES.INVALID_SELECTOR, "CSS was rejected by querySelectorAll.", { selector: sel.css }) };\n }\n if (candidates.length === 0) {\n return { errorEnvelope: err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: sel.css, candidates: suggestCandidates() }) };\n }\n } else if (sel.testid) {\n try {\n candidates = Array.from(document.querySelectorAll(testidSelector(sel.testid)));\n } catch {\n return { errorEnvelope: err(ERROR_CODES.INVALID_SELECTOR, "Invalid testid.", { testid: sel.testid }) };\n }\n } else if (sel.role || sel.name || sel.label || sel.text) {\n const wantRole = sel.role ? sel.role.toLowerCase() : null;\n const wantName = sel.name ? sel.name.toLowerCase() : null;\n const wantText = sel.text ? sel.text.toLowerCase() : null;\n let labelTargets = [];\n if (sel.label) {\n const lower = sel.label.toLowerCase();\n labelTargets = Array.from(document.querySelectorAll("label")).filter((l) => (l.textContent || "").trim().toLowerCase().includes(lower)).map((l) => {\n const forId = l.getAttribute("for");\n if (forId) return document.getElementById(forId);\n return l.querySelector("input, select, textarea");\n }).filter((x) => !!x);\n }\n const all = Array.from(document.querySelectorAll("*"));\n candidates = all.filter((el) => {\n if (!isVisible(el)) return false;\n const role = roleOf(el);\n if (wantRole && (!role || role.toLowerCase() !== wantRole)) return false;\n if (wantName) {\n const name = accessibleName(el).toLowerCase();\n if (!name.includes(wantName)) return false;\n }\n if (wantText) {\n const t = (el.textContent || "").replace(/\\s+/g, " ").trim().toLowerCase();\n if (!t.includes(wantText)) return false;\n }\n return true;\n });\n if (labelTargets.length) {\n const set = new Set(labelTargets);\n candidates = labelTargets.concat(candidates.filter((c) => !set.has(c)));\n }\n if (candidates.length === 0) {\n return {\n errorEnvelope: err(\n ERROR_CODES.SELECTOR_NOT_FOUND,\n "Semantic locator matches 0 elements.",\n { role: sel.role, name: sel.name, label: sel.label, text: sel.text, candidates: suggestCandidates() }\n )\n };\n }\n } else {\n return { errorEnvelope: err("no_target", "Provide a numeric ref, CSS selector, or semantic locator (--role/--name/--label/--text/--testid).") };\n }\n const visible = candidates.filter(isVisible);\n const pool = visible.length ? visible : candidates;\n if (sel.nth != null) {\n if (sel.nth < 0 || sel.nth >= pool.length) {\n return { errorEnvelope: err(ERROR_CODES.SELECTOR_NTH_OUT_OF_RANGE, `--nth ${sel.nth} beyond match count ${pool.length}.`) };\n }\n const el = pool[sel.nth];\n return { el, matches_n: pool.length, match_level: "reidentified", target: targetDesc };\n }\n if (pool.length > 1) {\n return {\n errorEnvelope: err(ERROR_CODES.SELECTOR_AMBIGUOUS, `CSS matches ${pool.length} elements and no --nth given.`, {\n matches_n: pool.length,\n candidates: pool.slice(0, 5).map((el) => uniqueCssPath(el))\n })\n };\n }\n return { el: pool[0], matches_n: 1, match_level: "reidentified", target: targetDesc };\n }\n function suggestCandidates() {\n const store = getRefStore();\n const recent = Array.from(store.refs.entries()).slice(-5).map(([, el]) => uniqueCssPath(el));\n return recent;\n }\n function compoundOf(el) {\n const tag = el.tagName.toLowerCase();\n if (tag === "input") {\n const input = el;\n const type = input.type;\n if (["date", "time", "datetime-local", "month", "week"].includes(type)) {\n const formatMap = {\n date: "YYYY-MM-DD",\n time: "HH:mm",\n "datetime-local": "YYYY-MM-DDTHH:mm",\n month: "YYYY-MM",\n week: "YYYY-Www"\n };\n return {\n control: type,\n format: formatMap[type] ?? type,\n current: input.value || "",\n min: input.min || null,\n max: input.max || null\n };\n }\n if (type === "file") {\n const current = Array.from(input.files ?? []).map((f) => f.name);\n return { control: "file", multiple: input.multiple, current, accept: input.accept || null };\n }\n return null;\n }\n if (tag === "select") {\n const select = el;\n const options = Array.from(select.options).slice(0, 50).map((o) => ({\n label: (o.textContent || "").trim(),\n value: o.value,\n selected: o.selected\n }));\n return {\n control: "select",\n multiple: select.multiple,\n current: select.selectedOptions[0] ? (select.selectedOptions[0].textContent || "").trim() : "",\n options,\n options_total: select.options.length\n };\n }\n return null;\n }\n var SKIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "head", "base", "template", "path", "circle", "rect", "g", "defs", "polygon", "line"]);\n function describeElement(el, ref) {\n const tag = el.tagName.toLowerCase();\n const role = el.getAttribute("role") ?? null;\n const name = accessibleName(el);\n const parts = [];\n if (ref != null) parts.push(`[${ref}]`);\n parts.push(tag);\n if (role && role !== IMPLICIT_ROLE[tag]) parts.push(`role=${role}`);\n if (tag === "input") {\n const input = el;\n if (input.type && input.type !== "text") parts.push(`[${input.type}]`);\n if (input.disabled) parts.push("(disabled)");\n }\n if (name) parts.push(JSON.stringify(name.length > 60 ? name.slice(0, 57) + "\\u2026" : name));\n if (tag === "a") {\n const href = el.getAttribute("href");\n if (href) parts.push(`\\u2192 ${href.slice(0, 50)}`);\n }\n if (el.disabled) parts.push("(disabled)");\n return parts.join(" ");\n }\n function buildStateTree() {\n const lines = [];\n const compounds = {};\n const budget = { nodes: 0, chars: 0, truncated: false };\n const MAX_NODES = 400;\n const MAX_CHARS = 9e3;\n let hiddenInteractive = 0;\n function emit(depth, text) {\n const line = " ".repeat(Math.min(depth, 12)) + text;\n lines.push(line);\n budget.chars += line.length;\n }\n function walk(el, depth) {\n if (budget.nodes >= MAX_NODES || budget.chars >= MAX_CHARS) {\n budget.truncated = true;\n return;\n }\n const tag = el.tagName.toLowerCase();\n if (SKIP_TAGS.has(tag)) return;\n budget.nodes++;\n const visible = isVisible(el);\n const interactive = isInteractive(el);\n if (interactive) {\n if (visible) {\n const ref = allocRef(el);\n emit(depth, describeElement(el, ref));\n const compound = compoundOf(el);\n if (compound) compounds[String(ref)] = compound;\n } else {\n hiddenInteractive++;\n return;\n }\n } else if (visible) {\n if (/^h[1-6]$/.test(tag)) {\n const t = accessibleName(el);\n if (t) emit(depth, `${"#".repeat(Number(tag[1]))} ${t}`);\n } else if (tag === "p" || tag === "li" || tag === "td" || tag === "th" || tag === "figcaption" || tag === "blockquote") {\n const ownText = ownTextOf(el);\n if (ownText) emit(depth, ownText.slice(0, 100));\n } else if (tag === "img") {\n const alt = el.getAttribute("alt") || "";\n if (alt) emit(depth, `img ${JSON.stringify(alt.slice(0, 60))}`);\n }\n }\n if (depth < 14) {\n for (const child of Array.from(el.children)) {\n walk(child, depth + 1);\n if (budget.truncated) return;\n }\n }\n }\n function ownTextOf(el) {\n let text = "";\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE) text += node.textContent || "";\n }\n return text.replace(/\\s+/g, " ").trim();\n }\n const root = document.querySelector("main") || document.querySelector("#root") || document.body;\n emit(0, `# ${document.title}`);\n walk(root, 0);\n const scrollable = document.documentElement.scrollHeight > window.innerHeight + 200;\n if (scrollable) {\n emit(0, "\\u2195 page is scrollable \\u2014 run cp.scroll down to reveal more");\n }\n if (hiddenInteractive > 0) {\n emit(0, `\\u2139 ${hiddenInteractive} hidden interactive element(s) not tagged`);\n }\n if (budget.truncated) {\n emit(0, "\\u2026 (snapshot truncated by budget \\u2014 narrow with a selector or scroll)");\n }\n return {\n url: location.href,\n title: document.title,\n text: lines.join("\\n"),\n compounds,\n hints: { scroll: scrollable, hidden_interactive: hiddenInteractive },\n truncated: budget.truncated\n };\n }\n function runControl(command, args) {\n const a = args || {};\n function getVisibleText(el) {\n return (el.textContent || "").trim().replace(/\\s+/g, " ").slice(0, 120);\n }\n function getAttrs(el) {\n const attrs = {};\n for (const name of ["id", "class", "name", "type", "href", "src", "placeholder", "value", "role", "aria-label", "data-testid"]) {\n attrs[name] = el.getAttribute(name);\n }\n return attrs;\n }\n function sel() {\n return {\n ref: a.ref,\n css: a.css,\n nth: a.nth,\n role: a.role,\n name: a.name,\n label: a.label,\n text: a.text,\n testid: a.testid\n };\n }\n function findElements(findArgs) {\n const limit = findArgs.limit ?? 20;\n const textMax = findArgs["text-max"] ?? 120;\n let elements = [];\n if (findArgs.css) {\n try {\n elements = Array.from(document.querySelectorAll(findArgs.css));\n } catch {\n return err(ERROR_CODES.INVALID_SELECTOR, "CSS was rejected by querySelectorAll.", { selector: findArgs.css });\n }\n } else if (findArgs.testid) {\n try {\n elements = Array.from(document.querySelectorAll(testidSelector(findArgs.testid)));\n } catch {\n return err(ERROR_CODES.INVALID_SELECTOR, "Invalid testid.");\n }\n } else if (findArgs.role || findArgs.name || findArgs.label) {\n const r = resolveTargetRich({ role: findArgs.role, name: findArgs.name, label: findArgs.label, text: findArgs.text });\n if ("errorEnvelope" in r) return r.errorEnvelope;\n elements = document.querySelectorAll("*").length ? semanticCandidates(findArgs) : [];\n } else if (findArgs.text) {\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);\n const lower = findArgs.text.toLowerCase();\n const seen = /* @__PURE__ */ new Set();\n let node;\n while (node = walker.nextNode()) {\n if (node.textContent?.toLowerCase().includes(lower)) {\n const el = node.parentElement;\n if (el && !seen.has(el)) {\n seen.add(el);\n elements.push(el);\n if (elements.length >= limit) break;\n }\n }\n }\n } else {\n elements = Array.from(document.querySelectorAll(INTERACTIVE_SELECTOR));\n }\n const out = elements.slice(0, limit).map((el, i) => {\n const ref = allocRef(el);\n return {\n nth: i,\n ref,\n tag: el.tagName.toLowerCase(),\n role: roleOf(el),\n text: getVisibleText(el).slice(0, textMax),\n attrs: getAttrs(el),\n visible: isVisible(el),\n compound: compoundOf(el) ?? void 0\n };\n });\n return out;\n }\n function semanticCandidates(findArgs) {\n const wantRole = findArgs.role ? findArgs.role.toLowerCase() : null;\n const wantName = findArgs.name ? findArgs.name.toLowerCase() : null;\n const wantText = findArgs.text ? findArgs.text.toLowerCase() : null;\n const all = Array.from(document.querySelectorAll("*"));\n return all.filter((el) => {\n if (!isVisible(el)) return false;\n if (wantRole) {\n const role = roleOf(el);\n if (!role || role.toLowerCase() !== wantRole) return false;\n }\n if (wantName && !accessibleName(el).toLowerCase().includes(wantName)) return false;\n if (wantText && !(el.textContent || "").toLowerCase().includes(wantText)) return false;\n return true;\n });\n }\n function pageState() {\n return {\n url: location.href,\n title: document.title,\n width: window.innerWidth,\n height: window.innerHeight,\n devicePixelRatio: window.devicePixelRatio\n };\n }\n function goto(gotoArgs) {\n if (!gotoArgs.url) return { error: "url_required" };\n let sameUrl = false;\n try {\n sameUrl = new URL(gotoArgs.url, window.location.href).href === window.location.href;\n } catch {\n sameUrl = false;\n }\n if (sameUrl) {\n if (gotoArgs.reload === true) {\n window.location.reload();\n return { navigated: true, reloaded: true, url: gotoArgs.url };\n }\n return { navigated: false, reused: true, url: gotoArgs.url };\n }\n window.location.href = gotoArgs.url;\n return { navigated: true, url: gotoArgs.url };\n }\n function evalScript(evalArgs) {\n if (!evalArgs.script) return { error: "script_required" };\n try {\n const fn = new Function(evalArgs.script);\n const result = fn();\n return { result };\n } catch (e) {\n return { error: String(e) };\n }\n }\n function fillElement(fArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable) {\n const isContentEditable = el.isContentEditable;\n if (isContentEditable) {\n el.innerText = fArgs.text ?? "";\n } else {\n input.value = fArgs.text ?? "";\n }\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n const actual = isContentEditable ? el.innerText : input.value;\n return actual === (fArgs.text ?? "") ? { filled: true, verified: true, text: fArgs.text, actual, target, matches_n, match_level } : err("verify_failed", "Input value diverged after fill (controlled component?).", { actual });\n }\n return err("invalid_type", "fill only supports input, textarea, and contenteditable.");\n }\n function typeElement(tArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n el.focus({ preventScroll: true });\n input.value = tArgs.text ?? "";\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n let autocomplete = false;\n try {\n autocomplete = !!document.querySelector("[role=\'listbox\'], [role=\'combobox\'] [role=\'listbox\'], datalist, .ant-select-dropdown:not([style*=\'hidden\']), [id^=\'listbox\']");\n } catch {\n }\n return { typed: true, text: tArgs.text, target, matches_n, match_level, autocomplete };\n }\n function clickElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.scrollIntoView({ block: "center" });\n el.click();\n return { clicked: true, target, matches_n, match_level, tag: el.tagName.toLowerCase(), text: getVisibleText(el) };\n }\n function selectElement(sArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n if (el.tagName !== "SELECT") {\n return err(ERROR_CODES.NOT_A_SELECT, "select was called on a non-<select> element.", { tag: el.tagName.toLowerCase() });\n }\n const select = el;\n const want = sArgs.label ?? sArgs.value ?? sArgs.option;\n if (!want) return err("option_required", "Provide label or value.");\n const byLabel = Array.from(select.options).find((o) => (o.textContent || "").trim() === want);\n const byValue = Array.from(select.options).find((o) => o.value === want);\n const opt = byLabel ?? byValue;\n if (!opt) {\n const available = Array.from(select.options).slice(0, 50).map((o) => (o.textContent || "").trim());\n return err(ERROR_CODES.OPTION_NOT_FOUND, "Option not found.", { available });\n }\n select.value = opt.value;\n select.dispatchEvent(new Event("input", { bubbles: true }));\n select.dispatchEvent(new Event("change", { bubbles: true }));\n return { selected: true, value: select.value, target, matches_n, match_level };\n }\n function pressKeys(kArgs) {\n const keyCombo = kArgs.keys ?? kArgs.key ?? "";\n if (!keyCombo) return { error: "keys_required" };\n const comboParts = keyCombo.split("+").map((s) => s.trim());\n const main = comboParts[comboParts.length - 1];\n const ctrl = comboParts.includes("Control") || comboParts.includes("Ctrl") || comboParts.includes("Meta") || comboParts.includes("Cmd");\n const alt = comboParts.includes("Alt");\n const shift = comboParts.includes("Shift");\n const target = document.activeElement ?? document.body;\n const opts = { key: main, bubbles: true, cancelable: true, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: comboParts.includes("Meta") || comboParts.includes("Cmd") };\n target.dispatchEvent(new KeyboardEvent("keydown", opts));\n target.dispatchEvent(new KeyboardEvent("keypress", opts));\n target.dispatchEvent(new KeyboardEvent("keyup", opts));\n return { pressed: true, key: keyCombo };\n }\n function hoverElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const rect = el.getBoundingClientRect();\n const opts = { bubbles: true, cancelable: true, clientX: rect.x + rect.width / 2, clientY: rect.y + rect.height / 2 };\n el.dispatchEvent(new MouseEvent("mouseover", opts));\n el.dispatchEvent(new MouseEvent("mousemove", opts));\n return { hovered: true, target, matches_n, match_level };\n }\n function focusElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.focus({ preventScroll: true });\n return { focused: true, target, matches_n, match_level };\n }\n function dblclickElement() {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n el.dispatchEvent(new MouseEvent("dblclick", { bubbles: true, cancelable: true }));\n return { dblclicked: true, target, matches_n, match_level };\n }\n function setChecked(want) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n const isAria = input.type !== "checkbox" && input.type !== "radio" && el.getAttribute("aria-checked") != null;\n if (!isAria && input.type !== "checkbox" && input.type !== "radio") {\n return err("invalid_type", "Target is not a checkbox/radio/aria-checked control.", { tag: el.tagName.toLowerCase() });\n }\n if (isAria) {\n el.setAttribute("aria-checked", String(want));\n el.dispatchEvent(new Event("change", { bubbles: true }));\n return { [want ? "checked" : "unchecked"]: true, changed: true, target, matches_n, match_level, kind: "aria" };\n }\n if (input.type === "radio" && !want) {\n return err("invalid_type", "Radio buttons cannot be unchecked directly; select another radio in the group.");\n }\n const changed = input.checked !== want;\n input.checked = want;\n input.dispatchEvent(new Event("input", { bubbles: true }));\n input.dispatchEvent(new Event("change", { bubbles: true }));\n return { [want ? "checked" : "unchecked"]: true, changed, target, matches_n, match_level, kind: input.type };\n }\n function scrollElement(sArgs) {\n const dir = sArgs.direction ?? "down";\n const amount = sArgs.amount ?? 500;\n if (dir !== "up" && dir !== "down") return { error: "invalid_direction" };\n const dy = dir === "down" ? amount : -amount;\n window.scrollBy({ top: dy, behavior: "instant" });\n return {\n scrolled: true,\n direction: dir,\n amount,\n scrollY: Math.round(window.scrollY),\n scrollHeight: document.documentElement.scrollHeight\n };\n }\n function backNav() {\n history.back();\n return { back: true };\n }\n function htmlJsonTree(el, depth, maxDepth, childrenMax, textMax, truncated) {\n const children = Array.from(el.children);\n const ownText = (el.textContent || "").replace(/\\s+/g, " ").trim();\n const node = {\n tag: el.tagName.toLowerCase(),\n attrs: getAttrs(el),\n text: ownText.length > textMax ? (ownText.slice(0, textMax), truncated.text_truncated = (truncated.text_truncated || 0) + 1, ownText.slice(0, textMax)) : ownText,\n children: [],\n compound: compoundOf(el) ?? void 0\n };\n if (depth < maxDepth) {\n let kids = children;\n if (children.length > childrenMax) {\n truncated.children_dropped = (truncated.children_dropped || 0) + (children.length - childrenMax);\n kids = children.slice(0, childrenMax);\n }\n node.children = kids.map((c) => htmlJsonTree(c, depth + 1, maxDepth, childrenMax, textMax, truncated));\n } else if (children.length) {\n truncated.depth = (truncated.depth || 0) + 1;\n }\n return node;\n }\n function getElement(gArgs) {\n const what = gArgs.what ?? "text";\n if (what === "title") return { value: document.title, matches_n: 1, match_level: "exact" };\n if (what === "url") return { value: location.href, matches_n: 1, match_level: "exact" };\n if (what === "html") {\n if (gArgs["as"] === "json") {\n const root2 = gArgs.css ? document.querySelector(gArgs.css) : document.body;\n if (!root2) return err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: gArgs.css });\n const truncated = {};\n const tree = htmlJsonTree(root2, 0, gArgs.depth ?? 6, gArgs["children-max"] ?? 20, gArgs["text-max"] ?? 200, truncated);\n return {\n value: tree,\n truncated: Object.keys(truncated).length ? truncated : void 0\n };\n }\n const root = gArgs.css ? document.querySelector(gArgs.css) : document.body;\n if (!root) return err(ERROR_CODES.SELECTOR_NOT_FOUND, "CSS matches 0 elements.", { selector: gArgs.css });\n let html = root.outerHTML;\n if (gArgs.max && html.length > gArgs.max) html = html.slice(0, gArgs.max) + "\\u2026";\n return { value: html };\n }\n const r = resolveTargetRich(gArgs);\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level } = r;\n switch (what) {\n case "text": {\n const t = getVisibleText(el);\n return { value: t.length > 2e3 ? t.slice(0, 2e3) : t, matches_n, match_level };\n }\n case "value":\n return { value: el.value ?? el.innerText ?? null, matches_n, match_level };\n case "attributes":\n return { value: getAttrs(el), matches_n, match_level };\n default:\n return { error: `unknown_what: ${what}` };\n }\n }\n function extractMarkdown(eArgs) {\n const chunkSize = eArgs["chunk-size"] ?? 0;\n const start = eArgs.start ?? 0;\n const scope = eArgs.selector ? document.querySelector(eArgs.selector) : document.querySelector("main") || document.querySelector("article") || document.body;\n if (!scope) return { error: "scope_not_found" };\n const parts = [];\n const walker = document.createTreeWalker(scope, NodeFilter.SHOW_ELEMENT);\n let node;\n while (node = walker.nextNode()) {\n const el = node;\n const tag = el.tagName.toLowerCase();\n const text = getVisibleText(el);\n if (!text) continue;\n if (tag === "h1") parts.push(`# ${text}`);\n else if (tag === "h2") parts.push(`## ${text}`);\n else if (tag === "h3") parts.push(`### ${text}`);\n else if (tag === "p" || tag === "li") parts.push(text);\n else if (tag === "a") parts.push(`[${text}](${el.getAttribute("href") ?? ""})`);\n }\n const full = parts.join("\\n\\n");\n const sliced = chunkSize > 0 ? full.slice(start, start + chunkSize) : full.slice(start);\n return {\n url: location.href,\n title: document.title,\n selector: eArgs.selector ?? "auto",\n total_chars: full.length,\n chunk_size: chunkSize,\n start,\n end: start + sliced.length,\n next_start_char: start + sliced.length < full.length ? start + sliced.length : null,\n content: sliced\n };\n }\n function listFrames() {\n const frames = Array.from(document.querySelectorAll("iframe")).map((f, index) => {\n const src = f.getAttribute("src") ?? "";\n let crossOrigin = false;\n try {\n if (src && /^https?:/.test(src)) crossOrigin = new URL(src, location.href).origin !== location.origin;\n } catch {\n }\n return { index, url: src, name: f.getAttribute("name") ?? "", cross_origin: crossOrigin };\n });\n return { frames };\n }\n function screenshotMeta() {\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n url: location.href,\n title: document.title\n };\n }\n function startTitleMarquee(tArgs) {\n const g = globalThis;\n if (g.__cpMarquee) {\n clearInterval(g.__cpMarquee.interval);\n } else {\n g.__cpMarquee = { original: document.title, interval: 0 };\n }\n const prefix = tArgs.text ?? "\\u{1F916}";\n const base = g.__cpMarquee.original;\n const full = `${prefix} ${base}`;\n const speed = Math.max(100, Math.min(2e3, tArgs.speed ?? 300));\n let pos = 0;\n const tick = () => {\n const display = full.slice(pos) + " " + full.slice(0, pos);\n document.title = display.slice(0, 60);\n pos = (pos + 1) % (full.length + 1 || 1);\n };\n tick();\n g.__cpMarquee.interval = window.setInterval(tick, speed);\n return { started: true, original: base, prefix, speed };\n }\n function stopTitleMarquee() {\n const g = globalThis;\n const state = g.__cpMarquee;\n if (!state) return { restored: false, reason: "no_active_marquee" };\n clearInterval(state.interval);\n document.title = state.original;\n delete g.__cpMarquee;\n return { restored: true, original: state.original };\n }\n function networkEntriesLegacy(nArgs) {\n const all = performance.getEntriesByType("resource").map((e) => ({\n url: e.name,\n type: e.initiatorType ?? "unknown",\n duration: Math.round(e.duration)\n }));\n let entries = all;\n if (nArgs.filter) {\n const keywords = nArgs.filter.split(",").map((s) => s.trim().toLowerCase());\n entries = all.filter((e) => keywords.some((kw) => e.url.toLowerCase().includes(kw)));\n }\n return { entries, count: entries.length, source: "performance-api" };\n }\n async function waitForCondition(wArgs) {\n const timeout = wArgs.timeout ?? 1e4;\n if (wArgs.time != null) {\n await new Promise((res) => setTimeout(res, Math.min(wArgs.time * 1e3, 3e4)));\n return { waited: true, kind: "time", seconds: wArgs.time };\n }\n const deadline = Date.now() + timeout;\n const pollInterval = 200;\n if (wArgs.selector) {\n while (Date.now() < deadline) {\n if (document.querySelector(wArgs.selector)) return { waited: true, kind: "selector", selector: wArgs.selector, elapsedMs: timeout - (deadline - Date.now()) };\n await new Promise((res) => setTimeout(res, pollInterval));\n }\n return err("wait_timeout", `Selector not found within ${timeout}ms.`, { selector: wArgs.selector });\n }\n if (wArgs.text) {\n const lower = wArgs.text.toLowerCase();\n while (Date.now() < deadline) {\n if ((document.body.textContent || "").toLowerCase().includes(lower)) {\n return { waited: true, kind: "text", text: wArgs.text, elapsedMs: timeout - (deadline - Date.now()) };\n }\n await new Promise((res) => setTimeout(res, pollInterval));\n }\n return err("wait_timeout", `Text not found within ${timeout}ms.`, { text: wArgs.text });\n }\n return { error: "no_condition" };\n }\n function uploadFiles(uArgs) {\n const r = resolveTargetRich(sel());\n if ("errorEnvelope" in r) return r.errorEnvelope;\n const { el, matches_n, match_level, target } = r;\n const input = el;\n if (input.type !== "file") return err("invalid_type", "Target is not an input[type=file].", { tag: el.tagName.toLowerCase() });\n try {\n const dt = new DataTransfer();\n for (const name of uArgs.files ?? []) {\n dt.items.add(new File([""], name.split("/").pop() || name));\n }\n input.files = dt.files;\n } catch {\n }\n input.dispatchEvent(new Event("change", { bubbles: true }));\n return {\n uploaded: true,\n files: uArgs.files ?? [],\n file_names: (uArgs.files ?? []).map((f) => f.split("/").pop()),\n target,\n matches_n,\n match_level,\n multiple: input.multiple,\n accept: input.accept || null\n };\n }\n function dragElements(dArgs) {\n const r1 = resolveTargetRich(sel());\n if ("errorEnvelope" in r1) return r1.errorEnvelope;\n const r2 = resolveTargetRich({ ref: dArgs.ref2, css: dArgs.css2 });\n if ("errorEnvelope" in r2) return r2.errorEnvelope;\n const src = r1.el;\n const tgt = r2.el;\n const make = (type) => {\n const r1rect = src.getBoundingClientRect();\n return new DragEvent(type, { bubbles: true, cancelable: true, clientX: r1rect.x + r1rect.width / 2, clientY: r1rect.y + r1rect.height / 2 });\n };\n src.dispatchEvent(make("dragstart"));\n tgt.dispatchEvent(new DragEvent("dragenter", { bubbles: true }));\n tgt.dispatchEvent(new DragEvent("dragover", { bubbles: true, cancelable: true }));\n tgt.dispatchEvent(new DragEvent("drop", { bubbles: true, cancelable: true }));\n src.dispatchEvent(new DragEvent("dragend", { bubbles: true }));\n return { dragged: true, source: String(sel().ref ?? sel().css ?? ""), target: String(dArgs.ref2 ?? dArgs.css2 ?? ""), source_matches_n: r1.matches_n, target_matches_n: r2.matches_n };\n }\n switch (command) {\n case "cp.state":\n return a.tree === false ? pageState() : buildStateTree();\n case "cp.snapshot":\n return buildStateTree();\n case "cp.find":\n return findElements({\n css: a.css,\n text: a.text,\n limit: a.limit,\n role: a.role,\n name: a.name,\n label: a.label,\n testid: a.testid,\n "text-max": a["text-max"]\n });\n case "cp.click":\n return clickElement();\n case "cp.type":\n return typeElement({ text: a.text });\n case "cp.goto":\n return goto({ url: a.url });\n case "cp.eval":\n return evalScript({ script: a.script });\n case "cp.fill":\n return fillElement({ text: a.text });\n case "cp.select":\n return selectElement({ label: a.label, value: a.value, option: a.option });\n case "cp.keys":\n return pressKeys({ keys: a.keys, key: a.key });\n case "cp.hover":\n return hoverElement();\n case "cp.focus":\n return focusElement();\n case "cp.dblclick":\n return dblclickElement();\n case "cp.check":\n return setChecked(true);\n case "cp.uncheck":\n return setChecked(false);\n case "cp.scroll":\n return scrollElement({ direction: a.direction, amount: a.amount });\n case "cp.back":\n return backNav();\n case "cp.get":\n return getElement({\n what: a.what,\n css: a.css,\n ref: a.ref,\n nth: a.nth,\n "as": a["as"],\n depth: a.depth,\n "children-max": a["children-max"],\n "text-max": a["text-max"],\n max: a.max,\n role: a.role,\n name: a.name,\n label: a.label,\n text: a.text,\n testid: a.testid\n });\n case "cp.extract":\n return extractMarkdown({\n "chunk-size": a["chunk-size"],\n start: a.start,\n selector: a.selector\n });\n case "cp.frames":\n return listFrames();\n case "cp.screenshot":\n return screenshotMeta();\n case "cp.network":\n return networkEntriesLegacy({ filter: a.filter });\n case "cp.wait":\n return waitForCondition({\n selector: a.selector,\n text: a.text,\n time: a.time,\n timeout: a.timeout\n });\n case "cp.upload":\n return uploadFiles({ files: a.files });\n case "cp.drag":\n return dragElements({ ref2: a.ref2, css2: a.css2 });\n case "cp.title.marquee":\n return startTitleMarquee({ text: a.text, speed: a.speed });\n case "cp.title.restore":\n return stopTitleMarquee();\n default:\n return { error: `unknown_command: ${command}` };\n }\n }\n var RUNTIME_VERSION = "0.3.0-opencli-parity";\n globalThis.__cpRunControl = runControl;\n globalThis.__cpRuntimeVersion = RUNTIME_VERSION;\n})();\n';
990
1003
 
991
1004
  // src/background/cdp.ts
992
1005
  var attached = /* @__PURE__ */ new Set();
@@ -1354,6 +1367,12 @@ async function detachIfAttached(tabId) {
1354
1367
  attached.delete(tabId);
1355
1368
  networkEnabledTabs.delete(tabId);
1356
1369
  }
1370
+ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
1371
+ if (changeInfo.status !== "loading") return;
1372
+ attached.delete(tabId);
1373
+ networkEnabledTabs.delete(tabId);
1374
+ chrome.debugger.detach({ tabId }).catch(() => void 0);
1375
+ });
1357
1376
  var CDP_ALLOWLIST = /* @__PURE__ */ new Set([
1358
1377
  "Accessibility.enable",
1359
1378
  "Accessibility.getFullAXTree",
@@ -2437,6 +2456,19 @@ async function runLinkedInSearch(args, log, ctx) {
2437
2456
  await closeTabGracefully(tab.id);
2438
2457
  }
2439
2458
  }
2459
+ let diagnostics = null;
2460
+ if (leads.size === 0 && tab.id != null) {
2461
+ const t = await chrome.tabs.get(tab.id).catch(() => null);
2462
+ const signal = t && t.id != null ? await cdpEvaluate(t.id, LIEPIN_WALL_CHECK_SOURCE).catch(() => null) : null;
2463
+ const bodySnippet = t && t.id != null && (!t.url || t.url !== "about:blank") ? await cdpEvaluate(
2464
+ t.id,
2465
+ '(() => { const b = document.body && document.body.innerText; return b ? b.replace(/\\s+/g, " ").trim().slice(0, 300) : null; })()'
2466
+ ).catch(() => null) : null;
2467
+ diagnostics = { url: t?.url ?? null, title: t?.title ?? null, signal, bodySnippet };
2468
+ if (signal) {
2469
+ log("warn", "hm.liepin.search 0 leads with risk-control signal", { signal, url: diagnostics.url });
2470
+ }
2471
+ }
2440
2472
  return {
2441
2473
  keywords: effectiveKeywords,
2442
2474
  searchUrl: u.toString(),
@@ -2444,7 +2476,10 @@ async function runLinkedInSearch(args, log, ctx) {
2444
2476
  leadCount: leads.size,
2445
2477
  leads: Array.from(leads.values()),
2446
2478
  keepTab,
2447
- tabId: keepTab ? tab.id ?? null : null
2479
+ tabId: keepTab ? tab.id ?? null : null,
2480
+ // Issue #24 P0:0 leads 不等于"没搜到",可能是登录墙/反爬页根本没渲染结果。
2481
+ // 带出诊断字段,由调用方决定 degraded;真实 0 结果 diagnostics.signal 为 null。
2482
+ ...leads.size === 0 && diagnostics ? { diagnostics, degraded: diagnostics.signal !== null, degradedReason: diagnostics.signal ?? void 0 } : {}
2448
2483
  };
2449
2484
  }
2450
2485
  var LIEPIN_WALL_CHECK_SOURCE = String.raw`(() => {
@@ -2723,7 +2758,15 @@ var LIEPIN_FILL_AND_SEARCH_SOURCE = String.raw`(async (P) => {
2723
2758
  const t = clean(el.textContent);
2724
2759
  return t && (t === text || t.replace(/\s+/g, '') === String(text).replace(/\s+/g, ''));
2725
2760
  });
2726
- if (!tag) { errors.push('missing_tag:' + text + '@' + containerSel); return false; }
2761
+ if (!tag) {
2762
+ const available = Array.from(container.querySelectorAll('label.tag-item, .tag-item'))
2763
+ .map((el) => clean(el.textContent))
2764
+ .filter(Boolean)
2765
+ .slice(0, 20);
2766
+ errors.push('missing_tag:' + text + '@' + containerSel
2767
+ + (available.length ? '; available=[' + available.join(', ') + ']' : ''));
2768
+ return false;
2769
+ }
2727
2770
  tag.click();
2728
2771
  return true;
2729
2772
  };
@@ -2883,8 +2926,18 @@ async function runLiepinSearch(args, log, ctx) {
2883
2926
  if (args[k] !== void 0) fillParams[k] = args[k];
2884
2927
  }
2885
2928
  const searchUrl = "https://h.liepin.com/search/getConditionItem";
2886
- const tab = await chrome.tabs.create({ url: searchUrl, active: false });
2887
- log("info", "hm.liepin.search tab created", { tabId: tab.id, url: searchUrl, maxPages, keyword });
2929
+ const existing = await chrome.tabs.query({ url: "https://h.liepin.com/search/getConditionItem*" }).catch(() => []);
2930
+ const reusable = existing.find((t) => !!t.id && !!t.url && t.url.startsWith("https://h.liepin.com/search/"));
2931
+ let tab;
2932
+ let reusedExisting = false;
2933
+ if (reusable && reusable.id != null) {
2934
+ tab = reusable;
2935
+ reusedExisting = true;
2936
+ log("info", "hm.liepin.search reusing existing search tab", { tabId: tab.id, url: tab.url });
2937
+ } else {
2938
+ tab = await chrome.tabs.create({ url: searchUrl, active: false });
2939
+ log("info", "hm.liepin.search tab created", { tabId: tab.id, url: searchUrl, maxPages, keyword });
2940
+ }
2888
2941
  if (ctx.sessionId && !ctx.explicitTab && tab.id) {
2889
2942
  addTabToSessionGroup(ctx.sessionId, ctx.groupTitle, tab).catch(
2890
2943
  (err3) => log("warn", "hm.liepin.search addTabToSessionGroup failed", { error: err3.message })
@@ -2954,7 +3007,7 @@ async function runLiepinSearch(args, log, ctx) {
2954
3007
  await assertNotBlocked(`page-${page}-next`);
2955
3008
  }
2956
3009
  } finally {
2957
- if (!keepTab && tab.id != null) {
3010
+ if (!keepTab && !reusedExisting && tab.id != null) {
2958
3011
  await closeTabGracefully(tab.id);
2959
3012
  }
2960
3013
  }
@@ -2967,7 +3020,9 @@ async function runLiepinSearch(args, log, ctx) {
2967
3020
  applied,
2968
3021
  fillErrors,
2969
3022
  keepTab,
2970
- tabId: keepTab ? tab.id ?? null : null
3023
+ reusedExisting,
3024
+ // tabId 如实记录:自建 tab 关闭后置 null;复用 tab 一定返回其 id(调用方可追踪/不再开)
3025
+ tabId: reusedExisting ? tab.id ?? null : keepTab ? tab.id ?? null : null
2971
3026
  };
2972
3027
  }
2973
3028
  var HIREDCHINA_EXTRACT_SOURCE = String.raw`(async () => {
@@ -3222,6 +3277,23 @@ async function handleTabCommand(command, args, log) {
3222
3277
  log("info", "cp.tab.new created", { page: String(tab.id), url: tab.url ?? url, active });
3223
3278
  return { page: String(tab.id), url: tab.url ?? url };
3224
3279
  }
3280
+ case "cp.tab.open": {
3281
+ const url = typeof args.url === "string" && args.url.trim() ? args.url.trim() : "";
3282
+ if (!url) throw new Error("tab_open_requires_url");
3283
+ if (!/^https?:\/\//.test(url)) throw new Error("tab_open_requires_http_url: " + url);
3284
+ const active = args.active === true;
3285
+ const pattern = url.includes("*") ? url : url + "*";
3286
+ const candidates = await chrome.tabs.query({ url: pattern }).catch(() => []);
3287
+ const hit = candidates.find((t) => !!t.id && !!t.url && t.url.startsWith("http"));
3288
+ if (hit && hit.id != null) {
3289
+ await chrome.tabs.update(hit.id, { active }).catch(() => void 0);
3290
+ log("info", "cp.tab.open reused existing tab", { page: String(hit.id), url: hit.url, active });
3291
+ return { page: String(hit.id), url: hit.url, reused: true };
3292
+ }
3293
+ const tab = await chrome.tabs.create({ url, active });
3294
+ log("info", "cp.tab.open created tab", { page: String(tab.id), url: tab.url ?? url, active });
3295
+ return { page: String(tab.id), url: tab.url ?? url, reused: false };
3296
+ }
3225
3297
  case "cp.tab.select": {
3226
3298
  if (!tabArg) throw new Error("tab_select_requires_tab_id");
3227
3299
  const tabId = Number(tabArg);
@@ -4012,6 +4084,14 @@ chrome.tabs.onUpdated.addListener((_tabId, changeInfo, tab) => {
4012
4084
  }
4013
4085
  if (changeInfo.url) {
4014
4086
  broadcastState();
4087
+ if (state.wsConnected) {
4088
+ sendToOffscreen(JSON.stringify({ type: "tab_event", event: "navigated", tabId: _tabId, url: changeInfo.url, ts: Date.now() }));
4089
+ }
4090
+ }
4091
+ });
4092
+ chrome.tabs.onCreated.addListener((t) => {
4093
+ if (state.wsConnected) {
4094
+ sendToOffscreen(JSON.stringify({ type: "tab_event", event: "created", tabId: t.id, url: t.url ?? null, openerTabId: t.openerTabId ?? null, windowId: t.windowId, index: t.index, ts: Date.now() }));
4015
4095
  }
4016
4096
  });
4017
4097
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
@@ -1 +1 @@
1
- {"buildTime":"2026-09-10T14:44:14.525Z","version":"0.1.8"}
1
+ {"buildTime":"2026-09-11T02:05:00.320Z","version":"0.1.11"}
@@ -13027,7 +13027,7 @@
13027
13027
  var { takeFullSnapshot } = record;
13028
13028
 
13029
13029
  // src/shared/messaging.ts
13030
- var SERVER_PORT = 34570;
13030
+ var SERVER_PORT = 34571;
13031
13031
  var WS_URL = `ws://localhost:${SERVER_PORT}/ws`;
13032
13032
  var SERVER_HTTP_URL = `http://localhost:${SERVER_PORT}`;
13033
13033
  function sendToBackground(message) {
@@ -3,7 +3,7 @@
3
3
  "minimum_chrome_version": "116",
4
4
  "name": "__MSG_extName__",
5
5
  "description": "__MSG_extDescription__",
6
- "version": "0.1.8",
6
+ "version": "0.1.11",
7
7
  "default_locale": "zh_CN",
8
8
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi/S88toqsiW6zleTj+V8jPUVTmrJiyDPvqTd+Cqrl/Ax9PCxaMsiXXUh2sNJh6sMj7xj0YBclCPMsI2wxIF8y4r6KnmWt49RuMg7EynvOT8fcRjxXBWGAz6hjGepWesOCMn9dgJ5IqgXP3j+Bfb+636IeW1nZxOedT25zJwJr81G4gIyc9kk+wCZPHUhQTsfNVxd8xfbxYQpcDuuVk1dDNEszeLUZGKYl9JcOQcaEQOWA4ED5avn67ZWQx5kzowB7XYtcTH0vz63cKNcK3O1iQ0hmFoohPKRWh9tNl545UQ2+mPaU6B/CNSZP2qwb+tgK7lVh5zG5qQkbaP8ZP8XaQIDAQAB",
9
9
  "permissions": [
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  (() => {
3
3
  // src/shared/messaging.ts
4
- var SERVER_PORT = 34570;
4
+ var SERVER_PORT = 34571;
5
5
  var WS_URL = `ws://localhost:${SERVER_PORT}/ws`;
6
6
  var SERVER_HTTP_URL = `http://localhost:${SERVER_PORT}`;
7
7
 
@@ -1,2 +1,2 @@
1
- *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.visible{visibility:visible}.fixed{position:fixed}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.ml-auto{margin-left:auto}.mr-1\.5{margin-right:.375rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-screen{height:100vh}.w-2{width:.5rem}.w-2\.5{width:.625rem}.min-w-0{min-width:0}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-full{border-radius:9999px}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.border-current{border-color:currentColor}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-orange-200{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-r-transparent{border-right-color:transparent}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.p-0{padding:0}.p-2\.5{padding:.625rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-px{padding-top:1px;padding-bottom:1px}.pt-1{padding-top:.25rem}.text-center{text-align:center}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.font-bold{font-weight:700}.font-semibold{font-weight:600}.leading-none{line-height:1}.leading-snug{line-height:1.375}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-orange-800{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity,1))}.text-purple-800{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:border-blue-300:hover{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.hover\:border-purple-300:hover{--tw-border-opacity:1;border-color:rgb(216 180 254/var(--tw-border-opacity,1))}.hover\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-purple-50:hover{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity,1))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\:text-purple-600:hover{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.focus\:border-blue-400:focus{--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-300:disabled{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.disabled\:text-gray-500:disabled{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.visible{visibility:visible}.fixed{position:fixed}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.ml-auto{margin-left:auto}.mr-1\.5{margin-right:.375rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-screen{height:100vh}.w-2{width:.5rem}.w-2\.5{width:.625rem}.min-w-0{min-width:0}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-full{border-radius:9999px}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.border-current{border-color:currentColor}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-orange-200{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-r-transparent{border-right-color:transparent}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.p-0{padding:0}.p-2\.5{padding:.625rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-px{padding-top:1px;padding-bottom:1px}.pt-1{padding-top:.25rem}.text-center{text-align:center}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.font-bold{font-weight:700}.font-semibold{font-weight:600}.leading-none{line-height:1}.leading-snug{line-height:1.375}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-orange-800{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity,1))}.text-purple-800{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hover\:border-blue-300:hover{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.hover\:border-purple-300:hover{--tw-border-opacity:1;border-color:rgb(216 180 254/var(--tw-border-opacity,1))}.hover\:bg-blue-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-purple-50:hover{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity,1))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\:text-purple-600:hover{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.focus\:border-blue-400:focus{--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-300:disabled{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.disabled\:text-gray-500:disabled{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}
2
2
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/