{"version":3,"file":"main.D93ed_.js","sources":["theme-10/virtual:theme","theme-10/discourse/lib/config.js","theme-10/discourse/api-initializers/channel-breadcrumb.js","theme-10/discourse/lib/areas.js","theme-10/discourse/lib/area-status.js","theme-10/discourse/components/esb/glyph.gjs","theme-10/discourse/components/area-footer.gjs","theme-10/discourse/lib/links.js","theme-10/discourse/components/esb/link-row.gjs","theme-10/discourse/lib/prefs.js","theme-10/discourse/components/esb/section.gjs","theme-10/discourse/components/esb/categories.gjs","theme-10/discourse/components/esb/area-launcher.gjs","theme-10/discourse/components/esb/chat.gjs","theme-10/discourse/components/esb/dms.gjs","theme-10/discourse/components/esb/glyph-picker.gjs","theme-10/discourse/components/esb/menu.gjs","theme-10/discourse/components/esb/link-group.gjs","theme-10/discourse/components/esb/tags.gjs","theme-10/discourse/components/esb/wiki.gjs","theme-10/discourse/components/owned-sidebar.gjs","theme-10/discourse/components/chat-menu-overlay.gjs","theme-10/discourse/api-initializers/owned-sidebar.js","theme-10/virtual:entrypoint:main"],"sourcesContent":["import { getObjectForTheme } from \"discourse/lib/theme-settings-store\";\n\nexport const settings = getObjectForTheme(10);\n\nexport function themePrefix(key) {\n  return `theme_translations.10.${key}`;\n}\n","// Mutable config populated by the api-initializers from theme `settings` (a\n// bare global only available in initializer scope, not in arbitrary modules).\n// Components import `config` and read resolved values. Settings are static per\n// page load, so populating once at boot is fine.\n\nexport const config = {\n  enabled: true, // settings.owned_sidebar_enabled\n  replaceNative: true, // settings.replace_native_sections\n  footerEnabled: true, // settings.footer_enabled\n  areaSidebarMobileEnabled: true, // settings.area_sidebar_mobile_enabled\n  areaSidebarDesktopEnabled: false, // settings.area_sidebar_desktop_enabled\n  wikiCategoryIds: [], // settings.wiki_category_ids\n  wikiIndexTopicIds: [], // settings.wiki_index_topic_ids\n  wikiHomeUrl: \"\", // settings.wiki_home_url\n  areas: [], // parsed/normalized settings.areas\n  layout: null, // parsed/normalized settings.layout (null = invalid -> fail safe)\n  rawLayout: null, // the layout as authored (un-normalized) — what the editor round-trips\n  layoutError: null, // parse/validation error message when layout is null\n  themeId: null, // this component's theme id (for saving layout from the frontend editor)\n  breadcrumb: true, // settings.chat_breadcrumb_enabled\n  allowHide: true, // settings.allow_hide_channels\n  hideMuted: false, // settings.hide_muted_channels\n  defaultGroupSort: \"activity\", // settings.default_group_sort\n  defaultChannelSort: \"alphabetical\", // settings.default_channel_sort\n  chatGroupsCollapsedByDefault: false, // settings.chat_groups_collapsed_by_default\n  pinnedCategoryIds: [], // settings.pinned_category_ids (parsed to numbers)\n  useChannelIcons: false, // settings.use_channel_icons\n  showTreeLines: false, // settings.show_tree_lines\n  expandedSidebarWidth: \"\", // settings.expanded_sidebar_width\n};\n\nconst KNOWN_TYPES = [\n  \"area_launcher\",\n  \"link\",\n  \"link_group\",\n  \"overflow_menu\",\n  \"categories\",\n  \"chat\",\n  \"direct_messages\",\n  \"wiki\",\n  \"tags\",\n  \"divider\",\n];\n\nconst DEFAULT_AREAS = [\n  {\n    key: \"home\",\n    label: \"Home\",\n    icon: \"house\",\n    href: \"/\",\n    match: \"home\",\n    footer: false,\n    sidebar: \"launcher\",\n  },\n  {\n    key: \"chat\",\n    label: \"Chat\",\n    icon: \"comments\",\n    href: \"/chat/browse/open\",\n    match: \"chat\",\n    footer: true,\n    sidebar: \"chat\",\n  },\n  {\n    key: \"dms\",\n    label: \"DMs\",\n    icon: \"envelope\",\n    href: \"/chat/direct-messages\",\n    match: \"dms\",\n    footer: false,\n    sidebar: \"dms\",\n  },\n  {\n    key: \"wiki\",\n    label: \"Wiki\",\n    icon: \"book-open\",\n    href: \"\",\n    match: \"wiki\",\n    footer: true,\n    sidebar: \"wiki\",\n  },\n  {\n    key: \"events\",\n    label: \"Events\",\n    icon: \"calendar-days\",\n    href: \"/calendar\",\n    match: \"events path:/calendar\",\n    footer: true,\n    sidebar: \"launcher\",\n  },\n  {\n    key: \"places\",\n    label: \"Places\",\n    icon: \"map-location-dot\",\n    href: \"/places\",\n    match: \"places path:/places\",\n    footer: true,\n    sidebar: \"launcher\",\n  },\n  {\n    key: \"forum\",\n    label: \"Forum\",\n    icon: \"list\",\n    href: \"/latest\",\n    match: \"forum\",\n    footer: true,\n    sidebar: \"forum\",\n  },\n  {\n    key: \"ai\",\n    label: \"AI\",\n    icon: \"robot\",\n    href: \"/discourse-ai/ai-bot/conversations\",\n    match: \"ai path:/discourse-ai path:/ai-bot\",\n    footer: false,\n    sidebar: \"launcher\",\n  },\n  {\n    key: \"profile\",\n    label: \"Profile\",\n    icon: \"user\",\n    href: \"/u/<me>\",\n    match: \"profile path:/u/\",\n    footer: true,\n    sidebar: \"launcher\",\n    logged_in_only: true,\n  },\n];\n\nfunction normalizeMatchTokens(value) {\n  if (Array.isArray(value)) {\n    return value.map((v) => String(v).trim()).filter(Boolean);\n  }\n  return String(value || \"\")\n    .split(/[\\s,|]+/)\n    .map((v) => v.trim())\n    .filter(Boolean);\n}\n\nfunction parseNumberList(value) {\n  if (Array.isArray(value)) {\n    return value.map((v) => parseInt(v, 10)).filter((n) => !isNaN(n));\n  }\n  return String(value || \"\")\n    .split(/[\\s,|]+/)\n    .map((v) => parseInt(v, 10))\n    .filter((n) => !isNaN(n));\n}\n\nfunction normalizeArea(raw, index) {\n  if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n    return null;\n  }\n  const key = String(raw.key || \"\").trim().toLowerCase();\n  if (!key) {\n    return null;\n  }\n  let href = String(raw.href || \"\");\n  if (key === \"chat\" && (!href || href === \"/chat\")) {\n    href = \"/chat/browse/open\";\n  } else if (\n    key === \"dms\" &&\n    (!href || href === \"/chat\" || href === \"/direct-messages\")\n  ) {\n    href = \"/chat/direct-messages\";\n  }\n  return {\n    ...raw,\n    key,\n    id: key,\n    label: String(raw.label || key),\n    icon: raw.icon || null,\n    emoji: raw.emoji || null,\n    href,\n    footer: raw.footer === true,\n    sidebar: String(raw.sidebar || \"\"),\n    matchers: normalizeMatchTokens(raw.match || raw.matchers || key),\n    order: Number.isFinite(raw.order) ? raw.order : index,\n  };\n}\n\nfunction hasExplicitArea(raw) {\n  return Boolean(raw.area || raw.areas || raw.area_key);\n}\n\nfunction inferredArea(raw, type, id) {\n  if (hasExplicitArea(raw)) {\n    return raw.area || raw.areas || raw.area_key;\n  }\n  if (type === \"chat\") {\n    return \"chat dms\";\n  }\n  if (type === \"direct_messages\") {\n    return \"dms\";\n  }\n  if (type === \"categories\" || type === \"tags\") {\n    return \"forum\";\n  }\n  if (type === \"wiki\") {\n    return \"wiki\";\n  }\n  if (id === \"home\" || raw.href === \"/\") {\n    return \"home\";\n  }\n  if (id === \"community\") {\n    return \"home forum\";\n  }\n  if (raw.href === \"/latest\" || raw.href === \"/categories\") {\n    return \"forum\";\n  }\n  if (String(raw.href || \"\").startsWith(\"/chat\")) {\n    return \"chat\";\n  }\n  if (String(raw.href || \"\").startsWith(\"/calendar\")) {\n    return \"events\";\n  }\n  if (String(raw.href || \"\").startsWith(\"/places\")) {\n    return \"places\";\n  }\n  return null;\n}\n\nexport function parseAreas(input) {\n  let parsed = input;\n  if (typeof input === \"string\") {\n    try {\n      parsed = JSON.parse(input);\n    } catch {\n      parsed = null;\n    }\n  }\n  if (!Array.isArray(parsed) || parsed.length === 0) {\n    parsed = DEFAULT_AREAS;\n  }\n  const areas = parsed.map((raw, i) => normalizeArea(raw, i)).filter(Boolean);\n  return areas.length > 0 ? areas : DEFAULT_AREAS.map(normalizeArea);\n}\n\n// Validate + normalize one layout item. Returns null (skip, with a console\n// warning) for unusable items; never throws.\nfunction normalizeItem(raw, index) {\n  if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n    // eslint-disable-next-line no-console\n    console.warn(`[edgetech-sidebar] layout[${index}] is not an object — skipped`);\n    return null;\n  }\n  const type = String(raw.type || \"\");\n  if (!KNOWN_TYPES.includes(type)) {\n    // eslint-disable-next-line no-console\n    console.warn(\n      `[edgetech-sidebar] layout[${index}] has unknown type \"${type}\" — skipped`\n    );\n    return null;\n  }\n  const id = String(raw.id || `${type}-${index}`);\n  const inferred = inferredArea(raw, type, id);\n  return {\n    ...raw,\n    type,\n    id,\n    area: hasExplicitArea(raw) ? raw.area : inferred,\n    areas: raw.areas,\n    // Canonical flag is `hidden` (absent = shown, so the schema editor's\n    // checkbox reads correctly); legacy `enabled: false` still honored.\n    enabled: raw.enabled !== false && raw.hidden !== true,\n    collapsible: raw.collapsible !== false,\n    collapsedByDefault: raw.collapsed_by_default === true,\n    areaOnly: raw.area_only === true,\n  };\n}\n\n// Parse the layout setting. As an `objects` typed setting it arrives as an\n// array (already structure-validated server-side); a JSON string is also\n// accepted (the frontend editor round-trips one, and it eases migration).\n// Invalid input -> null (the initializer then leaves the native sidebar\n// alone — PRD fail-safe requirement). `raw` is the parsed-but-untouched\n// array: the frontend editor edits and saves THAT, so authored config\n// round-trips without normalization noise.\nexport function parseLayout(input) {\n  let parsed = input;\n  if (typeof input === \"string\") {\n    try {\n      parsed = JSON.parse(input);\n    } catch (e) {\n      return {\n        layout: null,\n        raw: null,\n        error: `layout is not valid JSON: ${e.message}`,\n      };\n    }\n  }\n  if (!Array.isArray(parsed)) {\n    return {\n      layout: null,\n      raw: null,\n      error: \"layout must be an array of blocks\",\n    };\n  }\n  // Deep-copy so normalization and the editor never mutate the theme\n  // settings object itself.\n  parsed = JSON.parse(JSON.stringify(parsed));\n  const items = parsed\n    .map((raw, i) => normalizeItem(raw, i))\n    .filter(Boolean);\n  return { layout: items, raw: parsed, error: null };\n}\n\n// Called from each api-initializer (where the bare `settings` global is in\n// scope) so the shared `config` is populated regardless of initializer order.\n// Idempotent.\nexport function applyConfig(settings) {\n  config.enabled = settings.owned_sidebar_enabled;\n  config.replaceNative = settings.replace_native_sections;\n  config.footerEnabled = settings.footer_enabled;\n  config.areaSidebarMobileEnabled = settings.area_sidebar_mobile_enabled;\n  config.areaSidebarDesktopEnabled = settings.area_sidebar_desktop_enabled;\n  config.wikiCategoryIds = parseNumberList(settings.wiki_category_ids);\n  config.wikiIndexTopicIds = parseNumberList(settings.wiki_index_topic_ids);\n  config.wikiHomeUrl = String(settings.wiki_home_url || \"\").trim();\n  config.areas = parseAreas(settings.areas);\n  config.breadcrumb = settings.chat_breadcrumb_enabled;\n  config.allowHide = settings.allow_hide_channels;\n  config.hideMuted = settings.hide_muted_channels;\n  config.defaultGroupSort = settings.default_group_sort;\n  config.defaultChannelSort = settings.default_channel_sort;\n  config.chatGroupsCollapsedByDefault = settings.chat_groups_collapsed_by_default;\n  config.useChannelIcons = settings.use_channel_icons;\n  config.showTreeLines = settings.show_tree_lines;\n\n  // Coerce a bare number to px — a unitless --d-sidebar-width is invalid CSS\n  // and makes Discourse fall back to a percentage-ish width.\n  let width = (settings.expanded_sidebar_width || \"\").trim();\n  if (/^\\d+(\\.\\d+)?$/.test(width)) {\n    width += \"px\";\n  }\n  config.expandedSidebarWidth = width;\n\n  config.pinnedCategoryIds = String(settings.pinned_category_ids || \"\")\n    .split(\"|\")\n    .map((s) => parseInt(s, 10))\n    .filter((n) => !isNaN(n));\n\n  const { layout, raw, error } = parseLayout(settings.layout);\n  config.layout = layout;\n  config.rawLayout = raw;\n  config.layoutError = error;\n}\n","import { apiInitializer } from \"discourse/lib/api\";\nimport { applyConfig, config } from \"../lib/config\";\n\n// Parent-category breadcrumb in the open-channel navbar, ported from Chat\n// Navigator.\n//\n// The chat navbar has NO plugin outlet and NO transformer (confirmed against\n// source), so this injects DOM directly — the most upgrade-fragile feature in\n// this component, which is exactly why it sits behind its own kill switch\n// (settings.chat_breadcrumb_enabled). If a Discourse upgrade moves the navbar\n// markup, the selectors below stop matching and the injection no-ops rather\n// than throwing.\n\nconst BREADCRUMB_CLASS = \"esb-breadcrumb\";\nconst STACK_CLASS = \"esb-title-stack\";\nconst NAVBAR_SELECTOR = \".c-navbar\";\nconst TITLE_SELECTOR = \".c-navbar__channel-title, .c-navbar-channel-title\";\n\n// Wrap the channel-title link + breadcrumb in a column so the breadcrumb sits\n// BELOW the name. Critical: this must NOT change the navbar height. The navbar\n// is a fixed-height (45px) flex box centering a ~22px name line, so a compact\n// two-line column fits inside the existing slack. We add NO height/padding\n// override to the navbar (that's what blew it up to 77px once and broke the\n// full-page layout). CSS keeps the stacked lines small enough to fit.\nfunction ensureStack(navbar) {\n  navbar.querySelectorAll(`.${STACK_CLASS}`).forEach((s) => {\n    if (!s.querySelector(TITLE_SELECTOR)) {\n      s.remove(); // Ember re-rendered the title out of an old wrapper\n    }\n  });\n  const title = navbar.querySelector(TITLE_SELECTOR);\n  if (!title) {\n    return null;\n  }\n  let stack = title.closest(`.${STACK_CLASS}`);\n  if (!stack) {\n    stack = document.createElement(\"div\");\n    stack.className = STACK_CLASS;\n    title.parentNode.insertBefore(stack, title);\n    stack.appendChild(title);\n  }\n  return stack;\n}\n\n// Full category chain root-first, including the channel's own category.\nfunction buildChain(category) {\n  const chain = [];\n  let cat = category;\n  let guard = 0;\n  while (cat && guard++ < 10) {\n    chain.unshift(cat);\n    cat = cat.parentCategory;\n  }\n  return chain;\n}\n\n// The crumbs to show: Home + the chain, dropping the leaf only when it just\n// duplicates the channel name (so a \"General\" channel in a \"General\" category\n// shows only its parents, but \"Offering housing\" in \"Housing\" keeps \"Housing\").\nfunction crumbCategories(channel) {\n  const chain = buildChain(channel.chatable);\n  const leaf = chain[chain.length - 1];\n  const title = (channel.title || \"\").trim().toLowerCase();\n  if (leaf && (leaf.name || \"\").trim().toLowerCase() === title) {\n    chain.pop();\n  }\n  return chain;\n}\n\nfunction crumb(text, href, color) {\n  const a = document.createElement(\"a\");\n  a.className = \"esb-breadcrumb__crumb\";\n  a.href = href;\n  a.textContent = text;\n  if (color) {\n    a.style.setProperty(\"--esb-crumb-color\", `#${color}`);\n  }\n  return a;\n}\n\nfunction renderBreadcrumb(navbar, channel) {\n  // Only category channels have a category to show.\n  if (!channel || channel.chatableType !== \"Category\" || !channel.chatable) {\n    navbar.querySelector(`.${BREADCRUMB_CLASS}`)?.remove();\n    return;\n  }\n\n  const stack = ensureStack(navbar);\n  if (!stack) {\n    return;\n  }\n\n  const existing = stack.querySelector(`.${BREADCRUMB_CLASS}`);\n  if (existing?.dataset.channelId === String(channel.id)) {\n    return; // already rendered for this channel\n  }\n  existing?.remove();\n\n  const el = document.createElement(\"nav\");\n  el.className = BREADCRUMB_CLASS;\n  el.dataset.channelId = String(channel.id);\n  el.setAttribute(\"aria-label\", \"Channel location\");\n\n  // \"Home\" as the navigational root every category hangs off of.\n  el.appendChild(crumb(\"Home\", \"/\"));\n\n  for (const cat of crumbCategories(channel)) {\n    const sep = document.createElement(\"span\");\n    sep.className = \"esb-breadcrumb__sep\";\n    sep.textContent = \"›\";\n    el.appendChild(sep);\n    el.appendChild(crumb(cat.name, cat.url || `/c/${cat.id}`, cat.color));\n  }\n\n  stack.appendChild(el);\n}\n\nexport default apiInitializer((api) => {\n  try {\n    applyConfig(settings); // eslint-disable-line no-undef\n    if (!config.enabled || !config.breadcrumb) {\n      return;\n    }\n\n    let chatService;\n    try {\n      chatService = api.container.lookup(\"service:chat\");\n    } catch {\n      return; // chat plugin not installed\n    }\n    if (!chatService) {\n      return;\n    }\n\n    function tryInject() {\n      try {\n        const navbar = document.querySelector(NAVBAR_SELECTOR);\n        if (!navbar) {\n          return;\n        }\n        renderBreadcrumb(navbar, chatService?.activeChannel);\n      } catch {\n        // never let a render attempt throw — leave the native navbar intact\n      }\n    }\n\n    // IMPORTANT: do NOT observe the message list. A MutationObserver here\n    // fires on every loaded message, and re-injecting reflows the navbar,\n    // which trips chat's infinite-scroll-up — a runaway loop. Inject only on\n    // page change, with a few short delayed passes so the navbar can mount\n    // and the active channel settle. renderBreadcrumb is idempotent.\n    function injectPasses() {\n      [0, 120, 350, 800].forEach((delay) => setTimeout(tryInject, delay));\n    }\n\n    api.onPageChange(() => injectPasses());\n    injectPasses();\n  } catch (e) {\n    // eslint-disable-next-line no-console\n    console.error(\"[edgetech-sidebar] breadcrumb failed:\", e);\n  }\n});\n","import getURL from \"discourse/lib/get-url\";\nimport { ajax } from \"discourse/lib/ajax\";\nimport { config } from \"./config\";\n\nconst RESOLVE_PRIORITY = [\n  \"dms\",\n  \"ai\",\n  \"wiki\",\n  \"events\",\n  \"places\",\n  \"profile\",\n  \"chat\",\n  \"forum\",\n];\n\nconst wikiIndexTopicIds = new Set();\nconst wikiIndexLoadState = new Map();\nconst topicCategoryCache = new Map();\nconst topicCategoryLoadState = new Map();\n\nexport function normalizeAreaKeys(value) {\n  if (Array.isArray(value)) {\n    return value.map((v) => String(v).trim().toLowerCase()).filter(Boolean);\n  }\n  return String(value || \"\")\n    .split(/[\\s,|]+/)\n    .map((v) => v.trim().toLowerCase())\n    .filter(Boolean);\n}\n\nexport function areaKeysFor(item) {\n  if (!item || typeof item !== \"object\") {\n    return [];\n  }\n  return normalizeAreaKeys(item.areas || item.area || item.area_key);\n}\n\nexport function itemMatchesArea(item, areaKey, includeAlways = true) {\n  if (!areaKey) {\n    return item?.areaOnly !== true && item?.area_only !== true;\n  }\n  const keys = areaKeysFor(item);\n  if (keys.length === 0) {\n    return includeAlways && item?.areaOnly !== true && item?.area_only !== true;\n  }\n  return keys.includes(areaKey);\n}\n\nexport function areaVisible(area, currentUser) {\n  if (!area || area.hidden === true || area.enabled === false) {\n    return false;\n  }\n  if (area.logged_in_only && !currentUser) {\n    return false;\n  }\n  if (area.anonymous_only && currentUser) {\n    return false;\n  }\n  if (area.staff_only && !currentUser?.staff) {\n    return false;\n  }\n  if (area.admin_only && !currentUser?.admin) {\n    return false;\n  }\n  return true;\n}\n\nexport function normalizedPath(url) {\n  let path = String(url || \"/\");\n  try {\n    path = new URL(path, window.location.origin).pathname;\n  } catch {\n    path = path.split(\"?\")[0].split(\"#\")[0] || \"/\";\n  }\n  path = path.split(\"?\")[0].split(\"#\")[0] || \"/\";\n  if (path.length > 1) {\n    path = path.replace(/\\/$/, \"\");\n  }\n  return path || \"/\";\n}\n\nexport function categoryUrl(category) {\n  if (!category) {\n    return \"/\";\n  }\n  if (category.url) {\n    return category.url;\n  }\n  const slug = category.slug || category.name?.toLowerCase().replace(/\\s+/g, \"-\");\n  return slug && category.id ? `/c/${slug}/${category.id}` : \"/categories\";\n}\n\nexport function docCategories(site, siteSettings = null) {\n  if (siteSettings && siteSettings.doc_categories_enabled === false) {\n    return [];\n  }\n  // Theme-only explicit config: use the category picker setting as the source\n  // of truth when the Documentation plugin does not serialize its doc metadata\n  // to theme JS. No category/index topic ids are hardcoded.\n  if (config.wikiCategoryIds?.length) {\n    return config.wikiCategoryIds\n      .map((categoryId, index) => {\n        const category = (site?.categories || []).find((c) => c.id === categoryId);\n        if (!category) {\n          return null;\n        }\n        const indexTopicId =\n          category.doc_index_topic_id || config.wikiIndexTopicIds?.[index] || null;\n        return {\n          ...category,\n          doc_index_topic_id: indexTopicId,\n          doc_category_index: category.doc_category_index || [],\n        };\n      })\n      .filter(Boolean);\n  }\n  // Fallback for environments where discourse-doc-categories already exposes\n  // its configured index topic to the category serializer. Never infer docs\n  // from names, slugs, positions, or fixed ids.\n  return (site?.categories || []).filter((category) => category.doc_index_topic_id);\n}\n\nexport function docArticleCount(site, siteSettings = null) {\n  let count = 0;\n  for (const category of docCategories(site, siteSettings)) {\n    let categoryCount = 0;\n    const sections = category.doc_category_index || [];\n    for (const section of sections) {\n      categoryCount += section.links?.length || 0;\n    }\n    if (categoryCount === 0 && category.topic_count) {\n      categoryCount = category.topic_count;\n    }\n    count += categoryCount;\n  }\n  return count;\n}\n\nexport function wikiHref(site, siteSettings = null) {\n  if (config.wikiHomeUrl) {\n    return config.wikiHomeUrl;\n  }\n  const configuredHomepage = siteSettings?.doc_categories_homepage;\n  if (configuredHomepage && normalizedPath(configuredHomepage) !== \"/docs\") {\n    return configuredHomepage;\n  }\n  const first = docCategories(site, siteSettings)[0];\n  if (first) {\n    return categoryUrl(first);\n  }\n  if (configuredHomepage) {\n    return configuredHomepage;\n  }\n  return \"/docs\";\n}\n\nfunction routeParams(router) {\n  return router?.currentRoute?.params || {};\n}\n\nfunction routeAttrs(router) {\n  return router?.currentRoute?.attributes || {};\n}\n\nfunction safeGet(obj, key) {\n  if (!obj) {\n    return undefined;\n  }\n  if (typeof obj.get === \"function\") {\n    return obj.get(key);\n  }\n  return obj[key];\n}\n\nfunction currentCategoryIds(router) {\n  const attrs = routeAttrs(router);\n  const params = routeParams(router);\n  const category = safeGet(attrs, \"category\");\n  const parentCategory = safeGet(category, \"parentCategory\");\n  const candidates = [\n    safeGet(attrs, \"category_id\"),\n    safeGet(attrs, \"categoryId\"),\n    safeGet(category, \"id\"),\n    safeGet(parentCategory, \"id\"),\n    safeGet(category, \"parent_category_id\"),\n    safeGet(safeGet(attrs, \"topic\"), \"category_id\"),\n    safeGet(safeGet(attrs, \"topic\"), \"categoryId\"),\n    safeGet(safeGet(safeGet(attrs, \"topic\"), \"category\"), \"id\"),\n    safeGet(safeGet(safeGet(attrs, \"topic\"), \"category\"), \"parent_category_id\"),\n    params.category_id,\n  ];\n  const routePath = params.category_slug_path_with_id || \"\";\n  const routeMatch = String(routePath).match(/\\/(\\d+)$/);\n  if (routeMatch) {\n    candidates.push(routeMatch[1]);\n  }\n  const ids = [];\n  for (const candidate of candidates) {\n    const id = parseInt(candidate, 10);\n    if (!isNaN(id) && !ids.includes(id)) {\n      ids.push(id);\n    }\n  }\n  const pathMatch = normalizedPath(router?.currentURL).match(/^\\/c\\/.+\\/(\\d+)$/);\n  if (pathMatch) {\n    const id = parseInt(pathMatch[1], 10);\n    if (!isNaN(id) && !ids.includes(id)) {\n      ids.push(id);\n    }\n  }\n  const topicId = currentTopicId(router);\n  const cachedCategoryId = topicCategoryCache.get(topicId);\n  if (cachedCategoryId && !ids.includes(cachedCategoryId)) {\n    ids.push(cachedCategoryId);\n  }\n  return ids;\n}\n\nfunction currentTopicId(router) {\n  const attrs = routeAttrs(router);\n  const params = routeParams(router);\n  const candidates = [\n    safeGet(attrs, \"id\"),\n    safeGet(safeGet(attrs, \"topic\"), \"id\"),\n    params.id,\n  ];\n  for (const candidate of candidates) {\n    const id = parseInt(candidate, 10);\n    if (!isNaN(id)) {\n      return id;\n    }\n  }\n  const pathMatch = normalizedPath(router?.currentURL).match(/^\\/t\\/(?:[^/]+\\/)?(\\d+)/);\n  return pathMatch ? parseInt(pathMatch[1], 10) : null;\n}\n\nfunction withCategoryAncestors(categoryIds, site) {\n  const ids = [...categoryIds];\n  const categories = site?.categories || [];\n  const byId = new Map(categories.map((category) => [category.id, category]));\n\n  for (const categoryId of categoryIds) {\n    let category = byId.get(categoryId);\n    let guard = 0;\n    while (category && guard++ < 10) {\n      const parentId =\n        category.parent_category_id ||\n        category.parentCategory?.id ||\n        category.parent_category?.id;\n      if (!parentId || ids.includes(parentId)) {\n        break;\n      }\n      ids.push(parentId);\n      category = byId.get(parentId);\n    }\n  }\n\n  return ids;\n}\n\nfunction pathStarts(path, prefix) {\n  const cleanPrefix = normalizedPath(prefix);\n  return path === cleanPrefix || path.startsWith(`${cleanPrefix}/`);\n}\n\nfunction isDirectChatChannel(channel) {\n  return Array.isArray(channel?.chatable?.users);\n}\n\nfunction docLinkTopicIds(site, siteSettings = null) {\n  const ids = new Set(config.wikiIndexTopicIds || []);\n  for (const id of wikiIndexTopicIds) {\n    ids.add(id);\n  }\n  for (const category of docCategories(site, siteSettings)) {\n    if (category.doc_index_topic_id) {\n      ids.add(parseInt(category.doc_index_topic_id, 10));\n    }\n    for (const section of category.doc_category_index || []) {\n      for (const link of section.links || []) {\n        const match = normalizedPath(link.href).match(/^\\/t\\/(?:[^/]+\\/)?(\\d+)/);\n        if (match) {\n          ids.add(parseInt(match[1], 10));\n        }\n      }\n    }\n  }\n  return ids;\n}\n\nexport function parseIndexTopic(topic) {\n  const cooked = topic?.post_stream?.posts?.[0]?.cooked;\n  if (!cooked) {\n    return [];\n  }\n\n  const doc = new DOMParser().parseFromString(cooked, \"text/html\");\n  const sections = [];\n  let current = null;\n\n  const ensureSection = () => {\n    if (!current) {\n      current = { text: \"Index\", links: [] };\n      sections.push(current);\n    }\n    return current;\n  };\n\n  for (const node of Array.from(doc.body.children)) {\n    const tag = node.tagName?.toLowerCase();\n    if (/^h[1-3]$/.test(tag)) {\n      current = { text: node.textContent.trim(), links: [] };\n      sections.push(current);\n      continue;\n    }\n\n    if (tag === \"ul\" || tag === \"ol\") {\n      const section = ensureSection();\n      for (const link of Array.from(node.querySelectorAll(\"a[href]\"))) {\n        const text = link.textContent.trim();\n        const href = link.getAttribute(\"href\");\n        if (text && href) {\n          section.links.push({ text, href });\n        }\n      }\n    }\n  }\n\n  return sections.filter((section) => section.links.length > 0);\n}\n\nfunction topicIdsFromSections(sections) {\n  const ids = [];\n  for (const section of sections || []) {\n    for (const link of section.links || []) {\n      const match = normalizedPath(link.href).match(/^\\/t\\/(?:[^/]+\\/)?(\\d+)/);\n      if (match) {\n        const id = parseInt(match[1], 10);\n        if (!isNaN(id) && !ids.includes(id)) {\n          ids.push(id);\n        }\n      }\n    }\n  }\n  return ids;\n}\n\nasync function loadConfiguredIndexTopic(topicId) {\n  if (!topicId) {\n    return false;\n  }\n  if (wikiIndexLoadState.get(topicId) === \"loaded\") {\n    return false;\n  }\n  if (wikiIndexLoadState.has(topicId)) {\n    return wikiIndexLoadState.get(topicId);\n  }\n\n  const promise = ajax(`/t/${topicId}.json`)\n    .then((topic) => {\n      for (const id of topicIdsFromSections(parseIndexTopic(topic))) {\n        wikiIndexTopicIds.add(id);\n      }\n      wikiIndexLoadState.set(topicId, \"loaded\");\n      return true;\n    })\n    .catch(() => {\n      wikiIndexLoadState.set(topicId, \"failed\");\n      return false;\n    });\n  wikiIndexLoadState.set(topicId, promise);\n  return promise;\n}\n\nasync function loadCurrentTopicCategory(router) {\n  const topicId = currentTopicId(router);\n  if (!topicId || topicCategoryCache.has(topicId)) {\n    return false;\n  }\n  if (topicCategoryLoadState.has(topicId)) {\n    return topicCategoryLoadState.get(topicId);\n  }\n\n  const promise = ajax(`/t/${topicId}.json`)\n    .then((topic) => {\n      const categoryId = parseInt(topic.category_id, 10);\n      if (!isNaN(categoryId)) {\n        topicCategoryCache.set(topicId, categoryId);\n      }\n      topicCategoryLoadState.set(topicId, \"loaded\");\n      return true;\n    })\n    .catch(() => {\n      topicCategoryLoadState.set(topicId, \"failed\");\n      return false;\n    });\n  topicCategoryLoadState.set(topicId, promise);\n  return promise;\n}\n\nexport async function ensureWikiRouteContext(context = {}) {\n  const loaders = [];\n  for (const category of docCategories(context.site, context.siteSettings)) {\n    if (category.doc_index_topic_id) {\n      loaders.push(loadConfiguredIndexTopic(category.doc_index_topic_id));\n    }\n  }\n  if (normalizedPath(context.router?.currentURL).startsWith(\"/t\")) {\n    loaders.push(loadCurrentTopicCategory(context.router));\n  }\n  const results = await Promise.all(loaders);\n  return results.some(Boolean);\n}\n\nfunction defaultMatcher(key, context) {\n  const path = normalizedPath(context.router?.currentURL);\n  const routeName = context.router?.currentRouteName || \"\";\n  const docs = docCategories(context.site, context.siteSettings);\n  const docCategoryIds = new Set(docs.map((c) => c.id));\n  const categoryIds = withCategoryAncestors(\n    currentCategoryIds(context.router),\n    context.site\n  );\n  const topicId = currentTopicId(context.router);\n  const activeChannel = context.chat?.activeChannel;\n\n  switch (key) {\n    case \"home\":\n      return path === \"/\" || routeName === \"discovery.index\";\n    case \"dms\":\n      return (\n        pathStarts(path, \"/chat\") &&\n        (isDirectChatChannel(activeChannel) ||\n          routeName.toLowerCase().includes(\"direct\") ||\n          path.includes(\"/direct-message\") ||\n          path.includes(\"/direct-messages\"))\n      );\n    case \"chat\":\n      return pathStarts(path, \"/chat\") && !defaultMatcher(\"dms\", context);\n    case \"wiki\":\n      return (\n        categoryIds.some((categoryId) => docCategoryIds.has(categoryId)) ||\n        (topicId && docLinkTopicIds(context.site, context.siteSettings).has(topicId)) ||\n        docs.some((category) => pathStarts(path, categoryUrl(category)))\n      );\n    case \"events\":\n      return pathStarts(path, \"/calendar\") || routeName.includes(\"calendar\");\n    case \"places\":\n      return pathStarts(path, \"/places\") || routeName.includes(\"places\");\n    case \"profile\":\n      return pathStarts(path, \"/u\") || routeName.startsWith(\"user.\");\n    case \"forum\":\n      if (path === \"/\" || routeName === \"discovery.index\") {\n        return false;\n      }\n      return (\n        pathStarts(path, \"/latest\") ||\n        pathStarts(path, \"/new\") ||\n        pathStarts(path, \"/unread\") ||\n        pathStarts(path, \"/top\") ||\n        pathStarts(path, \"/categories\") ||\n        pathStarts(path, \"/c\") ||\n        pathStarts(path, \"/t\") ||\n        routeName.startsWith(\"discovery.\") ||\n        routeName.startsWith(\"topic.\")\n      );\n    case \"ai\":\n      return (\n        pathStarts(path, \"/discourse-ai\") ||\n        path.includes(\"/ai-bot\") ||\n        routeName.toLowerCase().includes(\"ai\")\n      );\n    default:\n      return false;\n  }\n}\n\nfunction tokenMatches(token, context) {\n  const path = normalizedPath(context.router?.currentURL);\n  const routeName = context.router?.currentRouteName || \"\";\n  const raw = String(token || \"\").trim();\n  const value = raw.replace(/\\*$/, \"\");\n\n  if (!value) {\n    return false;\n  }\n  if (value.startsWith(\"path:\")) {\n    return pathStarts(path, value.slice(5));\n  }\n  if (value.startsWith(\"exact:\")) {\n    return path === normalizedPath(value.slice(6));\n  }\n  if (value.startsWith(\"route:\")) {\n    return routeName.startsWith(value.slice(6));\n  }\n  if (value.startsWith(\"/\")) {\n    return pathStarts(path, value);\n  }\n  return defaultMatcher(value, context);\n}\n\nexport function resolveActiveArea(areas, context = {}) {\n  try {\n    const visible = (areas || []).filter((area) =>\n      areaVisible(area, context.currentUser)\n    );\n    const fallback = visible.find((area) => area.key === \"home\") || visible[0];\n    const priorityIndex = (area) => {\n      const index = RESOLVE_PRIORITY.indexOf(area.key);\n      return index === -1 ? RESOLVE_PRIORITY.length + area.order : index;\n    };\n    const candidates = visible\n      .filter((area) => area.key !== \"home\")\n      .slice()\n      .sort((a, b) => priorityIndex(a) - priorityIndex(b));\n\n    for (const area of candidates) {\n      const tokens = area.matchers?.length ? area.matchers : [area.key];\n      if (tokens.some((token) => tokenMatches(token, context))) {\n        return area;\n      }\n    }\n    return fallback || { key: \"home\", label: \"Home\", href: \"/\" };\n  } catch {\n    return { key: \"home\", label: \"Home\", href: \"/\" };\n  }\n}\n\nexport function hrefForArea(area, context = {}) {\n  if (!area) {\n    return getURL(\"/\");\n  }\n  let href = area.href || \"\";\n  if (area.key === \"wiki\" && !href) {\n    href = wikiHref(context.site, context.siteSettings);\n  }\n  if (area.key === \"profile\") {\n    const username = context.currentUser?.username;\n    href = href.replace(\"<me>\", username ? encodeURIComponent(username) : \"\");\n    if (!href || href === \"/u/\") {\n      href = username ? `/u/${encodeURIComponent(username)}` : \"/u\";\n    }\n  }\n  if (!href) {\n    href = \"/\";\n  }\n  if (/^https?:\\/\\//.test(href)) {\n    return href;\n  }\n  return getURL(href);\n}\n","import { docArticleCount } from \"./areas\";\n\nfunction chatChannels(chatChannelsManager) {\n  return chatChannelsManager?.publicMessageChannels || [];\n}\n\nfunction dmChannels(chatChannelsManager) {\n  return chatChannelsManager?.directMessageChannels || [];\n}\n\nfunction unreadFor(channel) {\n  return (channel.tracking?.unreadCount || 0) + (channel.tracking?.mentionCount || 0);\n}\n\nfunction chatUnread(channels) {\n  return channels.reduce((sum, channel) => sum + unreadFor(channel), 0);\n}\n\nfunction forumUnread(topicTrackingState) {\n  try {\n    return topicTrackingState?.countUnread?.() || 0;\n  } catch {\n    return 0;\n  }\n}\n\nfunction forumNew(topicTrackingState) {\n  try {\n    return topicTrackingState?.countNew?.() || 0;\n  } catch {\n    return 0;\n  }\n}\n\nfunction compactCount(value) {\n  if (!value || value <= 0) {\n    return null;\n  }\n  return value > 99 ? \"99+\" : value;\n}\n\nexport function statusForArea(area, services = {}) {\n  const key = area?.key;\n  const channels = chatChannels(services.chatChannelsManager);\n  const dms = dmChannels(services.chatChannelsManager);\n\n  switch (key) {\n    case \"chat\": {\n      const unread = chatUnread(channels);\n      return {\n        meta: channels.length ? `${channels.length} channels` : \"Channels\",\n        badge: compactCount(unread),\n        badgeKind: unread > 0 ? \"mention\" : \"count\",\n      };\n    }\n    case \"dms\": {\n      const unread = chatUnread(dms);\n      return {\n        meta: dms.length ? `${dms.length} conversations` : \"Messages\",\n        badge: compactCount(unread),\n        badgeKind: \"mention\",\n      };\n    }\n    case \"wiki\": {\n      const count = docArticleCount(services.site, services.siteSettings);\n      return {\n        meta: count ? `${count} articles` : \"Docs\",\n        badge: compactCount(count),\n        badgeKind: \"count\",\n      };\n    }\n    case \"forum\": {\n      const unread = forumUnread(services.topicTrackingState);\n      const fresh = forumNew(services.topicTrackingState);\n      const total = unread || fresh;\n      return {\n        meta: unread ? \"Unread topics\" : fresh ? \"New topics\" : \"Topics\",\n        badge: compactCount(total),\n        badgeKind: unread ? \"mention\" : \"count\",\n      };\n    }\n    case \"profile\": {\n      const count = services.currentUser?.all_unread_notifications_count || 0;\n      return {\n        meta: \"Notifications\",\n        badge: compactCount(count),\n        badgeKind: \"mention\",\n      };\n    }\n    case \"events\":\n      return { meta: \"Calendar\", badge: null, badgeKind: \"count\" };\n    case \"places\":\n      return { meta: \"Map\", badge: null, badgeKind: \"count\" };\n    case \"ai\":\n      return { meta: \"Assistant\", badge: null, badgeKind: \"count\" };\n    case \"home\":\n      return { meta: \"Main\", badge: null, badgeKind: \"count\" };\n    default:\n      return { meta: \"\", badge: null, badgeKind: \"count\" };\n  }\n}\n","import Component from \"@glimmer/component\";\nimport { htmlSafe } from \"@ember/template\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { emojiUnescape } from \"discourse/lib/text\";\n\n// One glyph slot for rows and section headers. Resolution order:\n//   1. @emoji as a name (\"tada\") -> the site's emoji image (full color)\n//   2. @emoji as a literal (unicode \"🎪\" or any short string) -> rendered text\n//   3. @icon -> FontAwesome icon (monochrome, themeable via @color)\n//   4. @fallback icon name\n// @color applies to FA icons (emoji are already colored).\nexport default class EsbGlyph extends Component {\n  get emojiHtml() {\n    const e = this.args.emoji;\n    if (e && /^[a-z0-9_+-]+$/i.test(e)) {\n      return htmlSafe(emojiUnescape(`:${e}:`));\n    }\n    return null;\n  }\n\n  get literal() {\n    const e = this.args.emoji;\n    if (e && !/^[a-z0-9_+-]+$/i.test(e)) {\n      return e;\n    }\n    return null;\n  }\n\n  get colorStyle() {\n    const c = this.args.color;\n    if (!c || !/^[#a-zA-Z0-9(),.\\s%-]+$/.test(String(c))) {\n      return null;\n    }\n    // A CSS variable, not `color:` — the glyph's .d-icon rule sets its own\n    // color and would override inheritance; it reads this var as an override.\n    return htmlSafe(`--esb-glyph-color: ${c};`);\n  }\n\n  <template>\n    <span class=\"esb-row__glyph\" style={{this.colorStyle}}>\n      {{#if this.emojiHtml}}\n        {{this.emojiHtml}}\n      {{else if this.literal}}\n        <span class=\"esb-glyph__literal\">{{this.literal}}</span>\n      {{else if @icon}}\n        {{icon @icon}}\n      {{else if @fallback}}\n        {{icon @fallback}}\n      {{/if}}\n    </span>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { getOwner } from \"@ember/owner\";\nimport { service } from \"@ember/service\";\nimport didInsert from \"@ember/render-modifiers/modifiers/did-insert\";\nimport didUpdate from \"@ember/render-modifiers/modifiers/did-update\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport {\n  areaVisible,\n  ensureWikiRouteContext,\n  hrefForArea,\n  resolveActiveArea,\n} from \"../lib/areas\";\nimport { statusForArea } from \"../lib/area-status\";\nimport { config } from \"../lib/config\";\nimport EsbGlyph from \"./esb/glyph\";\n\nexport default class AreaFooter extends Component {\n  @service composer;\n  @service currentUser;\n  @service router;\n  @service site;\n  @service siteSettings;\n  @service topicTrackingState;\n\n  @tracked wikiRouteVersion = 0;\n\n  optionalService(name) {\n    try {\n      return getOwner(this).lookup(`service:${name}`);\n    } catch {\n      return null;\n    }\n  }\n\n  get chat() {\n    return this.optionalService(\"chat\");\n  }\n\n  get chatChannelsManager() {\n    return this.optionalService(\"chat-channels-manager\");\n  }\n\n  get context() {\n    this.wikiRouteVersion;\n    return {\n      router: this.router,\n      site: this.site,\n      siteSettings: this.siteSettings,\n      currentUser: this.currentUser,\n      chat: this.chat,\n    };\n  }\n\n  get areas() {\n    return (config.areas || []).filter(\n      (area) => area.footer && areaVisible(area, this.currentUser)\n    );\n  }\n\n  get activeArea() {\n    return resolveActiveArea(config.areas, this.context);\n  }\n\n  get show() {\n    return (\n      config.enabled &&\n      config.layout &&\n      config.footerEnabled &&\n      this.site.mobileView &&\n      !this.composer?.isOpen &&\n      this.areas.length > 0\n    );\n  }\n\n  hrefFor = (area) => {\n    return hrefForArea(area, this.context);\n  };\n\n  isActive = (area) => {\n    return area.key === this.activeArea?.key;\n  };\n\n  statusFor = (area) => {\n    return statusForArea(area, {\n      chatChannelsManager: this.chatChannelsManager,\n      currentUser: this.currentUser,\n      site: this.site,\n      siteSettings: this.siteSettings,\n      topicTrackingState: this.topicTrackingState,\n    });\n  };\n\n  visit = (area, event) => {\n    const href = hrefForArea(area, this.context);\n    if (/^https?:\\/\\//.test(href) || wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(href);\n  };\n\n  refreshWikiRouteContext = async () => {\n    if (\n      await ensureWikiRouteContext({\n        router: this.router,\n        site: this.site,\n        siteSettings: this.siteSettings,\n      })\n    ) {\n      this.wikiRouteVersion++;\n    }\n  };\n\n  <template>\n    {{#if this.show}}\n      <nav\n        class=\"esb-area-footer\"\n        aria-label=\"Area navigation\"\n        {{didInsert this.refreshWikiRouteContext}}\n        {{didUpdate this.refreshWikiRouteContext this.router.currentURL}}\n      >\n        {{#each this.areas key=\"key\" as |area|}}\n          {{#let (this.statusFor area) as |status|}}\n            <a\n              href={{this.hrefFor area}}\n              class=\"esb-area-footer__item\n                {{if (this.isActive area) 'is-active'}}\"\n              aria-label={{area.label}}\n              aria-current={{if (this.isActive area) \"page\"}}\n              {{on \"click\" (fn this.visit area)}}\n            >\n              <span class=\"esb-area-footer__icon\">\n                <EsbGlyph\n                  @icon={{area.icon}}\n                  @emoji={{area.emoji}}\n                  @fallback=\"circle\"\n                />\n                {{#if status.badge}}\n                  <span\n                    class=\"esb-area-footer__badge\n                      is-{{status.badgeKind}}\"\n                    aria-label=\"{{status.badge}} {{status.meta}}\"\n                  >{{status.badge}}</span>\n                {{/if}}\n              </span>\n              <span class=\"esb-area-footer__label\">{{area.label}}</span>\n            </a>\n          {{/let}}\n        {{/each}}\n      </nav>\n    {{/if}}\n  </template>\n}\n","// Link + visibility helpers shared by all layout block components.\n\nimport getURL from \"discourse/lib/get-url\";\nimport { normalizeAreaKeys } from \"./areas\";\n\n// Evaluate a layout item's visibility rules against the current user.\n//\n// Flat form (the objects-typed setting; also what the schema editor offers):\n//   logged_in_only / anonymous_only / staff_only / admin_only: true\n//   visible_groups: [group ids]  -> member of at least one\n//\n// Legacy nested form (pre-objects JSON layouts) still honored:\n//   visibility: {logged_in, anonymous, staff, admin, groups: [names]}\nexport function itemVisible(item, currentUser) {\n  if (!item || typeof item !== \"object\") {\n    return true;\n  }\n\n  const v = item.visibility;\n  if (v && typeof v === \"object\") {\n    if (v.logged_in && !currentUser) {\n      return false;\n    }\n    if (v.anonymous && currentUser) {\n      return false;\n    }\n    if (v.staff && !currentUser?.staff) {\n      return false;\n    }\n    if (v.admin && !currentUser?.admin) {\n      return false;\n    }\n    if (Array.isArray(v.groups) && v.groups.length > 0) {\n      const memberOf = (currentUser?.groups || []).map((g) =>\n        String(g.name || \"\").toLowerCase()\n      );\n      const wanted = v.groups.map((g) => String(g).toLowerCase());\n      if (!wanted.some((g) => memberOf.includes(g))) {\n        return false;\n      }\n    }\n  }\n\n  if (item.logged_in_only && !currentUser) {\n    return false;\n  }\n  if (item.anonymous_only && currentUser) {\n    return false;\n  }\n  if (item.staff_only && !currentUser?.staff) {\n    return false;\n  }\n  if (item.admin_only && !currentUser?.admin) {\n    return false;\n  }\n  if (Array.isArray(item.visible_groups) && item.visible_groups.length > 0) {\n    const memberIds = (currentUser?.groups || []).map((g) => g.id);\n    if (!item.visible_groups.some((id) => memberIds.includes(id))) {\n      return false;\n    }\n  }\n  return true;\n}\n\n// True when an href points off-site (absolute URL to another origin).\nexport function isExternal(href) {\n  if (!href) {\n    return false;\n  }\n  if (href.startsWith(\"/\")) {\n    return false;\n  }\n  try {\n    const url = new URL(href, window.location.origin);\n    return url.origin !== window.location.origin;\n  } catch {\n    return false;\n  }\n}\n\n// Normalize one raw link object from the layout. `idx` disambiguates ids.\n// Spreads the raw object first so visibility fields (flat or legacy nested)\n// ride along for itemVisible().\nexport function normalizeLink(raw, idx = 0) {\n  if (!raw || typeof raw !== \"object\") {\n    return null;\n  }\n  const href = String(raw.href || \"\");\n  const external = raw.external === true || isExternal(href);\n  return {\n    ...raw,\n    id: String(raw.id || `link-${idx}`),\n    // Canonical flag is `hidden`; legacy `enabled: false` still honored.\n    enabled: raw.enabled !== false && raw.hidden !== true,\n    areaKeys: normalizeAreaKeys(raw.areas || raw.area || raw.area_key),\n    areaOnly: raw.area_only === true,\n    label: String(raw.label || href || \"Link\"),\n    icon: raw.icon || null,\n    emoji: raw.emoji || null,\n    iconColor: raw.icon_color || null,\n    href,\n    url: external ? href : getURL(href),\n    external,\n    // External links open in a new tab unless told otherwise; internal links\n    // never do (they route in-app).\n    newTab: external ? raw.new_tab !== false : false,\n    match: raw.match || (href === \"/\" ? \"exact\" : \"prefix\"),\n    badge: raw.badge ?? null,\n    title: raw.title || null,\n    // A link carrying its own `links` array renders as a popup menu row.\n    links: Array.isArray(raw.links) ? raw.links : null,\n  };\n}\n\n// Active-route test for a normalized link against router.currentURL.\nexport function linkIsActive(link, currentURL) {\n  if (!currentURL || link.external || link.match === \"none\") {\n    return false;\n  }\n  const current = currentURL.split(\"?\")[0].replace(/\\/$/, \"\") || \"/\";\n  const target = (link.href.split(\"?\")[0] || \"/\").replace(/\\/$/, \"\") || \"/\";\n  if (link.match === \"exact\") {\n    return current === target;\n  }\n  return current === target || current.startsWith(`${target}/`);\n}\n","import Component from \"@glimmer/component\";\nimport { on } from \"@ember/modifier\";\nimport { service } from \"@ember/service\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport { linkIsActive } from \"../../lib/links\";\nimport EsbGlyph from \"./glyph\";\n\n// One navigation row for a normalized link (lib/links.js normalizeLink).\n// Internal links route through the Ember router (SPA navigation, no reload);\n// external links get native navigation with rel=\"noopener noreferrer\" when\n// opening a new tab. Cmd/middle-click is respected for internal links.\n//\n// Active-route highlighting follows the link's `match` rule (exact / prefix /\n// none) against router.currentURL, which is tracked, so highlight follows\n// route changes automatically.\nexport default class EsbLinkRow extends Component {\n  @service router;\n\n  get active() {\n    return linkIsActive(this.args.link, this.router.currentURL);\n  }\n\n  click = (event) => {\n    if (this.args.link.external || wantsNewWindow(event)) {\n      // Native navigation. Let the click bubble: on mobile, core's\n      // outside-click handler closes the drawer, which is what we want.\n      this.args.afterClick?.();\n      return;\n    }\n    event.preventDefault();\n    this.args.afterClick?.();\n    DiscourseURL.routeTo(this.args.link.href);\n  };\n\n  <template>\n    <a\n      href={{@link.url}}\n      class=\"esb-row esb-link {{if this.active 'is-active'}}\"\n      target={{if @link.newTab \"_blank\"}}\n      rel={{if @link.newTab \"noopener noreferrer\"}}\n      title={{@link.title}}\n      {{on \"click\" this.click}}\n    >\n      <EsbGlyph\n        @icon={{@link.icon}}\n        @emoji={{@link.emoji}}\n        @color={{@link.iconColor}}\n        @fallback=\"link\"\n      />\n      <span class=\"esb-row__label\">{{@link.label}}</span>\n      {{#if @link.external}}\n        <span class=\"esb-row__ext\" aria-label=\"External link\">\n          {{icon \"arrow-up-right-from-square\"}}\n        </span>\n      {{/if}}\n      {{#if @link.badge}}\n        <span class=\"esb-badge is-count\">{{@link.badge}}</span>\n      {{/if}}\n    </a>\n  </template>\n}\n","// Per-user preference storage for the EdgeTech Sidebar.\n//\n// SEAM: this module is the single point of contact for per-user UI state:\n//   - which layout sections the user has collapsed\n//   - which chat groups the user has collapsed\n//   - per-channel visibility OVERRIDES (force-hide / force-show)\n//   - chat sort choices\n//\n// Backed by localStorage (per-device). To go cross-device later, a small plugin\n// can expose a per-user JSON endpoint and the read/write bodies here get\n// swapped WITHOUT touching any component. Keep the signatures stable.\n//\n// MIGRATION: chat-group/channel/sort state created by the predecessor\n// component (Chat Navigator, `edgetech_cso_*` keys) is read as a fallback so\n// users keep their collapsed groups, hidden channels, and sort choices.\n\nconst SECTIONS_KEY = \"edgetech_esb_sections_v1\"; // { [itemId]: \"collapsed\" | \"expanded\" }\nconst GROUPS_KEY = \"edgetech_esb_chat_groups_v1\"; // { [groupId]: \"collapsed\" | \"expanded\" }\nconst OVERRIDES_KEY = \"edgetech_esb_chat_overrides_v1\"; // { [channelId]: \"hidden\" | \"visible\" }\nconst SORT_KEY = \"edgetech_esb_chat_sort_v1\"; // { groups, channels: \"alphabetical\"|\"activity\" }\n\n// Chat Navigator keys (read-only fallbacks).\nconst LEGACY_GROUPS_KEY = \"edgetech_cso_collapsed_v2\";\nconst LEGACY_OVERRIDES_KEY = \"edgetech_cso_overrides_v1\";\nconst LEGACY_SORT_KEY = \"edgetech_cso_sort_v1\";\n\nfunction readJson(key, fallback) {\n  try {\n    const raw = window.localStorage?.getItem(key);\n    if (!raw) {\n      return fallback;\n    }\n    return JSON.parse(raw);\n  } catch {\n    return fallback;\n  }\n}\n\nfunction writeJson(key, value) {\n  try {\n    window.localStorage?.setItem(key, JSON.stringify(value));\n  } catch {\n    // localStorage unavailable (private browsing / quota) — degrade silently.\n  }\n}\n\nfunction readMap(key, legacyKey) {\n  let obj = readJson(key, null);\n  if (!obj && legacyKey) {\n    obj = readJson(legacyKey, null);\n  }\n  return obj && typeof obj === \"object\" && !Array.isArray(obj) ? obj : {};\n}\n\n// --- layout section collapse ---------------------------------------------------\n\nexport function loadSections() {\n  return readMap(SECTIONS_KEY);\n}\n\n// Returns the new map (assign to a tracked field).\n//\n// NOTE: reads the CURRENT stored map before merging rather than trusting the\n// caller's copy — every section component holds its own snapshot, so merging\n// from the caller would clobber writes made by sibling sections (lost update).\nexport function setSectionCollapsed(itemId, collapsed) {\n  const next = {\n    ...readMap(SECTIONS_KEY),\n    [itemId]: collapsed ? \"collapsed\" : \"expanded\",\n  };\n  writeJson(SECTIONS_KEY, next);\n  return next;\n}\n\n// Effective collapse: explicit user choice wins, else the layout default.\nexport function isSectionCollapsed(map, itemId, defaultCollapsed) {\n  const s = map[itemId];\n  if (s === \"collapsed\") {\n    return true;\n  }\n  if (s === \"expanded\") {\n    return false;\n  }\n  return Boolean(defaultCollapsed);\n}\n\n// --- chat group collapse ---------------------------------------------------------\n\nexport function loadGroupCollapsed() {\n  return readMap(GROUPS_KEY, LEGACY_GROUPS_KEY);\n}\n\nexport function setGroupCollapsed(map, groupId, collapsed) {\n  const next = { ...map, [groupId]: collapsed ? \"collapsed\" : \"expanded\" };\n  writeJson(GROUPS_KEY, next);\n  return next;\n}\n\nexport function isGroupCollapsed(map, groupId, defaultCollapsed) {\n  const s = map[groupId];\n  if (s === \"collapsed\") {\n    return true;\n  }\n  if (s === \"expanded\") {\n    return false;\n  }\n  return Boolean(defaultCollapsed);\n}\n\n// --- chat channel visibility overrides -------------------------------------------\n\nexport function loadOverrides() {\n  return readMap(OVERRIDES_KEY, LEGACY_OVERRIDES_KEY);\n}\n\n// state: \"hidden\" | \"visible\" | null (null clears the override).\nexport function setChannelOverride(overrides, channelId, state) {\n  const next = { ...overrides };\n  if (state === \"hidden\" || state === \"visible\") {\n    next[channelId] = state;\n  } else {\n    delete next[channelId];\n  }\n  writeJson(OVERRIDES_KEY, next);\n  return next;\n}\n\n// Effective visibility: explicit override wins, else the muted-based default.\n// Hiding never changes membership — a hidden channel stays followed.\nexport function isEffectivelyHidden(overrides, channelId, isMuted, mutedHidesByDefault) {\n  const override = overrides[channelId];\n  if (override === \"hidden\") {\n    return true;\n  }\n  if (override === \"visible\") {\n    return false;\n  }\n  return Boolean(mutedHidesByDefault && isMuted);\n}\n\n// --- chat sort -------------------------------------------------------------------\n\nexport function loadSort(defaults) {\n  const stored = readJson(SORT_KEY, null) || readJson(LEGACY_SORT_KEY, {});\n  const valid = (v) => (v === \"alphabetical\" || v === \"activity\" ? v : null);\n  return {\n    groups: valid(stored?.groups) || defaults.groups,\n    channels: valid(stored?.channels) || defaults.channels,\n  };\n}\n\nexport function setSort(current, kind, value) {\n  const next = { ...current, [kind]: value };\n  writeJson(SORT_KEY, next);\n  return next;\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { on } from \"@ember/modifier\";\nimport { htmlSafe } from \"@ember/template\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { or } from \"discourse/truth-helpers\";\nimport {\n  isSectionCollapsed,\n  loadSections,\n  setSectionCollapsed,\n} from \"../../lib/prefs\";\nimport EsbGlyph from \"./glyph\";\n\n// Generic collapsible section shell. EVERY section in the owned sidebar\n// (link groups, categories, chat, DMs, tags) renders through this, which is\n// what gives the whole sidebar one affordance language: same header, same\n// caret, same badge treatment, same collapse behavior.\n//\n// The header is a ROW: the collapse button fills it, and an optional\n// `headerActions` named block renders as siblings on the right (inline tool\n// icons — they can't live INSIDE the button, nested interactive elements are\n// invalid HTML). Actions hide while collapsed.\n//\n// Collapse state persists per layout-item id via lib/prefs.js. Badge counts\n// are passed in by the section's content component so collapsed sections\n// still communicate activity (@mentions always shows; @unreads shows as a dot\n// when collapsed, since expanded rows carry their own dots).\nexport default class EsbSection extends Component {\n  @tracked sections = loadSections();\n\n  get collapsed() {\n    if (!this.args.item.collapsible) {\n      return false;\n    }\n    return isSectionCollapsed(\n      this.sections,\n      this.args.item.id,\n      this.args.item.collapsedByDefault\n    );\n  }\n\n  get bodyId() {\n    return `esb-body-${this.args.item.id}`;\n  }\n\n  // Optional per-section accent color from the layout item (\"accent\": any CSS\n  // color). Drives the header's identity bar; defaults to var(--tertiary).\n  get accentStyle() {\n    const accent = this.args.item.accent;\n    if (!accent || !/^[#a-zA-Z0-9(),.\\s%-]+$/.test(String(accent))) {\n      return null;\n    }\n    return htmlSafe(`--esb-accent: ${accent};`);\n  }\n\n  toggle = (event) => {\n    // Keep the mobile drawer open: core closes it on any button click that\n    // bubbles to document, and a collapse toggle is not a navigation.\n    event?.stopPropagation();\n    this.sections = setSectionCollapsed(this.args.item.id, !this.collapsed);\n  };\n\n  <template>\n    <div\n      class=\"esb-section {{if this.collapsed 'is-collapsed'}}\"\n      data-esb-id={{@item.id}}\n      style={{this.accentStyle}}\n    >\n      {{#if @item.collapsible}}\n        <div class=\"esb-section__headrow\">\n          <button\n            type=\"button\"\n            class=\"esb-section__header\"\n            aria-expanded=\"{{if this.collapsed 'false' 'true'}}\"\n            aria-controls={{this.bodyId}}\n            {{on \"click\" this.toggle}}\n          >\n            {{icon (if this.collapsed \"caret-right\" \"caret-down\")}}\n            {{#if (or @item.icon @item.emoji)}}\n              <EsbGlyph @icon={{@item.icon}} @emoji={{@item.emoji}} />\n            {{/if}}\n            <span class=\"esb-section__title\">{{@item.label}}</span>\n            {{#if @mentions}}\n              <span\n                class=\"esb-badge is-mention\"\n                aria-label=\"{{@mentions}} unread mentions\"\n              >{{@mentions}}</span>\n            {{/if}}\n            {{#if this.collapsed}}\n              {{#if @unreads}}\n                <span\n                  class=\"esb-badge is-unread\"\n                  aria-label=\"Unread activity\"\n                ></span>\n              {{/if}}\n            {{/if}}\n          </button>\n          {{#if (has-block \"headerActions\")}}\n            {{#unless this.collapsed}}\n              <div class=\"esb-section__actions\">\n                {{yield to=\"headerActions\"}}\n              </div>\n            {{/unless}}\n          {{/if}}\n        </div>\n      {{else}}\n        <div class=\"esb-section__header is-static\">\n          {{#if (or @item.icon @item.emoji)}}\n            <EsbGlyph @icon={{@item.icon}} @emoji={{@item.emoji}} />\n          {{/if}}\n          <span class=\"esb-section__title\">{{@item.label}}</span>\n          {{#if @mentions}}\n            <span\n              class=\"esb-badge is-mention\"\n              aria-label=\"{{@mentions}} unread mentions\"\n            >{{@mentions}}</span>\n          {{/if}}\n        </div>\n      {{/if}}\n\n      {{#unless this.collapsed}}\n        <div class=\"esb-section__body\" id={{this.bodyId}}>\n          {{yield}}\n        </div>\n      {{/unless}}\n    </div>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { service } from \"@ember/service\";\nimport { htmlSafe } from \"@ember/template\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport getURL from \"discourse/lib/get-url\";\nimport { emojiUnescape } from \"discourse/lib/text\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport EsbLinkRow from \"./link-row\";\nimport EsbSection from \"./section\";\n\n// Categories section (layout type \"categories\"). Renders the categories the\n// current user can see as a flat-but-indented hierarchy (depth-first by\n// `position`), in owned markup with the unified row style.\n//\n// Category identity: emoji- and icon-styled categories show their emoji/icon;\n// plain categories get a colored squircle with a monogram (first letter) —\n// the Edge City visual, rebuilt natively instead of patched onto native\n// markup with a MutationObserver.\n//\n// Layout item options:\n//   source: \"all\" (default) | \"user\"  — \"user\" = only the user's sidebar categories\n//   only: [ids]      whitelist\n//   exclude: [ids]   blacklist\n//   max_depth: 3     hierarchy depth rendered\nexport default class EsbCategories extends Component {\n  @service site;\n  @service router;\n  @service currentUser;\n\n  get rows() {\n    const item = this.args.item;\n    const exclude = new Set(Array.isArray(item.exclude) ? item.exclude : []);\n    const only = Array.isArray(item.only) ? new Set(item.only) : null;\n    const maxDepth = Number.isInteger(item.max_depth) ? item.max_depth : 3;\n\n    const uncatId = this.site.uncategorized_category_id;\n    let cats = (this.site.categories || []).filter(\n      (c) =>\n        !exclude.has(c.id) &&\n        (item.include_uncategorized === true ||\n          (c.id !== uncatId && !c.isUncategorizedCategory))\n    );\n    if (only) {\n      cats = cats.filter((c) => only.has(c.id));\n    }\n    if (item.source === \"user\") {\n      const ids = this.currentUser?.sidebar_category_ids || [];\n      const idSet = new Set(ids);\n      cats = cats.filter((c) => idSet.has(c.id));\n    }\n\n    const byParent = new Map();\n    for (const c of cats) {\n      const pid = c.parent_category_id || 0;\n      if (!byParent.has(pid)) {\n        byParent.set(pid, []);\n      }\n      byParent.get(pid).push(c);\n    }\n    const ordered = (arr) =>\n      arr.sort(\n        (a, b) =>\n          (a.position ?? 0) - (b.position ?? 0) ||\n          (a.name || \"\").localeCompare(b.name || \"\")\n      );\n\n    const currentPath = (this.router.currentURL || \"\").split(\"?\")[0];\n    const rows = [];\n    const walk = (parentId, depth) => {\n      if (depth >= maxDepth) {\n        return;\n      }\n      for (const c of ordered(byParent.get(parentId) || [])) {\n        rows.push(this.rowFor(c, depth, currentPath));\n        walk(c.id, depth + 1);\n      }\n    };\n    walk(0, 0);\n    return rows;\n  }\n\n  rowFor(category, depth, currentPath) {\n    const url = category.url || `/c/${category.id}`;\n    const style = category.style_type ?? category.styleType;\n    const emoji = category.emoji;\n    const catIcon = category.icon;\n    return {\n      id: category.id,\n      name: category.name,\n      depth,\n      url,\n      squircleStyle: category.color\n        ? htmlSafe(`background-color: #${category.color};`)\n        : null,\n      monogram: (category.name || \"?\").trim().charAt(0).toUpperCase(),\n      emojiHtml:\n        style === \"emoji\" && emoji\n          ? htmlSafe(emojiUnescape(`:${emoji}:`))\n          : null,\n      iconName: style === \"icon\" && catIcon ? catIcon : null,\n      active:\n        currentPath === url || currentPath.startsWith(`${url}/`),\n    };\n  }\n\n  // Trailing \"All categories\" row (native parity); item.show_all_link: false\n  // hides it.\n  get allLink() {\n    if (this.args.item.show_all_link === false) {\n      return null;\n    }\n    return {\n      id: \"all-categories\",\n      label: \"All categories\",\n      icon: \"layer-group\",\n      href: \"/categories\",\n      url: getURL(\"/categories\"),\n      external: false,\n      newTab: false,\n      match: \"exact\",\n      badge: null,\n      title: null,\n      links: null,\n    };\n  }\n\n  visit = (row, event) => {\n    if (wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(row.url);\n  };\n\n  <template>\n    <EsbSection @item={{@item}}>\n      <ul class=\"esb-list\">\n        {{#each this.rows key=\"id\" as |row|}}\n          <li>\n            <a\n              href={{row.url}}\n              class=\"esb-row esb-cat esb-depth-{{row.depth}}\n                {{if row.active 'is-active'}}\"\n              {{on \"click\" (fn this.visit row)}}\n            >\n              <span class=\"esb-row__glyph\">\n                {{#if row.emojiHtml}}\n                  {{row.emojiHtml}}\n                {{else if row.iconName}}\n                  {{icon row.iconName}}\n                {{else}}\n                  <span\n                    class=\"esb-cat__squircle\"\n                    style={{row.squircleStyle}}\n                    data-letter={{row.monogram}}\n                  ></span>\n                {{/if}}\n              </span>\n              <span class=\"esb-row__label\">{{row.name}}</span>\n            </a>\n          </li>\n        {{/each}}\n        {{#if this.allLink}}\n          <li class=\"esb-all\">\n            <EsbLinkRow @link={{this.allLink}} />\n          </li>\n        {{/if}}\n      </ul>\n    </EsbSection>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { getOwner } from \"@ember/owner\";\nimport { service } from \"@ember/service\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport {\n  areaVisible,\n  hrefForArea,\n  resolveActiveArea,\n} from \"../../lib/areas\";\nimport { statusForArea } from \"../../lib/area-status\";\nimport { config } from \"../../lib/config\";\nimport EsbGlyph from \"./glyph\";\nimport EsbSection from \"./section\";\n\nexport default class EsbAreaLauncher extends Component {\n  @service currentUser;\n  @service router;\n  @service site;\n  @service siteSettings;\n  @service topicTrackingState;\n\n  optionalService(name) {\n    try {\n      return getOwner(this).lookup(`service:${name}`);\n    } catch {\n      return null;\n    }\n  }\n\n  get chat() {\n    return this.optionalService(\"chat\");\n  }\n\n  get chatChannelsManager() {\n    return this.optionalService(\"chat-channels-manager\");\n  }\n\n  get context() {\n    return {\n      router: this.router,\n      site: this.site,\n      siteSettings: this.siteSettings,\n      currentUser: this.currentUser,\n      chat: this.chat,\n    };\n  }\n\n  get activeArea() {\n    return resolveActiveArea(config.areas, this.context);\n  }\n\n  get areas() {\n    return (config.areas || []).filter((area) =>\n      areaVisible(area, this.currentUser)\n    );\n  }\n\n  statusFor = (area) => {\n    return statusForArea(area, {\n      chatChannelsManager: this.chatChannelsManager,\n      currentUser: this.currentUser,\n      site: this.site,\n      siteSettings: this.siteSettings,\n      topicTrackingState: this.topicTrackingState,\n    });\n  };\n\n  hrefFor = (area) => {\n    return hrefForArea(area, this.context);\n  };\n\n  isActive = (area) => {\n    return area.key === this.activeArea?.key;\n  };\n\n  visit = (area, event) => {\n    const href = hrefForArea(area, this.context);\n    if (/^https?:\\/\\//.test(href) || wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(href);\n  };\n\n  <template>\n    <EsbSection @item={{@item}}>\n      <div class=\"esb-area-launcher\">\n        {{#each this.areas key=\"key\" as |area|}}\n          {{#let (this.statusFor area) as |status|}}\n            <a\n              href={{this.hrefFor area}}\n              class=\"esb-area-launcher__button\n                {{if (this.isActive area) 'is-active'}}\"\n              aria-current={{if (this.isActive area) \"page\"}}\n              {{on \"click\" (fn this.visit area)}}\n            >\n              <span class=\"esb-area-launcher__glyph\">\n                <EsbGlyph\n                  @icon={{area.icon}}\n                  @emoji={{area.emoji}}\n                  @fallback=\"circle\"\n                />\n              </span>\n              <span class=\"esb-area-launcher__text\">\n                <span class=\"esb-area-launcher__label\">{{area.label}}</span>\n                {{#if status.meta}}\n                  <span class=\"esb-area-launcher__meta\">{{status.meta}}</span>\n                {{/if}}\n              </span>\n              {{#if status.badge}}\n                <span class=\"esb-badge is-{{status.badgeKind}}\">\n                  {{status.badge}}\n                </span>\n              {{/if}}\n            </a>\n          {{/let}}\n        {{/each}}\n      </div>\n    </EsbSection>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { cached, tracked } from \"@glimmer/tracking\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport didInsert from \"@ember/render-modifiers/modifiers/did-insert\";\nimport { LinkTo } from \"@ember/routing\";\nimport { service } from \"@ember/service\";\nimport { htmlSafe } from \"@ember/template\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport { emojiUnescape } from \"discourse/lib/text\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport { and, eq } from \"discourse/truth-helpers\";\nimport DMenu from \"float-kit/components/d-menu\";\nimport { config } from \"../../lib/config\";\nimport {\n  isEffectivelyHidden,\n  isGroupCollapsed,\n  isSectionCollapsed,\n  loadGroupCollapsed,\n  loadOverrides,\n  loadSections,\n  loadSort,\n  setChannelOverride,\n  setGroupCollapsed,\n  setSectionCollapsed,\n  setSort,\n} from \"../../lib/prefs\";\nimport EsbSection from \"./section\";\n\n// Chat section (layout type \"chat\") — Chat Navigator v2, rendered inside the\n// owned sidebar's generic section shell.\n//\n// STRUCTURE: chat is one coherent area. With `include_dms` / `include_threads`\n// on the layout item, Direct Messages and My Threads render INSIDE this\n// section as subsections under a \"Channels\" subsection — each subsection\n// collapsible on its own (small-caps headers, deliberately distinct from the\n// mixed-case category group headers). DM unreads roll up into the section\n// header pill alongside channel mentions.\n//\n// TOOLS: filter / message-search / sort are inline icon buttons in the\n// section header row (zero rows of chrome). The funnel expands a transient\n// filter input row; sort options live in a popup so changing them never\n// reflows the sidebar.\n//\n// Per-user state (subsection + group collapse, hidden channels, sort)\n// persists via lib/prefs.js, which also reads the previous Chat Navigator\n// localStorage keys so existing users keep their setup.\nexport default class EsbChat extends Component {\n  @service chat;\n  @service chatChannelsManager;\n  @service router;\n\n  @tracked collapsed = loadGroupCollapsed();\n  @tracked overrides = loadOverrides();\n  @tracked sections = loadSections();\n  @tracked showHidden = false;\n  @tracked filter = \"\";\n  @tracked filterOpen = false;\n  @tracked sort = loadSort({\n    groups: config.defaultGroupSort,\n    channels: config.defaultChannelSort,\n  });\n\n  searchUrl = \"/chat/search\";\n  browseUrl = \"/chat/browse/open\";\n\n  // The + tool: browse, join, or create channels (the browse page carries the\n  // create button for users with permission). Hidden via browse_button: false.\n  get showBrowse() {\n    return this.args.item.browse_button !== false;\n  }\n\n  // SPA-route the tool links (plain anchors would full-page reload); the\n  // click still bubbles so the mobile drawer closes on navigation.\n  visitUrl = (url, event) => {\n    if (wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(url);\n  };\n\n  get channels() {\n    return this.chatChannelsManager.publicMessageChannels || [];\n  }\n\n  // --- nested structure ------------------------------------------------------\n\n  get areaMode() {\n    return this.args.areaKey || null;\n  }\n\n  get showChannels() {\n    return this.areaMode !== \"dms\";\n  }\n\n  get sectionItem() {\n    if (this.areaMode !== \"dms\") {\n      return this.args.item;\n    }\n    return {\n      ...this.args.item,\n      label: this.dmsLabel,\n      icon: \"envelope\",\n    };\n  }\n\n  get includeDms() {\n    return (\n      this.args.item.include_dms === true &&\n      this.dmChannels.length > 0 &&\n      this.areaMode !== \"chat\"\n    );\n  }\n\n  get includeThreads() {\n    return this.args.item.include_threads === true && !this.areaMode;\n  }\n\n  get hasSubsections() {\n    return this.showChannels && (this.includeDms || this.includeThreads);\n  }\n\n  get channelsSubId() {\n    return `${this.args.item.id}:channels`;\n  }\n\n  get dmsSubId() {\n    return `${this.args.item.id}:dms`;\n  }\n\n  get channelsCollapsed() {\n    if (!this.showChannels) {\n      return true;\n    }\n    if (!this.hasSubsections) {\n      return false;\n    }\n    // While filtering, force the tree open so matches are visible.\n    if (this.filterActive) {\n      return false;\n    }\n    return isSectionCollapsed(this.sections, this.channelsSubId, false);\n  }\n\n  get dmsCollapsed() {\n    return isSectionCollapsed(this.sections, this.dmsSubId, false);\n  }\n\n  toggleChannelsSub = (event) => {\n    event?.stopPropagation();\n    this.sections = setSectionCollapsed(\n      this.channelsSubId,\n      !this.channelsCollapsed\n    );\n  };\n\n  toggleDmsSub = (event) => {\n    event?.stopPropagation();\n    this.sections = setSectionCollapsed(this.dmsSubId, !this.dmsCollapsed);\n  };\n\n  get channelsLabel() {\n    return this.args.item.channels_label || \"Channels\";\n  }\n\n  get dmsLabel() {\n    return this.args.item.dms_label || \"Direct Messages\";\n  }\n\n  get threadsLabel() {\n    return this.args.item.threads_label || \"My Threads\";\n  }\n\n  get threadsActive() {\n    return (this.router.currentURL || \"\").startsWith(\"/chat/threads\");\n  }\n\n  // Threads with unread replies, across all followed channels (tracked).\n  get threadsUnread() {\n    let n = 0;\n    for (const c of this.channels) {\n      n += c.unreadThreadsCount || 0;\n    }\n    return n;\n  }\n\n  visitThreads = (event) => {\n    if (wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(\"/chat/threads\");\n  };\n\n  // --- direct messages --------------------------------------------------------\n\n  get dmChannels() {\n    const dms = this.chatChannelsManager.directMessageChannels || [];\n    return dms.slice().sort((a, b) => this.activityOf(b) - this.activityOf(a));\n  }\n\n  // Every unread DM message is urgent — pill treatment, like mentions.\n  get dmUnread() {\n    let n = 0;\n    for (const c of this.dmChannels) {\n      n += (c.tracking?.unreadCount || 0) + (c.tracking?.mentionCount || 0);\n    }\n    return n;\n  }\n\n  dmIsGroup = (channel) => {\n    return (channel.chatable?.users?.length || 0) > 1;\n  };\n\n  dmUnreadOf = (channel) => {\n    return (\n      (channel.tracking?.unreadCount || 0) +\n      (channel.tracking?.mentionCount || 0)\n    );\n  };\n\n  // --- rendering gates ---------------------------------------------------------\n\n  get shouldRender() {\n    return (\n      (this.showChannels && this.channels.length > 0) ||\n      this.includeDms ||\n      this.includeThreads\n    );\n  }\n\n  get filterActive() {\n    return this.filter.trim().length > 0;\n  }\n\n  matchesFilter(channel) {\n    if (!this.filterActive) {\n      return true;\n    }\n    return (channel.title || \"\")\n      .toLowerCase()\n      .includes(this.filter.trim().toLowerCase());\n  }\n\n  // Walk a category up to its topmost ancestor (the pop-up).\n  rootCategoryFor(channel) {\n    let cat = channel.chatable;\n    if (!cat) {\n      return null;\n    }\n    let guard = 0;\n    while (cat.parentCategory && guard++ < 10) {\n      cat = cat.parentCategory;\n    }\n    return cat;\n  }\n\n  // Epoch ms of a channel's last message. Channels with no real message sort\n  // as oldest (the API hands back a near-now created_at for empty channels,\n  // so key off the message id, not the date).\n  activityOf = (channel) => {\n    const msg = channel.lastMessage;\n    if (!msg?.id) {\n      return 0;\n    }\n    return new Date(msg.createdAt).valueOf() || 0;\n  };\n\n  isHidden = (channel) => {\n    return isEffectivelyHidden(\n      this.overrides,\n      channel.id,\n      channel.currentUserMembership?.muted,\n      config.hideMuted\n    );\n  };\n\n  @cached\n  get groups() {\n    const byRoot = new Map();\n    for (const channel of this.channels) {\n      const root = this.rootCategoryFor(channel);\n      const key = root?.id ?? \"uncategorized\";\n      if (!byRoot.has(key)) {\n        byRoot.set(key, { category: root, channels: [] });\n      }\n      byRoot.get(key).channels.push(channel);\n    }\n\n    const groups = [];\n    for (const group of byRoot.values()) {\n      const id = group.category?.id ?? \"uncategorized\";\n      const channels = group.channels.filter(\n        (c) => (this.showHidden || !this.isHidden(c)) && this.matchesFilter(c)\n      );\n      if (channels.length === 0) {\n        continue;\n      }\n      this.sortChannels(channels);\n      // Rollups count ALL the group's channels (hidden included — a mention\n      // in a hidden channel still matters), not just the visible rows.\n      let groupMentions = 0;\n      let groupUnread = 0;\n      for (const c of group.channels) {\n        groupMentions += c.tracking?.mentionCount || 0;\n        groupUnread += c.tracking?.unreadCount || 0;\n      }\n      groups.push({\n        id,\n        name: group.category?.name ?? \"Other channels\",\n        color: group.category?.color,\n        mentions: groupMentions,\n        unread: groupUnread,\n        // While filtering, force groups open so matches are visible.\n        isCollapsed: this.filterActive\n          ? false\n          : isGroupCollapsed(\n              this.collapsed,\n              id,\n              config.chatGroupsCollapsedByDefault\n            ),\n        activity: Math.max(0, ...channels.map(this.activityOf)),\n        channels,\n      });\n    }\n    this.sortGroups(groups);\n    return groups;\n  }\n\n  sortChannels(channels) {\n    if (this.sort.channels === \"activity\") {\n      channels.sort((a, b) => this.activityOf(b) - this.activityOf(a));\n    } else {\n      channels.sort((a, b) => (a.title || \"\").localeCompare(b.title || \"\"));\n    }\n  }\n\n  sortGroups(groups) {\n    const pinned = config.pinnedCategoryIds;\n    const pinIndex = (g) => {\n      const i = pinned.indexOf(g.id);\n      return i === -1 ? Infinity : i;\n    };\n    groups.sort((a, b) => {\n      const pa = pinIndex(a);\n      const pb = pinIndex(b);\n      if (pa !== pb) {\n        return pa - pb;\n      }\n      if (this.sort.groups === \"activity\") {\n        return b.activity - a.activity;\n      }\n      return a.name.localeCompare(b.name);\n    });\n  }\n\n  get hiddenCount() {\n    let n = 0;\n    for (const c of this.channels) {\n      if (this.isHidden(c)) {\n        n++;\n      }\n    }\n    return n;\n  }\n\n  // Section-header rollups: channel mentions + urgent DM unreads in the pill;\n  // plain channel unreads as the collapsed dot.\n  get headerMentions() {\n    let n = 0;\n    if (this.showChannels) {\n      for (const c of this.channels) {\n        n += c.tracking?.mentionCount || 0;\n      }\n    }\n    if (this.includeDms) {\n      n += this.dmUnread;\n    }\n    return n;\n  }\n\n  get totalUnread() {\n    if (!this.showChannels) {\n      return 0;\n    }\n    let n = 0;\n    for (const c of this.channels) {\n      n += c.tracking?.unreadCount || 0;\n    }\n    return n;\n  }\n\n  isActive = (channel) => {\n    return this.chat.activeChannel?.id === channel.id;\n  };\n\n  unread = (channel) => {\n    return channel.tracking?.unreadCount || 0;\n  };\n\n  mentions = (channel) => {\n    return channel.tracking?.mentionCount || 0;\n  };\n\n  glyphFor = (channel) => {\n    if (!config.useChannelIcons) {\n      return {};\n    }\n    if (channel.emoji) {\n      return {\n        isEmoji: true,\n        emojiHtml: htmlSafe(emojiUnescape(`:${channel.emoji}:`)),\n      };\n    }\n    const cat = channel.chatable;\n    const style = cat?.style_type ?? cat?.styleType;\n    if (style === \"emoji\" && cat.emoji) {\n      return {\n        isEmoji: true,\n        emojiHtml: htmlSafe(emojiUnescape(`:${cat.emoji}:`)),\n      };\n    }\n    if (style === \"icon\" && cat.icon) {\n      return { isIcon: true, iconName: cat.icon };\n    }\n    return {};\n  };\n\n  updateFilter = (event) => {\n    this.filter = event.target.value;\n  };\n\n  clearFilter = (event) => {\n    event?.stopPropagation();\n    this.filter = \"\";\n  };\n\n  // The funnel icon expands/collapses a transient filter input row (no chrome\n  // while idle). Closing also clears.\n  toggleFilterOpen = (event) => {\n    event?.stopPropagation();\n    if (this.filterOpen) {\n      this.filterOpen = false;\n      this.filter = \"\";\n    } else {\n      this.filterOpen = true;\n    }\n  };\n\n  focusInput = (el) => {\n    el.focus();\n  };\n\n  // Sort choices live in a small popup menu (icon trigger) so changing them\n  // never reflows the sidebar.\n  setSortTo = (kind, value, event) => {\n    event?.stopPropagation();\n    this.sort = setSort(this.sort, kind, value);\n  };\n\n  stopBubble = (event) => {\n    event.stopPropagation();\n  };\n\n  toggleGroup = (group, event) => {\n    event?.stopPropagation();\n    this.collapsed = setGroupCollapsed(\n      this.collapsed,\n      group.id,\n      !group.isCollapsed\n    );\n  };\n\n  toggleChannelVisibility = (channel, event) => {\n    event.preventDefault();\n    event.stopPropagation();\n    const next = this.isHidden(channel) ? \"visible\" : \"hidden\";\n    this.overrides = setChannelOverride(this.overrides, channel.id, next);\n  };\n\n  toggleShowHidden = (event) => {\n    event?.stopPropagation();\n    this.showHidden = !this.showHidden;\n  };\n\n  <template>\n    {{#if this.shouldRender}}\n      <EsbSection\n        @item={{this.sectionItem}}\n        @mentions={{this.headerMentions}}\n        @unreads={{this.totalUnread}}\n      >\n        <:headerActions>\n          {{#if this.showChannels}}\n            <button\n              type=\"button\"\n              class=\"esb-tool {{if this.filterOpen 'is-active'}}\"\n              title=\"Filter channels\"\n              aria-label=\"Filter channels\"\n              aria-expanded=\"{{if this.filterOpen 'true' 'false'}}\"\n              {{on \"click\" this.toggleFilterOpen}}\n            >{{icon \"filter\"}}</button>\n\n            <a\n              class=\"esb-tool\"\n              href={{this.searchUrl}}\n              title=\"Search chat messages\"\n              aria-label=\"Search chat messages\"\n              {{on \"click\" (fn this.visitUrl this.searchUrl)}}\n            >{{icon \"magnifying-glass\"}}</a>\n\n            <div class=\"esb-menuwrap\" {{on \"click\" this.stopBubble}}>\n              <DMenu\n                @identifier=\"esb-chat-sort\"\n                @modalForMobile={{true}}\n                @triggerClass=\"esb-tool\"\n                @ariaLabel=\"Sort channels and groups\"\n              >\n                <:trigger>\n                  {{icon \"sort\"}}\n                </:trigger>\n                <:content>\n                  <div class=\"esb-sortmenu\">\n                    <div class=\"esb-sortmenu__group\">\n                      <span class=\"esb-sortmenu__label\">Groups</span>\n                      <button\n                        type=\"button\"\n                        class=\"esb-sortmenu__opt\n                          {{if (eq this.sort.groups 'activity') 'is-active'}}\"\n                        {{on \"click\" (fn this.setSortTo \"groups\" \"activity\")}}\n                      >Recent</button>\n                      <button\n                        type=\"button\"\n                        class=\"esb-sortmenu__opt\n                          {{if\n                            (eq this.sort.groups 'alphabetical')\n                            'is-active'\n                          }}\"\n                        {{on\n                          \"click\"\n                          (fn this.setSortTo \"groups\" \"alphabetical\")\n                        }}\n                      >A–Z</button>\n                    </div>\n                    <div class=\"esb-sortmenu__group\">\n                      <span class=\"esb-sortmenu__label\">Channels</span>\n                      <button\n                        type=\"button\"\n                        class=\"esb-sortmenu__opt\n                          {{if\n                            (eq this.sort.channels 'activity')\n                            'is-active'\n                          }}\"\n                        {{on \"click\" (fn this.setSortTo \"channels\" \"activity\")}}\n                      >Recent</button>\n                      <button\n                        type=\"button\"\n                        class=\"esb-sortmenu__opt\n                          {{if\n                            (eq this.sort.channels 'alphabetical')\n                            'is-active'\n                          }}\"\n                        {{on\n                          \"click\"\n                          (fn this.setSortTo \"channels\" \"alphabetical\")\n                        }}\n                      >A–Z</button>\n                    </div>\n                  </div>\n                </:content>\n              </DMenu>\n            </div>\n\n            {{#if this.showBrowse}}\n              <a\n                class=\"esb-tool\"\n                href={{this.browseUrl}}\n                title=\"Browse, join, or create channels\"\n                aria-label=\"Browse, join, or create channels\"\n                {{on \"click\" (fn this.visitUrl this.browseUrl)}}\n              >{{icon \"plus\"}}</a>\n            {{/if}}\n          {{/if}}\n        </:headerActions>\n\n        <:default>\n          <div class=\"esb-chat {{if config.showTreeLines 'esb-chat--lines'}}\">\n            {{#if (and this.showChannels this.filterOpen)}}\n              <div class=\"esb-chat__filter-row\">\n                <span class=\"esb-filter\">\n                  {{icon \"filter\"}}\n                  <input\n                    type=\"text\"\n                    class=\"esb-filter__input\"\n                    placeholder=\"Filter channels\"\n                    aria-label=\"Filter channels\"\n                    value={{this.filter}}\n                    {{didInsert this.focusInput}}\n                    {{on \"input\" this.updateFilter}}\n                  />\n                  {{#if this.filterActive}}\n                    <button\n                      type=\"button\"\n                      class=\"esb-filter__clear\"\n                      title=\"Clear filter\"\n                      aria-label=\"Clear filter\"\n                      {{on \"click\" this.clearFilter}}\n                    >{{icon \"xmark\"}}</button>\n                  {{/if}}\n                </span>\n              </div>\n            {{/if}}\n\n            {{#if (and this.showChannels this.hasSubsections)}}\n              <button\n                type=\"button\"\n                class=\"esb-sub__header\"\n                aria-expanded=\"{{if this.channelsCollapsed 'false' 'true'}}\"\n                {{on \"click\" this.toggleChannelsSub}}\n              >\n                {{icon (if this.channelsCollapsed \"caret-right\" \"caret-down\")}}\n                <span class=\"esb-sub__title\">{{this.channelsLabel}}</span>\n              </button>\n            {{/if}}\n\n            {{#if this.showChannels}}\n              {{#unless this.channelsCollapsed}}\n              <div class=\"esb-chat__channels-area\">\n                {{#each this.groups key=\"id\" as |group|}}\n                  <div class=\"esb-chat__group\" data-esb-group={{group.id}}>\n                    <button\n                      type=\"button\"\n                      class=\"esb-chat__group-header\"\n                      aria-expanded=\"{{if group.isCollapsed 'false' 'true'}}\"\n                      {{on \"click\" (fn this.toggleGroup group)}}\n                    >\n                      {{icon\n                        (if group.isCollapsed \"caret-right\" \"caret-down\")\n                      }}\n                      {{#if group.color}}\n                        <span\n                          class=\"esb-chat__swatch\"\n                          style={{htmlSafe (groupSwatch group.color)}}\n                        ></span>\n                      {{/if}}\n                      <span class=\"esb-chat__group-name\">{{group.name}}</span>\n                      {{#if group.mentions}}\n                        <span\n                          class=\"esb-badge is-mention\"\n                          aria-label=\"{{group.mentions}} unread mentions\"\n                        >{{group.mentions}}</span>\n                      {{/if}}\n                      {{#if group.isCollapsed}}\n                        {{#if group.unread}}\n                          <span\n                            class=\"esb-badge is-unread\"\n                            aria-label=\"Unread activity\"\n                          ></span>\n                        {{/if}}\n                      {{/if}}\n                      <span class=\"esb-chat__group-count\">\n                        {{group.channels.length}}\n                      </span>\n                    </button>\n\n                    {{#unless group.isCollapsed}}\n                      <ul class=\"esb-chat__channels\">\n                        {{#each group.channels key=\"id\" as |channel|}}\n                          <li\n                            class=\"esb-channel\n                              {{if (this.isActive channel) 'is-active'}}\n                              {{if (this.isHidden channel) 'is-hidden'}}\"\n                          >\n                            <LinkTo\n                              @route=\"chat.channel\"\n                              @models={{channel.routeModels}}\n                              class=\"esb-row esb-channel__link\"\n                            >\n                              <span class=\"esb-row__glyph\">\n                                {{#let (this.glyphFor channel) as |glyph|}}\n                                  {{#if glyph.isEmoji}}\n                                    {{glyph.emojiHtml}}\n                                  {{else if glyph.isIcon}}\n                                    {{icon glyph.iconName}}\n                                  {{else}}\n                                    {{icon \"hashtag\"}}\n                                  {{/if}}\n                                {{/let}}\n                              </span>\n                              <span class=\"esb-row__label\">\n                                {{channel.title}}\n                              </span>\n                              {{#if (this.mentions channel)}}\n                                <span\n                                  class=\"esb-badge is-mention\"\n                                  aria-label=\"{{this.mentions channel}} unread mentions\"\n                                >\n                                  {{this.mentions channel}}\n                                </span>\n                              {{else if (this.unread channel)}}\n                                <span\n                                  class=\"esb-badge is-unread\"\n                                  aria-label=\"Unread messages\"\n                                ></span>\n                              {{/if}}\n                            </LinkTo>\n\n                            {{#if config.allowHide}}\n                              <button\n                                type=\"button\"\n                                class=\"esb-channel__action\"\n                                title={{if\n                                  (this.isHidden channel)\n                                  \"Show in sidebar\"\n                                  \"Hide from sidebar (stay a member)\"\n                                }}\n                                {{on\n                                  \"click\"\n                                  (fn this.toggleChannelVisibility channel)\n                                }}\n                              >\n                                {{icon\n                                  (if (this.isHidden channel) \"eye\" \"eye-slash\")\n                                }}\n                              </button>\n                            {{/if}}\n                          </li>\n                        {{/each}}\n                      </ul>\n                    {{/unless}}\n                  </div>\n                {{else}}\n                  {{#if this.filterActive}}\n                    <div class=\"esb-empty\">\n                      No channels match \"{{this.filter}}\"\n                    </div>\n                  {{/if}}\n                {{/each}}\n\n                {{#if this.hiddenCount}}\n                  <button\n                    type=\"button\"\n                    class=\"esb-show-hidden\"\n                    {{on \"click\" this.toggleShowHidden}}\n                  >\n                    {{icon (if this.showHidden \"eye-slash\" \"eye\")}}\n                    {{if\n                      this.showHidden\n                      \"Hide hidden channels\"\n                      \"Show hidden channels\"\n                    }}\n                    <span class=\"esb-show-hidden__count\">\n                      {{this.hiddenCount}}\n                    </span>\n                  </button>\n                {{/if}}\n              </div>\n              {{/unless}}\n            {{/if}}\n\n            {{#if this.includeDms}}\n              <button\n                type=\"button\"\n                class=\"esb-sub__header\"\n                aria-expanded=\"{{if this.dmsCollapsed 'false' 'true'}}\"\n                {{on \"click\" this.toggleDmsSub}}\n              >\n                {{icon (if this.dmsCollapsed \"caret-right\" \"caret-down\")}}\n                <span class=\"esb-sub__title\">{{this.dmsLabel}}</span>\n                {{#if this.dmUnread}}\n                  <span\n                    class=\"esb-badge is-mention\"\n                    aria-label=\"{{this.dmUnread}} unread messages\"\n                  >{{this.dmUnread}}</span>\n                {{/if}}\n              </button>\n\n              {{#unless this.dmsCollapsed}}\n                <ul class=\"esb-chat__dms\">\n                  {{#each this.dmChannels key=\"id\" as |channel|}}\n                    <li\n                      class=\"esb-dm\n                        {{if (this.isActive channel) 'is-active'}}\"\n                    >\n                      <LinkTo\n                        @route=\"chat.channel\"\n                        @models={{channel.routeModels}}\n                        class=\"esb-row esb-dm__link\"\n                      >\n                        <span class=\"esb-row__glyph\">\n                          {{icon (if (this.dmIsGroup channel) \"users\" \"user\")}}\n                        </span>\n                        <span class=\"esb-row__label\">{{channel.title}}</span>\n                        {{#if (this.dmUnreadOf channel)}}\n                          <span\n                            class=\"esb-badge is-mention\"\n                            aria-label=\"{{this.dmUnreadOf channel}} unread messages\"\n                          >\n                            {{this.dmUnreadOf channel}}\n                          </span>\n                        {{/if}}\n                      </LinkTo>\n                    </li>\n                  {{/each}}\n                </ul>\n              {{/unless}}\n            {{/if}}\n\n            {{#if this.includeThreads}}\n              {{! A container-grade DRILL-IN: same level and small-caps style\n                  as the Channels/DM subsection headers, but it navigates to\n                  the threads page — the trailing chevron (vs leading caret)\n                  is the \"opens elsewhere, doesn't expand\" signal. }}\n              <a\n                href=\"/chat/threads\"\n                class=\"esb-sub__header esb-sub__link\n                  {{if this.threadsActive 'is-active'}}\"\n                {{on \"click\" this.visitThreads}}\n              >\n                {{icon \"comments\"}}\n                <span class=\"esb-sub__title\">{{this.threadsLabel}}</span>\n                {{#if this.threadsUnread}}\n                  <span\n                    class=\"esb-badge is-unread\"\n                    aria-label=\"{{this.threadsUnread}} threads with unread replies\"\n                  ></span>\n                {{/if}}\n                <span class=\"esb-sub__chevron\">{{icon \"angle-right\"}}</span>\n              </a>\n            {{/if}}\n          </div>\n        </:default>\n      </EsbSection>\n    {{/if}}\n  </template>\n}\n\nfunction groupSwatch(color) {\n  return `background-color: #${color};`;\n}\n","import Component from \"@glimmer/component\";\nimport { LinkTo } from \"@ember/routing\";\nimport { service } from \"@ember/service\";\nimport icon from \"discourse/helpers/d-icon\";\nimport EsbSection from \"./section\";\n\n// Direct messages section (layout type \"direct_messages\"). Renders the user's\n// DM channels in owned markup with the unified row/badge style, sorted by\n// most recent activity. Unread totals roll up to the section header so a\n// collapsed section still shows activity.\nexport default class EsbDms extends Component {\n  @service chat;\n  @service chatChannelsManager;\n\n  activityOf = (channel) => {\n    const msg = channel.lastMessage;\n    if (!msg?.id) {\n      return 0;\n    }\n    return new Date(msg.createdAt).valueOf() || 0;\n  };\n\n  get channels() {\n    const dms = this.chatChannelsManager.directMessageChannels || [];\n    return dms.slice().sort((a, b) => this.activityOf(b) - this.activityOf(a));\n  }\n\n  get shouldRender() {\n    return this.channels.length > 0;\n  }\n\n  // DM unreads are \"urgent\" in Discourse's model — every unread DM message\n  // deserves attention, so the rollup renders as a mention-style pill.\n  get totalUnread() {\n    let n = 0;\n    for (const c of this.channels) {\n      n +=\n        (c.tracking?.unreadCount || 0) + (c.tracking?.mentionCount || 0);\n    }\n    return n;\n  }\n\n  isActive = (channel) => {\n    return this.chat.activeChannel?.id === channel.id;\n  };\n\n  unread = (channel) => {\n    return (\n      (channel.tracking?.unreadCount || 0) +\n      (channel.tracking?.mentionCount || 0)\n    );\n  };\n\n  isGroup = (channel) => {\n    return (channel.chatable?.users?.length || 0) > 1;\n  };\n\n  <template>\n    {{#if this.shouldRender}}\n      <EsbSection @item={{@item}} @mentions={{this.totalUnread}}>\n        <ul class=\"esb-list\">\n          {{#each this.channels key=\"id\" as |channel|}}\n            <li class=\"esb-dm {{if (this.isActive channel) 'is-active'}}\">\n              <LinkTo\n                @route=\"chat.channel\"\n                @models={{channel.routeModels}}\n                class=\"esb-row esb-dm__link\"\n              >\n                <span class=\"esb-row__glyph\">\n                  {{icon (if (this.isGroup channel) \"users\" \"user\")}}\n                </span>\n                <span class=\"esb-row__label\">{{channel.title}}</span>\n                {{#if (this.unread channel)}}\n                  <span\n                    class=\"esb-badge is-mention\"\n                    aria-label=\"{{this.unread channel}} unread messages\"\n                  >\n                    {{this.unread channel}}\n                  </span>\n                {{/if}}\n              </LinkTo>\n            </li>\n          {{/each}}\n        </ul>\n      </EsbSection>\n    {{/if}}\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { htmlSafe } from \"@ember/template\";\nimport EmojiPickerContent from \"discourse/components/emoji-picker/content\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { emojiUnescape } from \"discourse/lib/text\";\nimport { eq, or } from \"discourse/truth-helpers\";\nimport DIconGridPickerContent from \"discourse/ui-kit/d-icon-grid-picker/content\";\nimport DMenu from \"float-kit/components/d-menu\";\n\n// ONE glyph button per editor row. The trigger previews the current glyph\n// (colored icon, emoji image, or an empty placeholder); the popup has two\n// tabs — Icons (core's available-icons grid, plus the color control, since\n// color only applies to icons) and Emoji (core's full emoji browser) — and a\n// Remove action. Picking an emoji clears icon AND color; picking an icon\n// clears emoji.\nexport default class EsbGlyphPicker extends Component {\n  @tracked tab = this.args.emoji ? \"emoji\" : \"icons\";\n\n  menuApi = null;\n\n  onRegisterApi = (api) => {\n    this.menuApi = api;\n  };\n\n  closeMenu = () => {\n    this.menuApi?.close();\n  };\n\n  get emojiHtml() {\n    const e = this.args.emoji;\n    if (e && /^[a-z0-9_+-]+$/i.test(e)) {\n      return htmlSafe(emojiUnescape(`:${e}:`));\n    }\n    return null;\n  }\n\n  get emojiLiteral() {\n    const e = this.args.emoji;\n    return e && !/^[a-z0-9_+-]+$/i.test(e) ? e : null;\n  }\n\n  get colorStyle() {\n    const c = this.args.color;\n    if (!c || !/^[#a-zA-Z0-9(),.\\s%-]+$/.test(String(c))) {\n      return null;\n    }\n    return htmlSafe(`color: ${c};`);\n  }\n\n  get colorHex() {\n    return /^#[0-9a-fA-F]{6}$/.test(this.args.color || \"\")\n      ? this.args.color\n      : \"#999999\";\n  }\n\n  setTab = (tab, event) => {\n    event?.stopPropagation();\n    this.tab = tab;\n  };\n\n  pickIcon = (id) => {\n    this.args.onIcon(id);\n    this.tab = \"icons\";\n    // stay open: the color control sits here, and trying shades is natural\n  };\n\n  pickEmoji = (name) => {\n    this.args.onEmoji(name);\n    this.closeMenu();\n  };\n\n  setColor = (event) => {\n    this.args.onColor(event.target.value);\n  };\n\n  clearColor = (event) => {\n    event?.stopPropagation();\n    this.args.onColor(null);\n  };\n\n  clearGlyph = (event) => {\n    event?.stopPropagation();\n    this.args.onClear();\n    this.closeMenu();\n  };\n\n  stopBubble = (event) => {\n    event.stopPropagation();\n  };\n\n  <template>\n    <div class=\"esb-glyphwrap\" {{on \"click\" this.stopBubble}}>\n      <DMenu\n        @identifier=\"esb-glyph-picker\"\n        @modalForMobile={{true}}\n        @triggerClass=\"esb-glyphpick\"\n        @onRegisterApi={{this.onRegisterApi}}\n        @ariaLabel=\"Choose icon or emoji\"\n      >\n        <:trigger>\n          {{#if this.emojiHtml}}\n            {{this.emojiHtml}}\n          {{else if this.emojiLiteral}}\n            <span class=\"esb-glyph__literal\">{{this.emojiLiteral}}</span>\n          {{else if @icon}}\n            <span class=\"esb-glyphpick__icon\" style={{this.colorStyle}}>\n              {{icon @icon}}\n            </span>\n          {{else}}\n            <span class=\"esb-glyphpick__empty\" title=\"No icon set\"></span>\n          {{/if}}\n        </:trigger>\n        <:content>\n          <div class=\"esb-glyphmenu\">\n            <div class=\"esb-glyphmenu__tabs\">\n              <button\n                type=\"button\"\n                class=\"esb-glyphmenu__tab\n                  {{if (eq this.tab 'icons') 'is-active'}}\"\n                {{on \"click\" (fn this.setTab \"icons\")}}\n              >Icon</button>\n              <button\n                type=\"button\"\n                class=\"esb-glyphmenu__tab\n                  {{if (eq this.tab 'emoji') 'is-active'}}\"\n                {{on \"click\" (fn this.setTab \"emoji\")}}\n              >Emoji</button>\n              {{#if (or @icon @emoji)}}\n                <button\n                  type=\"button\"\n                  class=\"esb-glyphmenu__remove\"\n                  title=\"Remove icon/emoji\"\n                  {{on \"click\" this.clearGlyph}}\n                >\n                  {{icon \"xmark\"}}\n                  Remove\n                </button>\n              {{/if}}\n            </div>\n\n            {{#if (eq this.tab \"icons\")}}\n              {{#if @showColor}}\n                <div class=\"esb-glyphmenu__colorrow\">\n                  {{icon \"palette\"}}\n                  <span class=\"esb-glyphmenu__colorlabel\">Icon color</span>\n                  {{#if @icon}}\n                    <span\n                      class=\"esb-glyphmenu__preview\"\n                      style={{this.colorStyle}}\n                    >{{icon @icon}}</span>\n                  {{/if}}\n                  <input\n                    type=\"color\"\n                    class=\"esb-glyphmenu__swatch\"\n                    value={{this.colorHex}}\n                    aria-label=\"Icon color\"\n                    {{on \"change\" this.setColor}}\n                  />\n                  {{#if @color}}\n                    <button\n                      type=\"button\"\n                      class=\"esb-glyphmenu__colorclear\"\n                      title=\"Remove color\"\n                      aria-label=\"Remove color\"\n                      {{on \"click\" this.clearColor}}\n                    >{{icon \"xmark\"}}</button>\n                  {{/if}}\n                </div>\n              {{/if}}\n              <DIconGridPickerContent\n                @value={{@icon}}\n                @onSelect={{this.pickIcon}}\n              />\n            {{else}}\n              <EmojiPickerContent\n                @didSelectEmoji={{this.pickEmoji}}\n                @close={{this.closeMenu}}\n                @context=\"topic\"\n              />\n            {{/if}}\n          </div>\n        </:content>\n      </DMenu>\n    </div>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { on } from \"@ember/modifier\";\nimport { service } from \"@ember/service\";\nimport icon from \"discourse/helpers/d-icon\";\nimport DMenu from \"float-kit/components/d-menu\";\nimport { itemMatchesArea } from \"../../lib/areas\";\nimport { itemVisible, normalizeLink } from \"../../lib/links\";\nimport EsbLinkRow from \"./link-row\";\n\n// Popup overflow menu. Used both as a standalone layout block (type\n// \"overflow_menu\") and as a nested row inside a link_group (any link that\n// carries its own `links` array). Rows reuse the same link schema and the\n// same EsbLinkRow as everywhere else; float-kit's DMenu provides outside\n// click + Escape handling, and renders as a modal sheet on mobile.\nexport default class EsbMenu extends Component {\n  @service currentUser;\n\n  get links() {\n    return (this.args.links || [])\n      .map((raw, i) => normalizeLink(raw, i))\n      .filter(Boolean)\n      .filter(\n        (l) =>\n          l.enabled &&\n          itemVisible(l, this.currentUser) &&\n          itemMatchesArea(l, this.args.areaKey)\n      );\n  }\n\n  get identifier() {\n    return `esb-menu-${this.args.id}`;\n  }\n\n  // The DMenu trigger is a button; without this, core's mobile handler would\n  // close the whole drawer when the menu is opened.\n  stopBubble = (event) => {\n    event.stopPropagation();\n  };\n\n  <template>\n    <div class=\"esb-menuwrap\" {{on \"click\" this.stopBubble}}>\n      <DMenu\n        @identifier={{this.identifier}}\n        @modalForMobile={{true}}\n        @triggerClass=\"esb-row esb-menu__trigger\"\n        @ariaLabel={{@label}}\n      >\n        <:trigger>\n          <span class=\"esb-row__glyph\">\n            {{icon (if @icon @icon \"ellipsis\")}}\n          </span>\n          <span class=\"esb-row__label\">{{@label}}</span>\n          <span class=\"esb-menu__chevron\">{{icon \"caret-down\"}}</span>\n        </:trigger>\n        <:content as |menu|>\n          <ul class=\"esb-menu__list\">\n            {{#each this.links key=\"id\" as |link|}}\n              <li>\n                <EsbLinkRow @link={{link}} @afterClick={{menu.close}} />\n              </li>\n            {{/each}}\n          </ul>\n        </:content>\n      </DMenu>\n    </div>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { service } from \"@ember/service\";\nimport { itemMatchesArea } from \"../../lib/areas\";\nimport { itemVisible, normalizeLink } from \"../../lib/links\";\nimport EsbLinkRow from \"./link-row\";\nimport EsbMenu from \"./menu\";\nimport EsbSection from \"./section\";\n\n// A collapsible section of links (layout type \"link_group\"). Any link that\n// carries its own `links` array renders as a popup menu row instead of a\n// plain link — that's how a native-style \"More…\" menu lives inside a group.\nexport default class EsbLinkGroup extends Component {\n  @service currentUser;\n\n  get links() {\n    return (this.args.item.links || [])\n      .map((raw, i) => normalizeLink(raw, i))\n      .filter(Boolean)\n      .filter(\n        (l) =>\n          l.enabled &&\n          itemVisible(l, this.currentUser) &&\n          itemMatchesArea(l, this.args.areaKey)\n      );\n  }\n\n  <template>\n    <EsbSection @item={{@item}}>\n      <ul class=\"esb-list\">\n        {{#each this.links key=\"id\" as |link|}}\n          <li>\n            {{#if link.links}}\n              <EsbMenu\n                @id={{link.id}}\n                @label={{link.label}}\n                @icon={{link.icon}}\n                @links={{link.links}}\n                @areaKey={{@areaKey}}\n              />\n            {{else}}\n              <EsbLinkRow @link={{link}} />\n            {{/if}}\n          </li>\n        {{/each}}\n      </ul>\n    </EsbSection>\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { service } from \"@ember/service\";\nimport getURL from \"discourse/lib/get-url\";\nimport EsbLinkRow from \"./link-row\";\nimport EsbSection from \"./section\";\n\n// Tags section (layout type \"tags\"). Shows the user's sidebar tags when they\n// have configured any, else the site's top tags. Disabled in the default\n// layout; admins flip it on for tag-driven communities.\nexport default class EsbTags extends Component {\n  @service currentUser;\n  @service site;\n\n  get links() {\n    let names;\n    const userTags = this.currentUser?.sidebarTags;\n    if (userTags?.length) {\n      names = userTags.map((t) => t.name);\n    } else {\n      names = this.site.top_tags || [];\n    }\n    return names.map((name) => ({\n      id: `tag-${name}`,\n      label: name,\n      icon: \"tag\",\n      href: `/tag/${name}`,\n      url: getURL(`/tag/${name}`),\n      external: false,\n      newTab: false,\n      match: \"prefix\",\n      badge: null,\n      title: null,\n      links: null,\n    }));\n  }\n\n  // Trailing \"All tags\" row (native parity); item.show_all_link: false hides it.\n  get allLink() {\n    if (this.args.item.show_all_link === false) {\n      return null;\n    }\n    return {\n      id: \"all-tags\",\n      label: \"All tags\",\n      icon: \"tag\",\n      href: \"/tags\",\n      url: getURL(\"/tags\"),\n      external: false,\n      newTab: false,\n      match: \"exact\",\n      badge: null,\n      title: null,\n      links: null,\n    };\n  }\n\n  get shouldRender() {\n    return this.links.length > 0;\n  }\n\n  <template>\n    {{#if this.shouldRender}}\n      <EsbSection @item={{@item}}>\n        <ul class=\"esb-list\">\n          {{#each this.links key=\"id\" as |link|}}\n            <li><EsbLinkRow @link={{link}} /></li>\n          {{/each}}\n          {{#if this.allLink}}\n            <li class=\"esb-all\">\n              <EsbLinkRow @link={{this.allLink}} />\n            </li>\n          {{/if}}\n        </ul>\n      </EsbSection>\n    {{/if}}\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { service } from \"@ember/service\";\nimport didInsert from \"@ember/render-modifiers/modifiers/did-insert\";\nimport { ajax } from \"discourse/lib/ajax\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport { categoryUrl, docCategories, parseIndexTopic } from \"../../lib/areas\";\nimport { normalizeLink } from \"../../lib/links\";\nimport EsbLinkRow from \"./link-row\";\nimport EsbSection from \"./section\";\n\nexport default class EsbWiki extends Component {\n  @service site;\n  @service siteSettings;\n\n  @tracked fetchedSections = {};\n  @tracked fetchState = {};\n\n  get categories() {\n    return docCategories(this.site, this.siteSettings).map((category) => {\n      const rawSections = category.doc_category_index?.length\n        ? category.doc_category_index\n        : this.fetchedSections[category.id] || this.fallbackSections(category);\n      const sections = rawSections.map((section, i) => {\n        const links = (section.links || [])\n          .map((link, j) =>\n            normalizeLink(\n              {\n                id: `wiki-${category.id}-${i}-${j}`,\n                label: link.text,\n                href: link.href,\n                icon: \"book-open\",\n              },\n              j\n            )\n          )\n          .filter(Boolean);\n        return {\n          id: `${category.id}-${i}`,\n          title: section.text,\n          links,\n        };\n      });\n      return {\n        id: category.id,\n        name: category.name,\n        url: categoryUrl(category),\n        sections,\n      };\n    });\n  }\n\n  loadIndexes = () => {\n    for (const category of docCategories(this.site, this.siteSettings)) {\n      if (\n        category.doc_category_index?.length ||\n        !category.doc_index_topic_id ||\n        this.fetchState[category.id]\n      ) {\n        continue;\n      }\n      this.fetchIndexTopic(category);\n    }\n  };\n\n  async fetchIndexTopic(category) {\n    this.fetchState = { ...this.fetchState, [category.id]: \"loading\" };\n    try {\n      const topic = await ajax(`/t/${category.doc_index_topic_id}.json`);\n      const sections = parseIndexTopic(topic);\n      if (sections.length > 0) {\n        this.fetchedSections = {\n          ...this.fetchedSections,\n          [category.id]: sections,\n        };\n        this.fetchState = { ...this.fetchState, [category.id]: \"loaded\" };\n      } else {\n        this.fetchState = { ...this.fetchState, [category.id]: \"failed\" };\n      }\n    } catch {\n      this.fetchState = { ...this.fetchState, [category.id]: \"failed\" };\n    }\n  }\n\n  fallbackSections(category) {\n    if (!category.doc_index_topic_id) {\n      return [];\n    }\n    return [\n      {\n        text: \"Index\",\n        links: [\n          {\n            text: \"Open index\",\n            href: `/t/${category.doc_index_topic_id}`,\n          },\n        ],\n      },\n    ];\n  }\n\n  get shouldRender() {\n    return this.categories.length > 0;\n  }\n\n  visit = (href, event) => {\n    if (/^https?:\\/\\//.test(href) || wantsNewWindow(event)) {\n      return;\n    }\n    event.preventDefault();\n    DiscourseURL.routeTo(href);\n  };\n\n  <template>\n    {{#if this.shouldRender}}\n      <EsbSection @item={{@item}}>\n        <div class=\"esb-wiki\">\n          <span {{didInsert this.loadIndexes}}></span>\n          {{#each this.categories key=\"id\" as |category|}}\n            <a\n              class=\"esb-sub__header esb-sub__link\"\n              href={{category.url}}\n              {{on \"click\" (fn this.visit category.url)}}\n            >\n              <span class=\"esb-sub__title\">{{category.name}}</span>\n            </a>\n\n            {{#each category.sections key=\"id\" as |section|}}\n              <div class=\"esb-wiki__group\">\n                <div class=\"esb-wiki__heading\">{{section.title}}</div>\n                <ul class=\"esb-list\">\n                  {{#each section.links key=\"id\" as |link|}}\n                    <li>\n                      <EsbLinkRow @link={{link}} />\n                    </li>\n                  {{/each}}\n                </ul>\n              </div>\n            {{/each}}\n          {{/each}}\n        </div>\n      </EsbSection>\n    {{/if}}\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { getOwner } from \"@ember/owner\";\nimport { service } from \"@ember/service\";\nimport bodyClass from \"discourse/helpers/body-class\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { ajax } from \"discourse/lib/ajax\";\nimport { popupAjaxError } from \"discourse/lib/ajax-error\";\nimport getURL from \"discourse/lib/get-url\";\nimport didInsert from \"@ember/render-modifiers/modifiers/did-insert\";\nimport didUpdate from \"@ember/render-modifiers/modifiers/did-update\";\nimport { config, parseLayout } from \"../lib/config\";\nimport { itemVisible, normalizeLink } from \"../lib/links\";\nimport EsbCategories from \"./esb/categories\";\nimport EsbAreaLauncher from \"./esb/area-launcher\";\nimport EsbChat from \"./esb/chat\";\nimport EsbDms from \"./esb/dms\";\nimport EsbGlyphPicker from \"./esb/glyph-picker\";\nimport EsbLinkGroup from \"./esb/link-group\";\nimport EsbLinkRow from \"./esb/link-row\";\nimport EsbMenu from \"./esb/menu\";\nimport EsbTags from \"./esb/tags\";\nimport EsbWiki from \"./esb/wiki\";\nimport {\n  areaKeysFor,\n  ensureWikiRouteContext,\n  itemMatchesArea,\n  resolveActiveArea,\n} from \"../lib/areas\";\n\n// Root of the owned sidebar. Mounted via the `before-sidebar-sections` plugin\n// outlet (inside Discourse's sidebar shell, above the native section list),\n// it renders the admin-configured layout in order. The native sections are\n// hidden by CSS gated on the `esb-replace` body class, which this component\n// adds REACTIVELY via {{bodyClass}} — so the owned sidebar automatically\n// steps aside (native admin sidebar returns) whenever a non-main sidebar\n// panel is active, and full native behavior returns the moment the component\n// stops rendering (kill switch, invalid layout, admin routes).\n//\n// The native sidebar footer (interface color selector, keyboard shortcuts,\n// section management) is deliberately NOT replaced — it stays sticky at the\n// bottom, outside this component.\n//\n// EDIT MODE (admins only): the pencil button swaps the sidebar for a layout\n// editor — reorder, rename, show/hide, glyphs (an icon picker limited to the\n// site's actually-available sprite icons + the full emoji picker), and accent\n// /icon colors — for blocks AND the links nested inside groups/menus (two\n// levels). Saves the raw layout back to the theme setting via the admin API;\n// anything deeper (hrefs, visibility rules) lives one click away in the\n// structured objects setting editor.\n\nconst SECTION_COMPONENTS = {\n  area_launcher: EsbAreaLauncher,\n  link_group: EsbLinkGroup,\n  categories: EsbCategories,\n  chat: EsbChat,\n  direct_messages: EsbDms,\n  wiki: EsbWiki,\n  tags: EsbTags,\n};\n\nexport default class OwnedSidebar extends Component {\n  @service currentUser;\n  @service router;\n  @service sidebarState;\n  @service site;\n  @service siteSettings;\n\n  @tracked layout = config.layout;\n  @tracked editMode = false;\n  @tracked draft = null;\n  @tracked saving = false;\n  @tracked wikiRouteVersion = 0;\n\n  optionalService(name) {\n    try {\n      return getOwner(this).lookup(`service:${name}`);\n    } catch {\n      return null;\n    }\n  }\n\n  get chatAvailable() {\n    return Boolean(this.optionalService(\"chat-channels-manager\"));\n  }\n\n  get chat() {\n    return this.optionalService(\"chat\");\n  }\n\n  get inChatPanel() {\n    return (\n      this.site.mobileView &&\n      this.sidebarState.currentPanel?.key === \"chat\" &&\n      (this.router.currentURL || \"\").startsWith(\"/chat\")\n    );\n  }\n\n  get show() {\n    return (\n      config.enabled &&\n      Boolean(this.layout) &&\n      (this.sidebarState.showMainPanel || this.inChatPanel)\n    );\n  }\n\n  get canEdit() {\n    return Boolean(this.currentUser?.admin && config.themeId);\n  }\n\n  get jsonEditorUrl() {\n    return getURL(`/admin/customize/themes/${config.themeId}`);\n  }\n\n  get areaSidebarEnabled() {\n    return this.site.mobileView\n      ? config.areaSidebarMobileEnabled\n      : config.areaSidebarDesktopEnabled;\n  }\n\n  get areaContext() {\n    this.wikiRouteVersion;\n    return {\n      router: this.router,\n      site: this.site,\n      siteSettings: this.siteSettings,\n      currentUser: this.currentUser,\n      chat: this.chat,\n    };\n  }\n\n  get activeArea() {\n    return resolveActiveArea(config.areas, this.areaContext);\n  }\n\n  get activeAreaKey() {\n    return this.areaSidebarEnabled ? this.activeArea?.key : null;\n  }\n\n  get launcherItem() {\n    const key = this.activeAreaKey || \"home\";\n    return {\n      id: `area-launcher-${key}`,\n      type: \"area_launcher\",\n      label: \"Areas\",\n      icon: \"grip\",\n      enabled: true,\n      collapsible: false,\n      areaOnly: true,\n    };\n  }\n\n  get wikiItem() {\n    return {\n      id: \"wiki\",\n      type: \"wiki\",\n      label: \"Wiki\",\n      icon: \"book-open\",\n      enabled: true,\n      collapsible: true,\n      collapsedByDefault: false,\n      area: \"wiki\",\n      areaOnly: true,\n    };\n  }\n\n  get renderedLayout() {\n    const items = this.layout || [];\n    if (!this.areaSidebarEnabled) {\n      return items.filter((item) => !item.areaOnly);\n    }\n\n    const areaKey = this.activeAreaKey;\n    const filtered = items.filter((item) => itemMatchesArea(item, areaKey));\n    const configuredLauncher = filtered.find(\n      (item) => item.type === \"area_launcher\"\n    );\n    const launcher = configuredLauncher || this.launcherItem;\n\n    if (areaKey === \"home\" || this.activeArea?.sidebar === \"launcher\") {\n      return [launcher];\n    }\n\n    if (\n      areaKey === \"wiki\" &&\n      !filtered.some((item) => item.type === \"wiki\")\n    ) {\n      return [this.wikiItem];\n    }\n\n    const dedicated = filtered.some((item) => {\n      return (\n        item.type !== \"divider\" &&\n        item.type !== \"area_launcher\" &&\n        areaKeysFor(item).includes(areaKey)\n      );\n    });\n    const hasLauncher = Boolean(configuredLauncher);\n    const wantsLauncher = areaKey && !dedicated;\n\n    if (wantsLauncher && !hasLauncher) {\n      return [launcher, ...filtered];\n    }\n    return filtered;\n  }\n\n  // The layout, resolved against the current user: disabled/invisible items\n  // dropped, chat-dependent items dropped when the chat plugin is missing,\n  // each remaining item paired with how it renders.\n  entriesFromLayout(layout) {\n    const chatOk = this.chatAvailable;\n    const entries = [];\n    for (const item of layout) {\n      if (!item.enabled || !itemVisible(item, this.currentUser)) {\n        continue;\n      }\n      if (\n        [\"chat\", \"direct_messages\"].includes(item.type) &&\n        (!chatOk || !this.currentUser)\n      ) {\n        continue;\n      }\n      if (item.type === \"divider\") {\n        entries.push({ id: item.id, isDivider: true });\n      } else if (item.type === \"link\") {\n        entries.push({ id: item.id, link: normalizeLink(item, 0) });\n      } else if (item.type === \"overflow_menu\") {\n        entries.push({ id: item.id, isMenu: true, item });\n      } else {\n        const comp = SECTION_COMPONENTS[item.type];\n        if (comp) {\n          entries.push({ id: item.id, comp, item });\n        }\n      }\n    }\n    return entries;\n  }\n\n  get entries() {\n    return this.entriesFromLayout(this.renderedLayout);\n  }\n\n  refreshWikiRouteContext = async () => {\n    if (\n      await ensureWikiRouteContext({\n        router: this.router,\n        site: this.site,\n        siteSettings: this.siteSettings,\n      })\n    ) {\n      this.wikiRouteVersion++;\n    }\n  };\n\n  // --- edit mode -------------------------------------------------------------\n\n  enterEdit = (event) => {\n    event?.stopPropagation();\n    this.draft = JSON.parse(JSON.stringify(config.rawLayout || []));\n    this.editMode = true;\n  };\n\n  cancelEdit = (event) => {\n    event?.stopPropagation();\n    this.editMode = false;\n    this.draft = null;\n  };\n\n  // Draft items are plain (untracked) objects; mutations don't invalidate\n  // Glimmer's render cache. Deep-cloning swaps every identity so the whole\n  // editor repaints. Cheap at sidebar scale. (rename() deliberately skips\n  // this so the label input keeps focus while typing.)\n  refresh() {\n    this.draft = JSON.parse(JSON.stringify(this.draft));\n  }\n\n  isEnabled = (item) => {\n    return item.enabled !== false && item.hidden !== true;\n  };\n\n  // Show/hide writes the canonical `hidden` flag (absent = shown — matches\n  // the schema editor's checkbox semantics) and drops the legacy `enabled`.\n  toggleHidden = (item, event) => {\n    event?.stopPropagation();\n    if (this.isEnabled(item)) {\n      item.hidden = true;\n    } else {\n      delete item.hidden;\n    }\n    delete item.enabled;\n    this.refresh();\n  };\n\n  rename = (item, event) => {\n    item.label = event.target.value;\n  };\n\n  // Glyphs: icon and emoji are mutually exclusive — picking one clears the\n  // other. The icon picker's clear button calls onChange(null).\n  setIcon = (item, value) => {\n    if (value) {\n      item.icon = value;\n      delete item.emoji;\n    } else {\n      delete item.icon;\n    }\n    this.refresh();\n  };\n\n  // Color applies to ICONS only (emoji are already colored), so it rides\n  // with the glyph: picking an emoji clears icon + color. Section accent\n  // bars are edited in the structured objects setting, not here.\n  setEmoji = (item, emoji) => {\n    item.emoji = emoji;\n    delete item.icon;\n    delete item.icon_color;\n    this.refresh();\n  };\n\n  showColor = (item) => {\n    return item.type !== \"divider\";\n  };\n\n  setColorValue = (item, value) => {\n    if (value) {\n      item.icon_color = value;\n    } else {\n      delete item.icon_color;\n    }\n    this.refresh();\n  };\n\n  clearGlyph = (item) => {\n    delete item.icon;\n    delete item.emoji;\n    delete item.icon_color;\n    this.refresh();\n  };\n\n  move = (index, delta, event) => {\n    event?.stopPropagation();\n    const next = [...this.draft];\n    const target = index + delta;\n    if (target < 0 || target >= next.length) {\n      return;\n    }\n    [next[index], next[target]] = [next[target], next[index]];\n    this.draft = next;\n  };\n\n  // Reorder for nested links: bi = block index, li = link index; si set when\n  // moving inside a link's own menu.\n  linkMove = (bi, li, si, delta, event) => {\n    event?.stopPropagation();\n    const arr =\n      si === null || si === undefined\n        ? this.draft[bi].links\n        : this.draft[bi].links[li].links;\n    const idx = si === null || si === undefined ? li : si;\n    const target = idx + delta;\n    if (target < 0 || target >= arr.length) {\n      return;\n    }\n    [arr[idx], arr[target]] = [arr[target], arr[idx]];\n    this.refresh();\n  };\n\n  stopBubble = (event) => {\n    event.stopPropagation();\n  };\n\n  save = async (event) => {\n    event?.stopPropagation();\n    this.saving = true;\n    const value = JSON.stringify(this.draft, null, 2);\n    try {\n      await ajax(`/admin/themes/${config.themeId}/setting`, {\n        type: \"PUT\",\n        data: { name: \"layout\", value },\n      });\n      const { layout, raw, error } = parseLayout(value);\n      if (!error) {\n        config.layout = layout;\n        config.rawLayout = raw;\n        this.layout = layout;\n      }\n      this.editMode = false;\n      this.draft = null;\n    } catch (e) {\n      popupAjaxError(e);\n    } finally {\n      this.saving = false;\n    }\n  };\n\n  <template>\n    {{#if this.show}}\n      {{bodyClass \"esb-active\"}}\n      {{#if config.replaceNative}}\n        {{bodyClass \"esb-replace\"}}\n      {{/if}}\n      <div\n        class=\"esb-root {{if this.editMode 'is-editing'}}\"\n        {{didInsert this.refreshWikiRouteContext}}\n        {{didUpdate this.refreshWikiRouteContext this.router.currentURL}}\n      >\n        {{#if this.editMode}}\n          <div class=\"esb-editor\" {{on \"click\" this.stopBubble}}>\n            <div class=\"esb-editor__bar\">\n              <span class=\"esb-editor__title\">Sidebar layout</span>\n              <button\n                type=\"button\"\n                class=\"esb-editor__save\"\n                disabled={{this.saving}}\n                {{on \"click\" this.save}}\n              >\n                {{icon \"check\"}}\n                {{if this.saving \"Saving…\" \"Save\"}}\n              </button>\n              <button\n                type=\"button\"\n                class=\"esb-editor__cancel\"\n                title=\"Discard changes\"\n                {{on \"click\" this.cancelEdit}}\n              >{{icon \"xmark\"}}</button>\n            </div>\n\n            <ul class=\"esb-editor__list\">\n              {{#each this.draft as |item index|}}\n                <li\n                  class=\"esb-editor__block\n                    {{unless (this.isEnabled item) 'is-disabled'}}\"\n                >\n                  <div class=\"esb-editor__row\">\n                    <input\n                      type=\"text\"\n                      class=\"esb-editor__label\"\n                      value={{item.label}}\n                      placeholder={{item.id}}\n                      aria-label=\"Block label\"\n                      {{on \"input\" (fn this.rename item)}}\n                    />\n                    <button\n                      type=\"button\"\n                      class=\"esb-editor__btn\"\n                      title=\"Move up\"\n                      aria-label=\"Move up\"\n                      disabled={{if index false true}}\n                      {{on \"click\" (fn this.move index -1)}}\n                    >{{icon \"caret-up\"}}</button>\n                    <button\n                      type=\"button\"\n                      class=\"esb-editor__btn\"\n                      title=\"Move down\"\n                      aria-label=\"Move down\"\n                      {{on \"click\" (fn this.move index 1)}}\n                    >{{icon \"caret-down\"}}</button>\n                    <button\n                      type=\"button\"\n                      class=\"esb-editor__btn\"\n                      title={{if (this.isEnabled item) \"Hide block\" \"Show block\"}}\n                      aria-label={{if\n                        (this.isEnabled item)\n                        \"Hide block\"\n                        \"Show block\"\n                      }}\n                      {{on \"click\" (fn this.toggleHidden item)}}\n                    >{{icon (if (this.isEnabled item) \"eye\" \"eye-slash\")}}</button>\n                  </div>\n                  <div class=\"esb-editor__meta\">\n                    <span class=\"esb-editor__type\">{{item.type}}</span>\n                    <EsbGlyphPicker\n                      @icon={{item.icon}}\n                      @emoji={{item.emoji}}\n                      @color={{item.icon_color}}\n                      @showColor={{this.showColor item}}\n                      @onIcon={{fn this.setIcon item}}\n                      @onEmoji={{fn this.setEmoji item}}\n                      @onColor={{fn this.setColorValue item}}\n                      @onClear={{fn this.clearGlyph item}}\n                    />\n                  </div>\n\n                  {{#if item.links}}\n                    <ul class=\"esb-editor__links\">\n                      {{#each item.links as |link linkIdx|}}\n                        <li>\n                          <div\n                            class=\"esb-editor__linkrow\n                              {{unless (this.isEnabled link) 'is-disabled'}}\"\n                          >\n                            <EsbGlyphPicker\n                              @icon={{link.icon}}\n                              @emoji={{link.emoji}}\n                              @color={{link.icon_color}}\n                              @showColor={{this.showColor link}}\n                              @onIcon={{fn this.setIcon link}}\n                              @onEmoji={{fn this.setEmoji link}}\n                              @onColor={{fn this.setColorValue link}}\n                              @onClear={{fn this.clearGlyph link}}\n                            />\n                            <input\n                              type=\"text\"\n                              class=\"esb-editor__label\"\n                              value={{link.label}}\n                              placeholder=\"label\"\n                              aria-label=\"Link label\"\n                              {{on \"input\" (fn this.rename link)}}\n                            />\n                            <button\n                              type=\"button\"\n                              class=\"esb-editor__btn\"\n                              title=\"Move up\"\n                              aria-label=\"Move up\"\n                              {{on \"click\" (fn this.linkMove index linkIdx null -1)}}\n                            >{{icon \"caret-up\"}}</button>\n                            <button\n                              type=\"button\"\n                              class=\"esb-editor__btn\"\n                              title=\"Move down\"\n                              aria-label=\"Move down\"\n                              {{on \"click\" (fn this.linkMove index linkIdx null 1)}}\n                            >{{icon \"caret-down\"}}</button>\n                            <button\n                              type=\"button\"\n                              class=\"esb-editor__btn\"\n                              title={{if\n                                (this.isEnabled link)\n                                \"Hide link\"\n                                \"Show link\"\n                              }}\n                              aria-label={{if\n                                (this.isEnabled link)\n                                \"Hide link\"\n                                \"Show link\"\n                              }}\n                              {{on \"click\" (fn this.toggleHidden link)}}\n                            >{{icon\n                                (if (this.isEnabled link) \"eye\" \"eye-slash\")\n                              }}</button>\n                          </div>\n                          {{#if link.links}}\n                            <ul class=\"esb-editor__links is-nested\">\n                              {{#each link.links as |sub subIdx|}}\n                                <li>\n                                  <div\n                                    class=\"esb-editor__linkrow\n                                      {{unless\n                                        (this.isEnabled sub)\n                                        'is-disabled'\n                                      }}\"\n                                  >\n                                    <EsbGlyphPicker\n                                      @icon={{sub.icon}}\n                                      @emoji={{sub.emoji}}\n                                      @color={{sub.icon_color}}\n                                      @showColor={{this.showColor sub}}\n                                      @onIcon={{fn this.setIcon sub}}\n                                      @onEmoji={{fn this.setEmoji sub}}\n                                      @onColor={{fn this.setColorValue sub}}\n                                      @onClear={{fn this.clearGlyph sub}}\n                                    />\n                                    <input\n                                      type=\"text\"\n                                      class=\"esb-editor__label\"\n                                      value={{sub.label}}\n                                      placeholder=\"label\"\n                                      aria-label=\"Link label\"\n                                      {{on \"input\" (fn this.rename sub)}}\n                                    />\n                                    <button\n                                      type=\"button\"\n                                      class=\"esb-editor__btn\"\n                                      title=\"Move up\"\n                                      aria-label=\"Move up\"\n                                      {{on\n                                        \"click\"\n                                        (fn this.linkMove index linkIdx subIdx -1)\n                                      }}\n                                    >{{icon \"caret-up\"}}</button>\n                                    <button\n                                      type=\"button\"\n                                      class=\"esb-editor__btn\"\n                                      title=\"Move down\"\n                                      aria-label=\"Move down\"\n                                      {{on\n                                        \"click\"\n                                        (fn this.linkMove index linkIdx subIdx 1)\n                                      }}\n                                    >{{icon \"caret-down\"}}</button>\n                                    <button\n                                      type=\"button\"\n                                      class=\"esb-editor__btn\"\n                                      title={{if\n                                        (this.isEnabled sub)\n                                        \"Hide link\"\n                                        \"Show link\"\n                                      }}\n                                      aria-label={{if\n                                        (this.isEnabled sub)\n                                        \"Hide link\"\n                                        \"Show link\"\n                                      }}\n                                      {{on \"click\" (fn this.toggleHidden sub)}}\n                                    >{{icon\n                                        (if\n                                          (this.isEnabled sub)\n                                          \"eye\"\n                                          \"eye-slash\"\n                                        )\n                                      }}</button>\n                                  </div>\n                                </li>\n                              {{/each}}\n                            </ul>\n                          {{/if}}\n                        </li>\n                      {{/each}}\n                    </ul>\n                  {{/if}}\n                </li>\n              {{/each}}\n            </ul>\n\n            <a\n              class=\"esb-editor__json\"\n              href={{this.jsonEditorUrl}}\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n            >\n              {{icon \"arrow-up-right-from-square\"}}\n              Full structured editor (links, visibility &amp; more)\n            </a>\n          </div>\n        {{else}}\n          {{#if this.canEdit}}\n            <button\n              type=\"button\"\n              class=\"esb-edit-toggle\"\n              title=\"Edit sidebar layout\"\n              aria-label=\"Edit sidebar layout\"\n              {{on \"click\" this.enterEdit}}\n            >{{icon \"pencil\"}}</button>\n          {{/if}}\n\n          {{#each this.entries key=\"id\" as |e|}}\n            {{#if e.isDivider}}\n              <hr class=\"esb-divider\" />\n            {{else if e.link}}\n              <div class=\"esb-standalone\">\n                <EsbLinkRow @link={{e.link}} />\n              </div>\n            {{else if e.isMenu}}\n              <div class=\"esb-standalone\">\n                <EsbMenu\n                  @id={{e.item.id}}\n                  @label={{e.item.label}}\n                  @icon={{e.item.icon}}\n                  @links={{e.item.links}}\n                  @areaKey={{this.activeAreaKey}}\n                />\n              </div>\n            {{else}}\n              <e.comp @item={{e.item}} @areaKey={{this.activeAreaKey}} />\n            {{/if}}\n          {{/each}}\n        {{/if}}\n      </div>\n    {{/if}}\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { on } from \"@ember/modifier\";\nimport { service } from \"@ember/service\";\nimport icon from \"discourse/helpers/d-icon\";\nimport { config } from \"../lib/config\";\nimport OwnedSidebar from \"./owned-sidebar\";\n\nexport default class ChatMenuOverlay extends Component {\n  @service router;\n  @service site;\n\n  @tracked open = false;\n\n  constructor() {\n    super(...arguments);\n    window.addEventListener(\n      \"edgetech-sidebar:toggle-chat-menu\",\n      this.openMenu\n    );\n  }\n\n  willDestroy() {\n    super.willDestroy(...arguments);\n    window.removeEventListener(\n      \"edgetech-sidebar:toggle-chat-menu\",\n      this.openMenu\n    );\n  }\n\n  get show() {\n    return (\n      config.enabled &&\n      config.layout &&\n      this.site.mobileView &&\n      (this.router.currentURL || \"\").startsWith(\"/chat\")\n    );\n  }\n\n  openMenu = () => {\n    if (this.show) {\n      this.open = true;\n    }\n  };\n\n  close = (event) => {\n    event?.preventDefault();\n    this.open = false;\n  };\n\n  panelClick = (event) => {\n    event.stopPropagation();\n    if (event.target?.closest?.(\"a[href]\")) {\n      this.open = false;\n    }\n  };\n\n  <template>\n    {{#if this.show}}\n      {{#if this.open}}\n        <div class=\"esb-chat-menu\" role=\"dialog\" aria-modal=\"true\">\n          <button\n            type=\"button\"\n            class=\"esb-chat-menu__scrim\"\n            aria-label=\"Close menu\"\n            {{on \"click\" this.close}}\n          ></button>\n          <aside class=\"esb-chat-menu__panel\" {{on \"click\" this.panelClick}}>\n            <div class=\"esb-chat-menu__bar\">\n              <span class=\"esb-chat-menu__title\">Menu</span>\n              <button\n                type=\"button\"\n                class=\"esb-chat-menu__close\"\n                title=\"Close menu\"\n                aria-label=\"Close menu\"\n                {{on \"click\" this.close}}\n              >{{icon \"xmark\"}}</button>\n            </div>\n            <OwnedSidebar />\n          </aside>\n        </div>\n      {{/if}}\n    {{/if}}\n  </template>\n}\n","import { apiInitializer } from \"discourse/lib/api\";\nimport { wantsNewWindow } from \"discourse/lib/intercept-click\";\nimport DiscourseURL from \"discourse/lib/url\";\nimport { applyConfig, config } from \"../lib/config\";\nimport AreaFooter from \"../components/area-footer\";\nimport ChatMenuOverlay from \"../components/chat-menu-overlay\";\nimport OwnedSidebar from \"../components/owned-sidebar\";\n\nlet chatBackHandlerInstalled = false;\n\nfunction mobileAreaMode() {\n  return (\n    document.body?.classList.contains(\"mobile-view\") ||\n    document.documentElement?.classList.contains(\"mobile-view\") ||\n    window.matchMedia(\"(max-width: 767px)\").matches\n  );\n}\n\nfunction installChatBackToForumHandler(api) {\n  if (chatBackHandlerInstalled) {\n    return;\n  }\n  chatBackHandlerInstalled = true;\n\n  const decorateChatButton = () => {\n    document.querySelectorAll(\"a.back-to-forum\").forEach((link) => {\n      if (!mobileAreaMode()) {\n        if (link.classList.contains(\"esb-chat-menu-trigger\")) {\n          link.classList.remove(\"esb-chat-menu-trigger\");\n          if (link.dataset.esbOriginalHtml) {\n            link.innerHTML = link.dataset.esbOriginalHtml;\n          }\n          if (link.dataset.esbOriginalTitle) {\n            link.setAttribute(\"title\", link.dataset.esbOriginalTitle);\n          } else {\n            link.removeAttribute(\"title\");\n          }\n          if (link.dataset.esbOriginalAriaLabel) {\n            link.setAttribute(\n              \"aria-label\",\n              link.dataset.esbOriginalAriaLabel\n            );\n          } else {\n            link.removeAttribute(\"aria-label\");\n          }\n        }\n        return;\n      }\n\n      if (!link.dataset.esbOriginalHtml) {\n        link.dataset.esbOriginalHtml = link.innerHTML;\n        link.dataset.esbOriginalTitle = link.getAttribute(\"title\") || \"\";\n        link.dataset.esbOriginalAriaLabel =\n          link.getAttribute(\"aria-label\") || \"\";\n      }\n      link.classList.add(\"esb-chat-menu-trigger\");\n      link.setAttribute(\"title\", \"Menu\");\n      link.setAttribute(\"aria-label\", \"Menu\");\n      if (!link.querySelector(\".esb-chat-menu-trigger__bars\")) {\n        link.replaceChildren();\n        const bars = document.createElement(\"span\");\n        bars.className = \"esb-chat-menu-trigger__bars\";\n        bars.setAttribute(\"aria-hidden\", \"true\");\n        for (let i = 0; i < 3; i++) {\n          bars.appendChild(document.createElement(\"span\"));\n        }\n        const label = document.createElement(\"span\");\n        label.className = \"esb-chat-menu-trigger__label\";\n        label.textContent = \"Menu\";\n        link.append(bars, label);\n      }\n    });\n  };\n\n  document.addEventListener(\n    \"click\",\n    (event) => {\n      const link = event.target?.closest?.(\"a.back-to-forum\");\n      if (!link || wantsNewWindow(event) || !config.enabled || !config.layout) {\n        return;\n      }\n\n      if (window.location.pathname.startsWith(\"/chat\") && mobileAreaMode()) {\n        event.preventDefault();\n        event.stopImmediatePropagation();\n        window.dispatchEvent(\n          new CustomEvent(\"edgetech-sidebar:toggle-chat-menu\")\n        );\n        return;\n      }\n\n      const forum = config.areas.find((area) => area.key === \"forum\");\n      const href = forum?.href || \"/latest\";\n      if (!href || /^https?:\\/\\//.test(href)) {\n        return;\n      }\n\n      event.preventDefault();\n      event.stopImmediatePropagation();\n      DiscourseURL.routeTo(href);\n    },\n    true\n  );\n\n  decorateChatButton();\n  api.onPageChange?.(() => decorateChatButton());\n  if (document.body) {\n    new MutationObserver(decorateChatButton).observe(document.body, {\n      childList: true,\n      subtree: true,\n    });\n  }\n}\n\n// Mounts the owned sidebar inside Discourse's sidebar shell via the\n// `before-sidebar-sections` outlet. The native section list is hidden by CSS\n// gated on a body class the component itself manages reactively (see\n// owned-sidebar.gjs) — so the kill switch, invalid config, and admin routes\n// all restore native behavior with nothing lingering.\n//\n// FAIL SAFE: invalid layout JSON logs an error and leaves the native sidebar\n// completely untouched (PRD requirement).\n\nexport default apiInitializer((api) => {\n  try {\n    applyConfig(settings); // eslint-disable-line no-undef\n\n    // The frontend layout editor saves via /admin/themes/:id/setting and needs\n    // this theme's id. `themePrefix` is a theme-scoped global of the form\n    // (key) => `theme_translations.<id>.<key>`.\n    try {\n      const id = parseInt(themePrefix(\"x\").split(\".\")[1], 10); // eslint-disable-line no-undef\n      config.themeId = isNaN(id) ? null : id;\n    } catch {\n      config.themeId = null;\n    }\n\n    if (!config.enabled) {\n      return;\n    }\n\n    if (!config.layout) {\n      // eslint-disable-next-line no-console\n      console.error(\n        `[edgetech-sidebar] disabled — ${config.layoutError}. The native sidebar is unchanged.`\n      );\n      return;\n    }\n\n    // Optional sidebar widening (SCSS can't read settings). Widens the whole\n    // sidebar column, matching Chat Navigator's behavior.\n    if (config.expandedSidebarWidth) {\n      document.documentElement.style.setProperty(\n        \"--d-sidebar-width\",\n        config.expandedSidebarWidth\n      );\n    }\n\n    api.renderInOutlet(\"before-sidebar-sections\", OwnedSidebar);\n    api.renderInOutlet(\"above-footer\", AreaFooter);\n    api.renderInOutlet(\"above-footer\", ChatMenuOverlay);\n    installChatBackToForumHandler(api);\n  } catch (e) {\n    // eslint-disable-next-line no-console\n    console.error(\"[edgetech-sidebar] failed to boot, native sidebar kept:\", e);\n  }\n});\n","import * as Mod1 from \"./discourse/api-initializers/channel-breadcrumb\";\nimport * as Mod2 from \"./discourse/api-initializers/owned-sidebar\";\nimport * as Mod3 from \"./discourse/components/area-footer\";\nimport * as Mod4 from \"./discourse/components/chat-menu-overlay\";\nimport * as Mod5 from \"./discourse/components/esb/area-launcher\";\nimport * as Mod6 from \"./discourse/components/esb/categories\";\nimport * as Mod7 from \"./discourse/components/esb/chat\";\nimport * as Mod8 from \"./discourse/components/esb/dms\";\nimport * as Mod9 from \"./discourse/components/esb/glyph-picker\";\nimport * as Mod10 from \"./discourse/components/esb/glyph\";\nimport * as Mod11 from \"./discourse/components/esb/link-group\";\nimport * as Mod12 from \"./discourse/components/esb/link-row\";\nimport * as Mod13 from \"./discourse/components/esb/menu\";\nimport * as Mod14 from \"./discourse/components/esb/section\";\nimport * as Mod15 from \"./discourse/components/esb/tags\";\nimport * as Mod16 from \"./discourse/components/esb/wiki\";\nimport * as Mod17 from \"./discourse/components/owned-sidebar\";\nimport * as Mod18 from \"./discourse/lib/area-status\";\nimport * as Mod19 from \"./discourse/lib/areas\";\nimport * as Mod20 from \"./discourse/lib/config\";\nimport * as Mod21 from \"./discourse/lib/links\";\nimport * as Mod22 from \"./discourse/lib/prefs\";\nconst compatModules = {\n  \"discourse/api-initializers/channel-breadcrumb\": Mod1,\n  \"discourse/api-initializers/owned-sidebar\": Mod2,\n  \"discourse/components/area-footer\": Mod3,\n  \"discourse/components/chat-menu-overlay\": Mod4,\n  \"discourse/components/esb/area-launcher\": Mod5,\n  \"discourse/components/esb/categories\": Mod6,\n  \"discourse/components/esb/chat\": Mod7,\n  \"discourse/components/esb/dms\": Mod8,\n  \"discourse/components/esb/glyph-picker\": Mod9,\n  \"discourse/components/esb/glyph\": Mod10,\n  \"discourse/components/esb/link-group\": Mod11,\n  \"discourse/components/esb/link-row\": Mod12,\n  \"discourse/components/esb/menu\": Mod13,\n  \"discourse/components/esb/section\": Mod14,\n  \"discourse/components/esb/tags\": Mod15,\n  \"discourse/components/esb/wiki\": Mod16,\n  \"discourse/components/owned-sidebar\": Mod17,\n  \"discourse/lib/area-status\": Mod18,\n  \"discourse/lib/areas\": Mod19,\n  \"discourse/lib/config\": Mod20,\n  \"discourse/lib/links\": Mod21,\n  \"discourse/lib/prefs\": Mod22,\n};\nexport { compatModules };\nexport default compatModules;\n"],"names":["settings","getObjectForTheme","config","enabled","replaceNative","footerEnabled","areaSidebarMobileEnabled","areaSidebarDesktopEnabled","wikiCategoryIds","wikiIndexTopicIds","wikiHomeUrl","areas","layout","rawLayout","layoutError","themeId","breadcrumb","allowHide","hideMuted","defaultGroupSort","defaultChannelSort","chatGroupsCollapsedByDefault","pinnedCategoryIds","useChannelIcons","showTreeLines","expandedSidebarWidth","KNOWN_TYPES","DEFAULT_AREAS","key","label","icon","href","match","footer","sidebar","logged_in_only","parseNumberList","value","Array","isArray","map","v","parseInt","filter","n","isNaN","String","split","normalizeArea","raw","index","trim","toLowerCase","id","emoji","matchers","Boolean","order","Number","isFinite","hasExplicitArea","area","area_key","parseAreas","input","parsed","JSON","parse","length","i","normalizeItem","console","warn","type","includes","inferred","startsWith","inferredArea","hidden","collapsible","collapsedByDefault","collapsed_by_default","areaOnly","area_only","parseLayout","e","error","message","stringify","applyConfig","owned_sidebar_enabled","replace_native_sections","footer_enabled","area_sidebar_mobile_enabled","area_sidebar_desktop_enabled","wiki_category_ids","wiki_index_topic_ids","wiki_home_url","chat_breadcrumb_enabled","allow_hide_channels","hide_muted_channels","default_group_sort","default_channel_sort","chat_groups_collapsed_by_default","use_channel_icons","show_tree_lines","width","expanded_sidebar_width","test","pinned_category_ids","s","BREADCRUMB_CLASS","STACK_CLASS","TITLE_SELECTOR","crumb","text","color","a","document","createElement","className","textContent","style","setProperty","renderBreadcrumb","navbar","channel","chatableType","chatable","querySelector","remove","stack","querySelectorAll","forEach","title","closest","parentNode","insertBefore","appendChild","ensureStack","existing","dataset","channelId","el","setAttribute","cat","chain","category","guard","unshift","parentCategory","buildChain","leaf","name","pop","crumbCategories","sep","url","channelBreadcrumb","apiInitializer","api","chatService","container","lookup","tryInject","activeChannel","injectPasses","delay","setTimeout","onPageChange","RESOLVE_PRIORITY","Set","wikiIndexLoadState","Map","topicCategoryCache","topicCategoryLoadState","normalizeAreaKeys","areaKeysFor","item","itemMatchesArea","areaKey","includeAlways","keys","areaVisible","currentUser","anonymous_only","staff_only","staff","admin_only","admin","normalizedPath","path","URL","window","location","origin","pathname","replace","categoryUrl","slug","docCategories","site","siteSettings","doc_categories_enabled","categoryId","categories","find","c","indexTopicId","doc_index_topic_id","doc_category_index","docArticleCount","count","categoryCount","sections","section","links","topic_count","wikiHref","configuredHomepage","doc_categories_homepage","first","routeParams","router","currentRoute","params","routeAttrs","attributes","safeGet","obj","get","currentTopicId","attrs","candidates","candidate","pathMatch","currentURL","pathStarts","prefix","cleanPrefix","parseIndexTopic","topic","cooked","post_stream","posts","doc","DOMParser","parseFromString","current","ensureSection","push","node","from","body","children","tag","tagName","link","getAttribute","async","loadConfiguredIndexTopic","topicId","has","promise","ajax","then","ids","topicIdsFromSections","add","set","catch","ensureWikiRouteContext","context","loaders","category_id","loadCurrentTopicCategory","Promise","all","some","defaultMatcher","routeName","currentRouteName","docs","docCategoryIds","categoryIds","byId","parentId","parent_category_id","parent_category","withCategoryAncestors","routePath","category_slug_path_with_id","routeMatch","cachedCategoryId","currentCategoryIds","chat","users","docLinkTopicIds","tokenMatches","token","slice","resolveActiveArea","visible","fallback","priorityIndex","indexOf","sort","b","hrefForArea","getURL","username","encodeURIComponent","chatUnread","channels","reduce","sum","tracking","unreadCount","mentionCount","unreadFor","compactCount","statusForArea","services","chatChannelsManager","publicMessageChannels","dms","directMessageChannels","dmChannels","unread","meta","badge","badgeKind","topicTrackingState","countUnread","forumUnread","fresh","countNew","forumNew","all_unread_notifications_count","EsbGlyph","Component","emojiHtml","this","args","htmlSafe","emojiUnescape","literal","colorStyle","setComponentTemplate","createTemplateFactory","block","moduleName","scope","isStrictMode","AreaFooter","dt7948","g","prototype","service","composer","tracked","wikiRouteVersion","optionalService","getOwner","activeArea","show","mobileView","isOpen","hrefFor","isActive","statusFor","visit","event","wantsNewWindow","preventDefault","DiscourseURL","routeTo","refreshWikiRouteContext","didInsert","didUpdate","on","fn","itemVisible","visibility","logged_in","anonymous","groups","memberOf","visible_groups","memberIds","isExternal","normalizeLink","idx","external","areaKeys","iconColor","icon_color","newTab","new_tab","linkIsActive","target","EsbLinkRow","active","click","afterClick","SECTIONS_KEY","GROUPS_KEY","OVERRIDES_KEY","SORT_KEY","readJson","localStorage","getItem","writeJson","setItem","readMap","legacyKey","loadSections","setSectionCollapsed","itemId","collapsed","next","isSectionCollapsed","defaultCollapsed","loadGroupCollapsed","setGroupCollapsed","groupId","isGroupCollapsed","loadOverrides","setChannelOverride","overrides","state","isEffectivelyHidden","isMuted","mutedHidesByDefault","override","loadSort","defaults","stored","valid","setSort","kind","EsbSection","bodyId","accentStyle","accent","toggle","stopPropagation","or","EsbCategories","rows","exclude","only","maxDepth","isInteger","max_depth","uncatId","uncategorized_category_id","cats","include_uncategorized","isUncategorizedCategory","source","sidebar_category_ids","idSet","byParent","pid","currentPath","walk","depth","position","localeCompare","rowFor","style_type","styleType","catIcon","squircleStyle","monogram","charAt","toUpperCase","iconName","allLink","show_all_link","row","EsbAreaLauncher","EsbChat","showHidden","filterOpen","searchUrl","browseUrl","showBrowse","browse_button","visitUrl","areaMode","showChannels","sectionItem","dmsLabel","includeDms","include_dms","includeThreads","include_threads","hasSubsections","channelsSubId","dmsSubId","channelsCollapsed","filterActive","dmsCollapsed","toggleChannelsSub","toggleDmsSub","channelsLabel","channels_label","dms_label","threadsLabel","threads_label","threadsActive","threadsUnread","unreadThreadsCount","visitThreads","activityOf","dmUnread","dmIsGroup","dmUnreadOf","shouldRender","matchesFilter","rootCategoryFor","msg","lastMessage","Date","createdAt","valueOf","isHidden","currentUserMembership","muted","byRoot","root","group","values","sortChannels","groupMentions","groupUnread","mentions","isCollapsed","activity","Math","max","sortGroups","cached","pinned","pinIndex","Infinity","pa","pb","hiddenCount","headerMentions","totalUnread","glyphFor","isEmoji","isIcon","updateFilter","clearFilter","toggleFilterOpen","focusInput","focus","setSortTo","stopBubble","toggleGroup","toggleChannelVisibility","toggleShowHidden","DMenu","eq","and","groupSwatch","LinkTo","EsbDms","isGroup","EsbGlyphPicker","tab","menuApi","onRegisterApi","closeMenu","close","emojiLiteral","colorHex","setTab","pickIcon","onIcon","pickEmoji","onEmoji","setColor","onColor","clearColor","clearGlyph","onClear","DIconGridPickerContent","EmojiPickerContent","EsbMenu","l","identifier","EsbLinkGroup","EsbTags","names","userTags","sidebarTags","t","top_tags","EsbWiki","fetchedSections","fetchState","fallbackSections","j","loadIndexes","fetchIndexTopic","SECTION_COMPONENTS","area_launcher","link_group","direct_messages","wiki","tags","OwnedSidebar","sidebarState","editMode","draft","saving","chatAvailable","inChatPanel","currentPanel","showMainPanel","canEdit","jsonEditorUrl","areaSidebarEnabled","areaContext","activeAreaKey","launcherItem","wikiItem","renderedLayout","items","filtered","configuredLauncher","launcher","dedicated","hasLauncher","entriesFromLayout","chatOk","entries","isDivider","isMenu","comp","enterEdit","cancelEdit","refresh","isEnabled","toggleHidden","rename","setIcon","setEmoji","showColor","setColorValue","move","delta","linkMove","bi","li","si","arr","save","data","popupAjaxError","bodyClass","ChatMenuOverlay","open","constructor","super","arguments","addEventListener","openMenu","willDestroy","removeEventListener","panelClick","chatBackHandlerInstalled","mobileAreaMode","classList","contains","documentElement","matchMedia","matches","ownedSidebar","renderInOutlet","decorateChatButton","esbOriginalHtml","innerHTML","esbOriginalTitle","esbOriginalAriaLabel","replaceChildren","bars","append","removeAttribute","stopImmediatePropagation","dispatchEvent","CustomEvent","forum","MutationObserver","observe","childList","subtree","installChatBackToForumHandler","compatModules","Mod1","Mod3","Mod4","Mod5","Mod6","Mod7","Mod8","Mod9","Mod10","Mod11","Mod12","Mod13","Mod14","Mod15","Mod16","Mod17","Mod18","Mod19","Mod20","Mod21","Mod22"],"mappings":"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"}