{"version":3,"file":"EntityMenu-JGr2lqRz.cjs","names":[],"sources":["../src/atoms/Chip/entity.ts","../src/atoms/Chip/picker.ts","../src/atoms/Chip/Chip.tsx","../src/molecules/EntityMenu/EntityMenu.tsx"],"sourcesContent":["/**\n * Entity vocabulary shared by `Chip purpose=\"entity\"`, `EntityMenu` and\n * `AnalystMarkdown`.\n *\n * The kind list is provisional: it covers what the product surfaces today, but\n * the canonical set belongs to the entity-resolution service and should be\n * agreed with its owners before anything depends on exhaustiveness. It is a\n * closed union rather than a string so that the defang rules below, and the\n * accessible-name labels, cannot silently miss a kind.\n */\n\nexport type EntityKind =\n  // Network indicators\n  | 'ip'\n  | 'domain'\n  | 'url'\n  | 'email'\n  // File and execution indicators\n  | 'hash'\n  | 'file'\n  | 'process'\n  // Principals\n  | 'host'\n  | 'user'\n  | 'account'\n  // Catalogue references\n  | 'cve'\n  | 'technique'\n  | 'rule';\n\n/** Intelligence verdict. Uses the status palette, never the severity scale. */\nexport type EntityVerdict = 'malicious' | 'suspicious' | 'benign' | 'unknown';\n\n/**\n * Spoken form of each kind. This is what prefixes an entity's accessible name,\n * so a screen-reader user hears \"IP address 203.0.113.47\" rather than a bare\n * string of digits with no indication of what it is.\n */\nexport const ENTITY_KIND_LABEL: Record<EntityKind, string> = {\n  ip: 'IP address',\n  domain: 'Domain',\n  url: 'URL',\n  email: 'Email address',\n  hash: 'File hash',\n  file: 'File',\n  process: 'Process',\n  host: 'Host',\n  user: 'User',\n  account: 'Account',\n  cve: 'CVE',\n  technique: 'Technique',\n  rule: 'Rule',\n};\n\n/**\n * Kinds for which a defanged copy is meaningful. Defanging a hash or a username\n * produces a string that is merely wrong, so the action is hidden rather than\n * offered and ignored.\n */\nconst DEFANGABLE: ReadonlySet<EntityKind> = new Set<EntityKind>(['ip', 'domain', 'url', 'email']);\n\nexport function isDefangable(kind: EntityKind): boolean {\n  return DEFANGABLE.has(kind);\n}\n\n/**\n * Neutralise an indicator so it can be pasted into a ticket, a chat message or\n * an email without being turned into a live link, unfurled by a preview bot, or\n * rewritten by a security proxy.\n *\n * Follows the convention used by CyberChef and MISP: every dot is bracketed,\n * `http` becomes `hxxp`, and `@` is bracketed. Bracketing every dot rather than\n * only the last is what makes the result unambiguous to the analyst refanging\n * it at the other end.\n *\n * @example defangIndicator('203.0.113.47')     // '203[.]0[.]113[.]47'\n * @example defangIndicator('https://evil.com') // 'hxxps[://]evil[.]com'\n */\nexport function defangIndicator(value: string): string {\n  return value\n    .replace(/https?:\\/\\//gi, (scheme) =>\n      scheme.replace(/^http/i, 'hxxp').replace('://', '[://]'),\n    )\n    .replace(/\\./g, '[.]')\n    .replace(/@/g, '[@]');\n}\n","/**\n * Label derivation for `Chip purpose=\"picker\"`.\n *\n * Kept out of the component so it can be tested in the node-only unit project,\n * and because the visible text and the accessible name have to be derived\n * together. They diverge on purpose -- the chip abbreviates to stay narrow in a\n * filter bar, while the accessible name spells the selection out -- and a\n * reader of one has to be able to see the other beside it, or they drift.\n */\n\n/**\n * How the trigger summarises its selection.\n *\n * `value` names the first selection and counts the rest, which is what a user\n * wants for a short closed vocabulary: `Severity: Critical +1` still says which\n * severity. `count` names none of them, for values too long to sit in a chip --\n * a datastore index called `classifyout-saviynt_subscriber-saviynt-v4` would\n * push the chip past the width of the bar and tell the user nothing more than\n * `Datasources: 3` does.\n */\nexport type PickerSummary = 'value' | 'count';\n\nexport interface PickerLabel {\n  /**\n   * The field's name. Always present, and rendered in the muted ink: it is the\n   * constant, so it should not compete with the part that changes.\n   */\n  field: string;\n  /**\n   * The selection as shown after the colon -- a value under `summary: 'value'`,\n   * a number under `'count'`. Undefined when nothing is selected, which is what\n   * suppresses the colon rather than leaving `Severity:` trailing into nothing.\n   */\n  detail?: string;\n  /** What the counter shows, or 0 when no counter is needed. */\n  overflow: number;\n  /** The full selection, spoken. Always names what the visible text elides. */\n  accessibleName: string;\n}\n\n/**\n * How many selections the accessible name enumerates before it starts counting.\n *\n * Reading forty datastore names is worse than useless -- it buries the one fact\n * the user asked for, which is how many are on. Five is enough to confirm a\n * short selection and short enough not to become a recital.\n */\nconst SPOKEN_LIMIT = 5;\n\nfunction spoken(values: string[]): string {\n  if (values.length <= SPOKEN_LIMIT) return values.join(', ');\n  const rest = values.length - SPOKEN_LIMIT;\n  return `${values.slice(0, SPOKEN_LIMIT).join(', ')} and ${rest} more`;\n}\n\n/**\n * Derives the visible text, the counter and the accessible name for a picker\n * chip.\n *\n * @param field  The filter's name, e.g. `Severity`. Always visible, so an empty\n *               picker still says what it filters.\n * @param values The selection, already resolved to the labels a user reads --\n *               not raw option values. A chip showing `sev_1` has failed.\n */\nexport function pickerLabel(\n  field: string,\n  values: string[],\n  summary: PickerSummary = 'value',\n): PickerLabel {\n  if (values.length === 0) {\n    // No colon and no number: an unset picker reads as the name of the thing it\n    // filters, which is also what makes it obvious there is nothing to clear.\n    return { field, overflow: 0, accessibleName: `${field}, none selected` };\n  }\n\n  const accessibleName = `${field}, ${values.length} selected: ${spoken(values)}`;\n\n  if (summary === 'count') {\n    return { field, detail: String(values.length), overflow: 0, accessibleName };\n  }\n\n  return {\n    field,\n    detail: values[0],\n    // The counter is what the first value is hiding, so it counts the others\n    // rather than the whole selection. `Critical +1` means two are on; showing\n    // `Critical +2` for two would read as three.\n    overflow: values.length - 1,\n    accessibleName,\n  };\n}\n","/**\n * Chip\n * Classification: mui-overrides (adds `subtle` + `dot` variants, extended color axis)\n * MUI base: https://mui.com/material-ui/react-chip/\n *\n * The single compact-label primitive. Callers pick a `purpose` from meaning and\n * the component derives the appearance, the rendered semantics and the keyboard\n * contract from it:\n *\n *   static      severity | status | category | metric | lifecycle\n *   interactive filter   | select | entity   | overflow | picker\n *\n * The older `variant` + `color` API still works and is unchanged, but it is\n * deprecated: four appearances crossed with ten colors is forty combinations of\n * which roughly eight are correct usage, and nothing in the types said which.\n * `purpose` collapses that to eight legal states.\n *\n * The prop is `purpose` rather than `role` because `role` is a DOM/ARIA\n * attribute already present on `ChipProps` via `HTMLAttributes`; reusing the\n * name would collide with it in the type system and read ambiguously next to a\n * genuine `role=\"button\"`.\n */\nimport MuiChip, { type ChipProps as MuiChipProps } from '@mui/material/Chip';\nimport Box from '@mui/material/Box';\nimport { darken, type Theme } from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\nimport { type SeverityKey } from '@/tokens/colors';\nimport { compactGlyphScale, fontFamily } from '@/tokens/typography';\nimport { ENTITY_KIND_LABEL, type EntityKind, type EntityVerdict } from './entity';\nimport { pickerLabel, type PickerSummary } from './picker';\n\n/** What the chip means. Chosen from intent, never from appearance. */\nexport type ChipPurpose =\n  | 'severity'\n  | 'status'\n  | 'category'\n  | 'metric'\n  | 'lifecycle'\n  | 'filter'\n  | 'select'\n  | 'entity'\n  | 'overflow'\n  | 'picker';\n\n/** Chip size scale. `small` is dense; `medium` matches Button `small`. */\nexport type ChipSize = 'small' | 'medium' | 'large' | 'extraLarge';\n\n/**\n * How mature a *feature* is. Deliberately not `status`: a workflow state\n * describes a record and the user may act on it, whereas a stage describes the\n * product surface itself and changes only when engineering ships something.\n * BigCommerce and GitHub both keep these apart for the same reason.\n *\n * Closed on purpose. A free-text version of this becomes \"Coming soon\",\n * \"COMING SOON\" and \"Coming Soon!\" in three screens.\n */\nexport type LifecycleStage = 'new' | 'beta' | 'alpha' | 'preview' | 'deprecated';\n\n/**\n * Semantic tone for a workflow or health state. Deliberately separate from the\n * severity scale: an alert's severity and its workflow state are different\n * dimensions and must not share a color vocabulary. Matches the `tone` values\n * on `TableStatusCell` so a status reads the same in and out of a table.\n */\nexport type ChipStatusTone = 'active' | 'success' | 'warning' | 'error' | 'info' | 'neutral';\n\n/** Intelligence verdict on an entity. Uses the status palette, not severity. */\nexport type ChipEntityVerdict = EntityVerdict;\n\n/**\n * Props every purpose accepts. `variant`, `color`, `label`, `onClick`,\n * `onDelete`, `clickable` and `deleteIcon` are withheld on purpose: appearance\n * and interaction are derived from `purpose` and must not be overridden, which\n * is what keeps a static label from being styled to look interactive.\n *\n * `onToggle` is dropped for a different reason: `HTMLAttributes` declares it as\n * the DOM toggle event for `<details>`, so leaving it in place would intersect\n * with the select purpose's own `onToggle` and make it uncallable.\n *\n * `size` stays available here because seven of the eight purposes honour it;\n * `entity` withholds it separately, for the reason given on `EntityChipProps`.\n */\ntype ChipPurposeBase = Omit<\n  MuiChipProps,\n  'variant' | 'color' | 'label' | 'onClick' | 'onDelete' | 'clickable' | 'deleteIcon' | 'onToggle'\n>;\n\n/** Severity or priority. Static. The label is derived from `level`. */\nexport type SeverityChipProps = ChipPurposeBase & {\n  purpose: 'severity';\n  level: SeverityKey;\n  /** Overrides the canonical word. Only for a genuinely different vocabulary. */\n  label?: string;\n};\n\n/** Workflow or health state. Static. */\nexport type StatusChipProps = ChipPurposeBase & {\n  purpose: 'status';\n  tone: ChipStatusTone;\n  label: string;\n};\n\n/** Category or classification metadata. Static, always neutral. */\nexport type CategoryChipProps = ChipPurposeBase & {\n  purpose: 'category';\n  label: string;\n};\n\n/** A measured value such as a file size or a related-item count. Static. */\nexport type MetricChipProps = ChipPurposeBase & {\n  purpose: 'metric';\n  label: string;\n};\n\n/**\n * The release stage of a feature: `New`, `Beta`, `Deprecated`. Static, and the\n * word is derived from `stage` so the same feature cannot read as \"Beta\" in the\n * nav and \"BETA\" on its page.\n *\n * This marks the product surface, never a record. If you would let a user\n * filter a list by it, it is data and belongs in `category` -- \"Built-in\" on a\n * workflow row is a property of that workflow, not a release stage.\n */\nexport type LifecycleChipProps = ChipPurposeBase & {\n  purpose: 'lifecycle';\n  stage: LifecycleStage;\n  /** Overrides the canonical word, for a product with its own vocabulary. */\n  label?: string;\n};\n\n/** An applied filter the user can remove. `onRemove` is required. */\nexport type FilterChipProps = ChipPurposeBase & {\n  purpose: 'filter';\n  label: string;\n  /** Rendered as `field: label` so the criterion survives out of context. */\n  field?: string;\n  onRemove: () => void;\n};\n\n/**\n * A toggleable option. `selected` and `onToggle` are both required.\n *\n * Each chip is independent: toggling it does not change its neighbours. That\n * is the standalone on/off — the same chip is pressed or not. A mutually\n * exclusive group is parent state, not a chip mode: keep one selected value\n * and pass `selected={value === option}` so clicking one switches the group\n * to that option.\n */\nexport type SelectChipProps = ChipPurposeBase & {\n  purpose: 'select';\n  label: string;\n  selected: boolean;\n  onToggle: (next: boolean) => void;\n};\n\n/**\n * An entity value inside prose or a dense cell. Opens a detail menu.\n *\n * `size` is withheld here and nowhere else. An entity chip derives its type\n * from the line box it sits in, so a size from the chip scale has nothing to\n * act on -- accepting the prop would compile and then do nothing, which is the\n * failure this API exists to prevent. To make one bigger or smaller, change the\n * surrounding `Typography`.\n */\nexport type EntityChipProps = Omit<ChipPurposeBase, 'size'> & {\n  purpose: 'entity';\n  /** What the value is. Spoken in the accessible name and gates defanging. */\n  kind: EntityKind;\n  /** Canonical, full value. This is what a copy action must yield. */\n  value: string;\n  /** Shortened text for display. Defaults to `value`. */\n  display?: string;\n  /** Omit when the value was never resolved: an unresolved chip carries no tint. */\n  verdict?: EntityVerdict;\n  onOpen?: (event: React.MouseEvent<HTMLElement>) => void;\n};\n\n/** Discloses the remainder of a capped group. */\nexport type OverflowChipProps = ChipPurposeBase & {\n  purpose: 'overflow';\n  count: number;\n  onDisclose: () => void;\n  expanded?: boolean;\n};\n\n/**\n * The trigger for a multi-select value picker: the field it filters, what is\n * currently selected, and a chevron. This is the one chip that both carries a\n * value and changes it, and the exception is narrow -- it changes *which values\n * are selected*, never the record underneath. A chip that fires a verb is still\n * a Button.\n *\n * Its two targets line up with the two things a user wants from an applied\n * filter, and neither duplicates the other: the body opens the panel, and the X\n * removes the filter just as it does on `purpose=\"filter\"`. Emptying the\n * selection without losing the field is `Reset`, inside the panel.\n *\n * It renders the trigger only. The panel of options is `FilterMenu`, which owns\n * this chip and passes `expanded`, exactly as `EntityMenu` pairs with the\n * entity purpose. Reach for `FilterMenu`; a bare picker chip is for the rare\n * case where you are supplying your own surface.\n *\n * `values` are the labels a user reads, already resolved from option values. A\n * chip showing `sev_1` instead of `Critical` has failed.\n */\nexport type PickerChipProps = ChipPurposeBase & {\n  purpose: 'picker';\n  /** The filter's name, e.g. `Severity`. Shown even when nothing is selected. */\n  field: string;\n  values: string[];\n  /** Whether the panel is open. Drives the chevron and `aria-expanded`. */\n  expanded: boolean;\n  onOpen: (event: React.MouseEvent<HTMLElement>) => void;\n  /**\n   * Removes the whole filter, exactly as `onRemove` does on `purpose=\"filter\"`.\n   * The X is the same glyph in the same corner on both, so it has to mean the\n   * same thing: the criterion leaves the bar. Clearing the *selection* while\n   * keeping the field is the panel's `Reset`, not this.\n   *\n   * The caller owns the removal, since a chip cannot take itself out of its\n   * parent's list of active fields. Omit it for a bar of fixed fields that are\n   * always present.\n   */\n  onRemove?: () => void;\n  /** Defaults to `value`. Use `count` when the values are too long to show. */\n  summary?: PickerSummary;\n  /** The id of the panel this chip opens, for `aria-controls`. */\n  panelId?: string;\n};\n\n/**\n * The pre-`purpose` API. Still supported so consumers can upgrade the package\n * without touching code, and warned about once per combination in development.\n */\nexport type LegacyChipProps = MuiChipProps & { purpose?: undefined };\n\nexport type ChipProps =\n  | SeverityChipProps\n  | StatusChipProps\n  | CategoryChipProps\n  | MetricChipProps\n  | LifecycleChipProps\n  | FilterChipProps\n  | SelectChipProps\n  | EntityChipProps\n  | OverflowChipProps\n  | PickerChipProps\n  | LegacyChipProps;\n\n/** Canonical severity words. Derived rather than passed so `level` and `label` cannot disagree. */\nconst SEVERITY_LABEL: Record<SeverityKey, string> = {\n  critical: 'Critical',\n  high: 'High',\n  medium: 'Medium',\n  low: 'Low',\n  info: 'Info',\n  untriaged: 'Untriaged',\n};\n\nconst SEVERITY_COLOR = {\n  critical: 'critical',\n  high: 'high',\n  medium: 'medium',\n  low: 'low',\n  info: 'info',\n  untriaged: 'neutral',\n} as const satisfies Record<SeverityKey, MuiChipProps['color']>;\n\nconst STATUS_COLOR = {\n  active: 'success',\n  success: 'success',\n  warning: 'warning',\n  error: 'error',\n  info: 'info',\n  neutral: 'neutral',\n} as const satisfies Record<ChipStatusTone, MuiChipProps['color']>;\n\n/** Canonical stage words. Derived so one feature cannot be \"Beta\" and \"BETA\". */\nconst LIFECYCLE_LABEL: Record<LifecycleStage, string> = {\n  new: 'New',\n  beta: 'Beta',\n  alpha: 'Alpha',\n  preview: 'Preview',\n  deprecated: 'Deprecated',\n};\n\nconst VERDICT_COLOR = {\n  malicious: 'error',\n  suspicious: 'warning',\n  benign: 'success',\n  unknown: 'neutral',\n} as const satisfies Record<ChipEntityVerdict, MuiChipProps['color']>;\n\n/**\n * An entity chip sits inside a sentence, so it takes its height and size from\n * the surrounding line box instead of the chip size scale. A chip with its own\n * vertical padding pushes its line further apart than its neighbours, which\n * reads as a rendering bug rather than as emphasis. Nothing here may use a\n * fixed size: the whole point is that the chip is subordinate to the text it\n * sits in.\n *\n * `letterSpacing` is reset because the theme tracks chip text for legibility at\n * small sizes, which fights a monospace value an analyst is reading character\n * by character before copying it.\n */\nconst ENTITY_SX = {\n  height: 'auto',\n  fontFamily: fontFamily.mono,\n  // `em`, not `rem` or a fixed size, so the chip tracks whatever type it is\n  // dropped into -- body copy, a table cell, a caption. The 0.875 factor is\n  // the correction for the font swap: a monospace face at the same nominal\n  // size reads visibly larger than the proportional body face beside it,\n  // because its glyphs carry a taller x-height and a wider fixed advance.\n  // This matches the inline `code` scale, which is the same problem solved\n  // the same way, and keeps an entity chip and a code span the same size\n  // when they sit in one sentence.\n  fontSize: '0.875em',\n  letterSpacing: 'normal',\n  verticalAlign: 'baseline',\n  '& .MuiChip-label': { px: 0.75, py: 0, lineHeight: 'inherit' },\n} as const;\n\n/**\n * A `field: value` label is several things in a row rather than a string -- for\n * a picker, the field and its selection, the `+N` counter, then the chevron --\n * so it is a flex line. `inline-flex` keeps it from establishing a block box\n * inside MUI's label span, which would break the chip's vertical centring.\n */\nconst TWO_TONE_LABEL_SX = {\n  display: 'inline-flex',\n  alignItems: 'center',\n  gap: 0.5,\n} as const;\n\n/**\n * The field name is the constant and the value is what changes, so the field\n * keeps the chip's own muted ink and the value is promoted to the primary one.\n * Without the split, the eye has to read the whole pill to find the one word\n * that differs between two chips in the same bar.\n *\n * Shared by `purpose=\"filter\"` and `purpose=\"picker\"`, which sit side by side in\n * the same bar and name their field the same way. A criterion the user can only\n * remove and one they can also edit should differ by the chevron, not by how\n * their text is weighted.\n */\nconst FIELD_NAME_SX = { color: 'text.secondary' } as const;\nconst FIELD_VALUE_SX = { color: 'text.primary', fontWeight: 500 } as const;\n\n/**\n * The `+N` counter. An outlined pill in the brand hue, drawn inside the label\n * rather than as a `Badge`: a Badge anchors a count *to* an element from\n * outside it, which would float this over the chip's border and clip against\n * its neighbour in a wrapping filter bar.\n *\n * The brand ink is darkened in light mode for the same reason the lifecycle\n * chip darkens it -- the palette's turquoise is tuned for a dark surface and\n * misses AA against a light one.\n */\nconst PICKER_COUNTER_SX = {\n  display: 'inline-flex',\n  alignItems: 'center',\n  borderRadius: 999,\n  border: '1px solid',\n  borderColor: (theme: Theme) =>\n    theme.palette.mode === 'light' ? darken(theme.palette.primary.main, 0.1) : theme.palette.primary.main,\n  color: (theme: Theme) =>\n    theme.palette.mode === 'light' ? darken(theme.palette.primary.main, 0.1) : theme.palette.primary.main,\n  fontSize: '0.75rem',\n  fontWeight: 500,\n  lineHeight: 1,\n  px: 0.625,\n  py: '3px',\n  // The counter must not stretch the pill it sits in.\n  flexShrink: 0,\n} as const;\n\n/**\n * The chevron points down when closed and flips when open. Getting this wrong\n * is a real bug rather than a polish issue: a chevron that never moves stops\n * being a state indicator and becomes decoration.\n */\nconst PICKER_CHEVRON_SX = {\n  // The same ratio the theme gives the leading and delete icons. It was 1.125rem\n  // (a flat 18px), which made the chevron a third size on a chip that already\n  // had two.\n  fontSize: compactGlyphScale,\n  color: 'text.secondary',\n  flexShrink: 0,\n  transition: 'transform 150ms ease',\n} as const;\n\n/**\n * Props owned by one or more purposes. Everything else a caller passes -- `sx`,\n * `size`, `icon`, `disabled`, `data-*`, event handlers -- is forwarded to MUI\n * untouched. Keeping this as one list means a new purpose adds its props here\n * rather than adding another destructure to every branch below.\n */\nconst PURPOSE_OWNED_PROPS = new Set<string>([\n  'purpose',\n  'level',\n  'tone',\n  'stage',\n  'label',\n  'field',\n  'onRemove',\n  'selected',\n  'onToggle',\n  'value',\n  'display',\n  'verdict',\n  'kind',\n  'onOpen',\n  'count',\n  'onDisclose',\n  'expanded',\n  'values',\n  'summary',\n  'panelId',\n]);\n\n/** The caller's props minus everything a purpose derives, ready to spread onto MUI. */\nfunction forwarded(props: ChipProps): MuiChipProps {\n  const out: Record<string, unknown> = {};\n  for (const key of Object.keys(props)) {\n    if (!PURPOSE_OWNED_PROPS.has(key)) out[key] = (props as Record<string, unknown>)[key];\n  }\n  return out as MuiChipProps;\n}\n\nconst warnedLegacy = new Set<string>();\n\nfunction warnLegacyUsage(variant: unknown, color: unknown): void {\n  if (process.env.NODE_ENV === 'production') return;\n  const key = `${String(variant)}/${String(color)}`;\n  if (warnedLegacy.has(key)) return;\n  warnedLegacy.add(key);\n  console.warn(\n    `[Octopus UI] <Chip variant=\"${String(variant)}\" color=\"${String(color)}\" /> uses the ` +\n      'deprecated appearance API. Pass a `purpose` instead (severity, status, category, metric, ' +\n      'lifecycle, filter, select, entity, overflow, picker) and the appearance is derived for ' +\n      'you. See Guidelines > Choosing > Compact Labels.',\n  );\n}\n\n/**\n * The compact-label primitive.\n *\n * @example\n * <Chip purpose=\"severity\" level=\"critical\" />\n * <Chip purpose=\"status\" tone=\"warning\" label=\"Investigating\" />\n * <Chip purpose=\"filter\" field=\"Severity\" label=\"Critical\" onRemove={clear} />\n * <Chip purpose=\"entity\" kind=\"ip\" value=\"203.0.113.47\" verdict=\"malicious\" />\n *\n * @example Prefer FilterMenu, which owns this chip and its panel.\n * <Chip purpose=\"picker\" field=\"Severity\" values={['Critical', 'High']}\n *       expanded={open} onOpen={openPanel} onRemove={dropField} />\n */\nexport function Chip(props: ChipProps) {\n  const rest = forwarded(props);\n\n  switch (props.purpose) {\n    case 'severity':\n      return (\n        <MuiChip\n          {...rest}\n          variant=\"outlined\"\n          color={SEVERITY_COLOR[props.level]}\n          label={props.label ?? SEVERITY_LABEL[props.level]}\n        />\n      );\n\n    case 'status':\n      return (\n        <MuiChip {...rest} variant=\"dot\" color={STATUS_COLOR[props.tone]} label={props.label} />\n      );\n\n    // The three neutral pills share the `field` chrome: an input-coloured fill\n    // in a quiet outline, so a bar of them reads as one row of controls rather\n    // than as three kinds of object. Colour stays reserved for severity.\n    case 'category':\n    case 'metric':\n      return <MuiChip {...rest} variant=\"field\" label={props.label} />;\n\n    case 'lifecycle':\n      // The one place the brand accent is spent on a chip, and it is spent on\n      // the outline rather than a fill: an outlined pill in a hue no severity\n      // level uses is legible as \"not one of the data pills\" without adding a\n      // sixth colour to the scale severity owns. All five stages share it --\n      // the colour says \"release marker\", the word says which.\n      return (\n        <MuiChip\n          {...rest}\n          variant=\"outlined\"\n          color=\"primary\"\n          label={props.label ?? LIFECYCLE_LABEL[props.stage]}\n        />\n      );\n\n    case 'filter': {\n      const text = props.field ? `${props.field}: ${props.label}` : props.label;\n      return (\n        <MuiChip\n          {...rest}\n          variant=\"field\"\n          // Two-tone, matching `purpose=\"picker\"`: the field name muted, the\n          // value promoted. `text` above stays the flat string for the\n          // accessible name, so splitting the visual label costs nothing to a\n          // screen reader. Without a `field` the whole label *is* the value.\n          label={\n            props.field ? (\n              <Box component=\"span\" sx={TWO_TONE_LABEL_SX}>\n                <Box component=\"span\" sx={FIELD_NAME_SX}>{`${props.field}:`}</Box>\n                <Box component=\"span\" sx={FIELD_VALUE_SX}>\n                  {props.label}\n                </Box>\n              </Box>\n            ) : (\n              <Box component=\"span\" sx={FIELD_VALUE_SX}>\n                {props.label}\n              </Box>\n            )\n          }\n          onDelete={props.onRemove}\n          // MUI's delete icon is not itself a focusable button; removal by\n          // keyboard is Backspace or Delete on the focused chip. Naming the\n          // chip as a filter is what makes that target unambiguous.\n          aria-label={`${text} filter`}\n        />\n      );\n    }\n\n    case 'select': {\n      const { selected, onToggle } = props;\n      return (\n        <MuiChip\n          {...rest}\n          variant={selected ? 'filled' : 'subtle'}\n          color={selected ? 'primary' : 'neutral'}\n          label={props.label}\n          onClick={() => onToggle(!selected)}\n          aria-pressed={selected}\n        />\n      );\n    }\n\n    case 'entity': {\n      const { value, display, verdict, kind, onOpen } = props;\n      const { sx, ...entityRest } = rest;\n      const name = `${ENTITY_KIND_LABEL[kind]} ${value}`;\n      return (\n        <MuiChip\n          {...entityRest}\n          // An entity chip lives inside a sentence, so it must be phrasing\n          // content: MUI's default `div` inside a `<p>` is invalid markup and\n          // the browser closes the paragraph early, splitting the sentence.\n          component=\"span\"\n          // Not the chip's size -- `ENTITY_SX` overrides the height and font\n          // size this would set. It is here so an `icon`, if one is passed,\n          // gets the small metrics rather than metrics for a 28px chip.\n          size=\"small\"\n          variant=\"subtle\"\n          color={verdict ? VERDICT_COLOR[verdict] : 'neutral'}\n          label={display ?? value}\n          onClick={onOpen}\n          aria-haspopup={onOpen ? 'menu' : undefined}\n          // The visible text may be truncated, so the accessible name carries\n          // the full value plus the verdict a sighted user reads from the tint.\n          aria-label={verdict ? `${name}, ${verdict}` : name}\n          sx={[ENTITY_SX, ...(Array.isArray(sx) ? sx : [sx])]}\n        />\n      );\n    }\n\n    case 'overflow':\n      return (\n        <MuiChip\n          {...rest}\n          variant=\"field\"\n          label={`+${props.count} more`}\n          onClick={props.onDisclose}\n          aria-expanded={props.expanded ?? false}\n        />\n      );\n\n    case 'picker': {\n      const { field, values, expanded, onOpen, onRemove, summary, panelId } = props;\n      const { detail, overflow, accessibleName } = pickerLabel(field, values, summary);\n      return (\n        <MuiChip\n          {...rest}\n          // The same field chrome as `purpose=\"filter\"`, because a picker chip\n          // sits in the same bar and is the same kind of thing: an applied\n          // criterion. What marks it as operable is the chevron, which no static\n          // chip has -- a brighter border would have made the two read as\n          // different species of pill when only their affordances differ.\n          variant=\"field\"\n          onClick={onOpen}\n          // Not gated on the selection: removing an empty filter field is a\n          // real thing to want, and a target that appears and disappears as the\n          // user ticks boxes is a moving goalpost.\n          onDelete={onRemove}\n          // A dialog, not a menu: the panel holds a search field and an Apply\n          // button, which a menu's role cannot legally contain.\n          aria-haspopup=\"dialog\"\n          aria-expanded={expanded}\n          // Only while open, because the panel does not exist when it is closed\n          // and a dangling reference is worse than none.\n          aria-controls={expanded ? panelId : undefined}\n          // The visible text elides the rest of the selection behind `+N`, so\n          // the name spells it out.\n          aria-label={accessibleName}\n          label={\n            <Box component=\"span\" sx={TWO_TONE_LABEL_SX}>\n              <Box component=\"span\" sx={FIELD_NAME_SX}>\n                {detail === undefined ? field : `${field}:`}\n              </Box>\n              {detail !== undefined && (\n                <Box component=\"span\" sx={FIELD_VALUE_SX}>\n                  {detail}\n                </Box>\n              )}\n              {overflow > 0 && (\n                <Box component=\"span\" sx={PICKER_COUNTER_SX}>\n                  {`+${overflow}`}\n                </Box>\n              )}\n              <ExpandMoreIcon\n                sx={[\n                  PICKER_CHEVRON_SX,\n                  { transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' },\n                ]}\n              />\n            </Box>\n          }\n        />\n      );\n    }\n\n    default:\n      // The pre-`purpose` API. `label` is a legitimate MUI prop here, so this\n      // branch forwards the caller's props as given rather than the stripped set.\n      if (props.variant || props.color) warnLegacyUsage(props.variant, props.color);\n      return <MuiChip {...props} />;\n  }\n}\n\nexport default Chip;\n","/**\n * EntityMenu\n * Classification: custom (composes MUI Menu)\n *\n * The detail and pivot menu opened by `Chip purpose=\"entity\"`. Three regions in\n * a fixed order -- identity, attributes, actions -- so analysts build muscle\n * memory across every entity they open. The shape follows Carbon's\n * explainability popover template: what this is, then the supporting detail,\n * then what you can do about it.\n *\n * The copy actions live here rather than at the call site because they are the\n * reason the pattern exists, and because getting them right is not obvious: a\n * copy must yield the canonical value even when the chip shows a truncated one,\n * and a defanged copy must be correct for the kind.\n */\nimport { useState } from 'react';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Divider from '@mui/material/Divider';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport Snackbar from '@mui/material/Snackbar';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport ShieldOutlinedIcon from '@mui/icons-material/ShieldOutlined';\nimport {\n  ENTITY_KIND_LABEL,\n  defangIndicator,\n  isDefangable,\n  type EntityKind,\n  type EntityVerdict,\n} from '@/atoms/Chip';\nimport { fontFamily } from '@/tokens/typography';\n\n/** A single supporting fact about the entity. */\nexport interface EntityAttribute {\n  label: string;\n  value: string;\n}\n\nexport interface Entity {\n  kind: EntityKind;\n  /** Canonical, full value. Copy actions always yield this, never `display`. */\n  value: string;\n  /** Shortened text the chip shows. Never what gets copied. */\n  display?: string;\n  /** Omit when the value was never resolved. */\n  verdict?: EntityVerdict;\n  attributes?: EntityAttribute[];\n}\n\nexport interface EntityMenuAction {\n  id: string;\n  label: string;\n  icon?: React.ReactNode;\n  /**\n   * Why this action cannot be run, if it cannot. Rendered as text beneath the\n   * label: a greyed row with no explanation reads as a bug and gets retried.\n   */\n  unavailableReason?: string;\n}\n\nexport interface EntityMenuProps {\n  entity: Entity;\n  /** The chip that opened the menu. */\n  anchorEl: HTMLElement | null;\n  open: boolean;\n  onClose: () => void;\n  /**\n   * Pivot and navigation actions, appended after the copy actions. Keep to\n   * three or so: this menu opens from a word in a paragraph, so it is far too\n   * easy to trigger by accident for anything destructive or privileged. Those\n   * belong on the entity page behind confirmation and permission checks.\n   */\n  actions?: EntityMenuAction[];\n  onAction?: (actionId: string, entity: Entity) => void;\n  /** Suppresses the copy confirmation, for callers with their own Snackbar. */\n  disableCopyFeedback?: boolean;\n}\n\n/**\n * Writes to the clipboard, falling back to a hidden textarea where the async\n * Clipboard API is unavailable -- it needs a secure context, so an analyst on a\n * plain-HTTP internal deployment would otherwise get a silent no-op.\n */\nasync function writeToClipboard(text: string): Promise<boolean> {\n  try {\n    if (navigator.clipboard?.writeText) {\n      await navigator.clipboard.writeText(text);\n      return true;\n    }\n  } catch {\n    // Fall through to the legacy path rather than reporting failure: a rejected\n    // permission prompt still leaves execCommand viable in some browsers.\n  }\n\n  try {\n    const scratch = document.createElement('textarea');\n    scratch.value = text;\n    scratch.setAttribute('readonly', '');\n    scratch.style.position = 'fixed';\n    scratch.style.opacity = '0';\n    document.body.appendChild(scratch);\n    scratch.select();\n    const ok = document.execCommand('copy');\n    document.body.removeChild(scratch);\n    return ok;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * @example\n * const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n *\n * <Chip purpose=\"entity\" kind=\"ip\" value=\"203.0.113.47\" onOpen={(e) => setAnchor(e.currentTarget)} />\n * <EntityMenu\n *   entity={{ kind: 'ip', value: '203.0.113.47', verdict: 'malicious' }}\n *   anchorEl={anchor}\n *   open={Boolean(anchor)}\n *   onClose={() => setAnchor(null)}\n *   actions={[{ id: 'pivot', label: 'Pivot to search' }]}\n *   onAction={handleAction}\n * />\n */\nexport function EntityMenu({\n  entity,\n  anchorEl,\n  open,\n  onClose,\n  actions = [],\n  onAction,\n  disableCopyFeedback = false,\n}: EntityMenuProps) {\n  const [toast, setToast] = useState<string | null>(null);\n\n  const kindLabel = ENTITY_KIND_LABEL[entity.kind];\n  // Boolean, not the raw length: an empty attributes array would otherwise\n  // render a literal \"0\" into the menu.\n  const hasAttributes = Boolean(entity.verdict) || Boolean(entity.attributes?.length);\n\n  const handleCopy = async (text: string, what: string) => {\n    const ok = await writeToClipboard(text);\n    onClose();\n    if (!disableCopyFeedback) {\n      setToast(ok ? `${what} copied` : `Could not copy ${what.toLowerCase()}`);\n    }\n  };\n\n  const handleAction = (action: EntityMenuAction) => {\n    if (action.unavailableReason) return;\n    onClose();\n    onAction?.(action.id, entity);\n  };\n\n  return (\n    <>\n      <Menu\n        anchorEl={anchorEl}\n        open={open}\n        onClose={onClose}\n        anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}\n        transformOrigin={{ vertical: 'top', horizontal: 'left' }}\n        slotProps={{\n          paper: { sx: { minWidth: 260, maxWidth: 360 } },\n          // The menu is named by the entity so a screen-reader user knows which\n          // of several chips in a paragraph they have opened.\n          list: { 'aria-label': `${kindLabel} ${entity.value} actions`, dense: true },\n        }}\n      >\n        {/* Identity */}\n        <Box sx={{ px: 2, py: 1 }}>\n          <Typography variant=\"caption\" sx={{ color: 'text.secondary', display: 'block' }}>\n            {kindLabel}\n          </Typography>\n          <Typography\n            variant=\"body2\"\n            sx={{ fontFamily: fontFamily.mono, wordBreak: 'break-all', color: 'text.primary' }}\n          >\n            {entity.value}\n          </Typography>\n        </Box>\n\n        {/* Attributes */}\n        {hasAttributes && <Divider />}\n        {hasAttributes && (\n          <Box sx={{ px: 2, py: 1, display: 'grid', gap: 0.5 }}>\n            {entity.verdict && <AttributeRow label=\"Verdict\" value={capitalise(entity.verdict)} />}\n            {entity.attributes?.map((attribute) => (\n              <AttributeRow key={attribute.label} {...attribute} />\n            ))}\n          </Box>\n        )}\n\n        <Divider />\n\n        {/* Actions */}\n        <MenuItem onClick={() => handleCopy(entity.value, 'Value')}>\n          <ListItemIcon>\n            <ContentCopyIcon fontSize=\"small\" />\n          </ListItemIcon>\n          <ListItemText primary=\"Copy value\" />\n        </MenuItem>\n\n        {isDefangable(entity.kind) && (\n          <MenuItem onClick={() => handleCopy(defangIndicator(entity.value), 'Defanged value')}>\n            <ListItemIcon>\n              <ShieldOutlinedIcon fontSize=\"small\" />\n            </ListItemIcon>\n            <ListItemText\n              primary=\"Copy defanged\"\n              secondary={defangIndicator(entity.value)}\n              slotProps={{\n                secondary: { sx: { fontFamily: fontFamily.mono, wordBreak: 'break-all' } },\n              }}\n            />\n          </MenuItem>\n        )}\n\n        {actions.map((action) => (\n          <MenuItem\n            key={action.id}\n            onClick={() => handleAction(action)}\n            disabled={Boolean(action.unavailableReason)}\n            // A disabled MenuItem is removed from the tab order, which hides the\n            // reason from keyboard and screen-reader users. Keeping it focusable\n            // is what lets them find out why.\n            sx={{ '&.Mui-disabled': { pointerEvents: 'auto' } }}\n          >\n            {action.icon && <ListItemIcon>{action.icon}</ListItemIcon>}\n            <ListItemText primary={action.label} secondary={action.unavailableReason} />\n          </MenuItem>\n        ))}\n      </Menu>\n\n      <Snackbar\n        open={Boolean(toast)}\n        message={toast ?? ''}\n        autoHideDuration={2500}\n        onClose={() => setToast(null)}\n        anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n      />\n    </>\n  );\n}\n\nfunction AttributeRow({ label, value }: EntityAttribute) {\n  return (\n    <Box sx={{ display: 'flex', gap: 2, justifyContent: 'space-between' }}>\n      <Typography variant=\"caption\" sx={{ color: 'text.secondary', flexShrink: 0 }}>\n        {label}\n      </Typography>\n      <Typography variant=\"caption\" sx={{ color: 'text.primary', textAlign: 'right' }}>\n        {value}\n      </Typography>\n    </Box>\n  );\n}\n\nfunction capitalise(value: string): string {\n  return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nexport default EntityMenu;\n"],"mappings":"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"}