web-mojo 2.4.5 → 2.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.es.js +1 -1
  4. package/dist/auth.cjs.js +1 -1
  5. package/dist/auth.es.js +1 -1
  6. package/dist/charts.cjs.js +1 -1
  7. package/dist/charts.es.js +1 -1
  8. package/dist/chunks/{AssistantPanelView-DlBgWeM_.js → AssistantPanelView-DmlaVMvK.js} +2 -2
  9. package/dist/chunks/{AssistantPanelView-DlBgWeM_.js.map → AssistantPanelView-DmlaVMvK.js.map} +1 -1
  10. package/dist/chunks/{AssistantPanelView-C4ZyIQoH.js → AssistantPanelView-rkVX6wky.js} +2 -2
  11. package/dist/chunks/{AssistantPanelView-C4ZyIQoH.js.map → AssistantPanelView-rkVX6wky.js.map} +1 -1
  12. package/dist/chunks/ChatView-D2WOSxPu.js +2 -0
  13. package/dist/chunks/ChatView-D2WOSxPu.js.map +1 -0
  14. package/dist/chunks/ChatView-kWguc444.js +2 -0
  15. package/dist/chunks/ChatView-kWguc444.js.map +1 -0
  16. package/dist/chunks/Passkeys-B1-Z4-16.js +2 -0
  17. package/dist/chunks/Passkeys-B1-Z4-16.js.map +1 -0
  18. package/dist/chunks/Passkeys-BlHx11-5.js +2 -0
  19. package/dist/chunks/Passkeys-BlHx11-5.js.map +1 -0
  20. package/dist/chunks/{TicketPanelView-if4ogL_D.js → TicketPanelView-BGQZ6q2B.js} +2 -2
  21. package/dist/chunks/{TicketPanelView-if4ogL_D.js.map → TicketPanelView-BGQZ6q2B.js.map} +1 -1
  22. package/dist/chunks/{TicketPanelView-DeeANyKv.js → TicketPanelView-BZ8e66o1.js} +2 -2
  23. package/dist/chunks/{TicketPanelView-DeeANyKv.js.map → TicketPanelView-BZ8e66o1.js.map} +1 -1
  24. package/dist/chunks/{UserProfileView-BPjz7uIp.js → UserProfileView-DDflzpTa.js} +2 -2
  25. package/dist/chunks/{UserProfileView-BPjz7uIp.js.map → UserProfileView-DDflzpTa.js.map} +1 -1
  26. package/dist/chunks/{UserProfileView-udbSWe1S.js → UserProfileView-cUF8ED9n.js} +2 -2
  27. package/dist/chunks/{UserProfileView-udbSWe1S.js.map → UserProfileView-cUF8ED9n.js.map} +1 -1
  28. package/dist/chunks/{admin-C3-HqQvC.js → admin-CmyeTstQ.js} +2 -2
  29. package/dist/chunks/{admin-C3-HqQvC.js.map → admin-CmyeTstQ.js.map} +1 -1
  30. package/dist/chunks/{admin-DrFBTXnB.js → admin-D6gHOojO.js} +2 -2
  31. package/dist/chunks/{admin-DrFBTXnB.js.map → admin-D6gHOojO.js.map} +1 -1
  32. package/dist/chunks/{index-dFX91Gwz.js → index-Cxffar1o.js} +2 -2
  33. package/dist/chunks/{index-dFX91Gwz.js.map → index-Cxffar1o.js.map} +1 -1
  34. package/dist/chunks/{index-Byf1pa3Z.js → index-DsID1QpB.js} +2 -2
  35. package/dist/chunks/{index-Byf1pa3Z.js.map → index-DsID1QpB.js.map} +1 -1
  36. package/dist/chunks/{version-CjNfmLdK.js → version-DdqQDDOW.js} +2 -2
  37. package/dist/chunks/{version-CjNfmLdK.js.map → version-DdqQDDOW.js.map} +1 -1
  38. package/dist/chunks/{version-C9vC8_Oy.js → version-NEBZhkhG.js} +2 -2
  39. package/dist/chunks/{version-C9vC8_Oy.js.map → version-NEBZhkhG.js.map} +1 -1
  40. package/dist/docit.cjs.js +1 -1
  41. package/dist/docit.es.js +1 -1
  42. package/dist/index.cjs.js +1 -1
  43. package/dist/index.es.js +1 -1
  44. package/dist/lightbox.cjs.js +1 -1
  45. package/dist/lightbox.es.js +1 -1
  46. package/dist/user-profile.cjs.js +1 -1
  47. package/dist/user-profile.es.js +1 -1
  48. package/dist/web-mojo.lite.iife.js +13 -0
  49. package/dist/web-mojo.lite.iife.js.map +1 -1
  50. package/dist/web-mojo.lite.iife.min.js +44 -44
  51. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/chunks/ChatView-CcR1GR30.js +0 -2
  54. package/dist/chunks/ChatView-CcR1GR30.js.map +0 -1
  55. package/dist/chunks/ChatView-ChyTcmrd.js +0 -2
  56. package/dist/chunks/ChatView-ChyTcmrd.js.map +0 -1
  57. package/dist/chunks/Passkeys-DJn9yy-0.js +0 -2
  58. package/dist/chunks/Passkeys-DJn9yy-0.js.map +0 -1
  59. package/dist/chunks/Passkeys-WWAg5tKf.js +0 -2
  60. package/dist/chunks/Passkeys-WWAg5tKf.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TicketPanelView-if4ogL_D.js","sources":["../../src/extensions/admin/incidents/ActionCardView.js","../../src/extensions/admin/incidents/TicketPanelView.js"],"sourcesContent":["import View from '@core/View.js';\nimport Modal from '@core/views/feedback/Modal.js';\n\nfunction esc(s) {\n if (!s) return '';\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nconst ALLOWED_REFS = new Set([\n 'incident.RuleSet', 'incident.Incident', 'incident.Event',\n 'incident.Ticket', 'account.GeoLocatedIP'\n]);\n\nconst HANDLER_COLORS = {\n 'incident.rule_approval': { dot: 'accent', label: 'Rule' },\n 'incident.block_confirm': { dot: 'red', label: 'Block' },\n 'incident.rule_update': { dot: 'green', label: 'Update' },\n 'incident.escalate': { dot: 'amber', label: 'Escalate' }\n};\n\nclass ActionCardView extends View {\n constructor(options = {}) {\n super({\n className: 'action-card-view',\n ...options\n });\n this.action = options.action;\n this.noteId = options.noteId;\n this.ticketStatus = options.ticketStatus;\n }\n\n get isResolved() {\n return !!this.action?.resolved;\n }\n\n get isContext() {\n return this.action?.type === 'context';\n }\n\n get isClosed() {\n return this.ticketStatus === 'closed' || this.ticketStatus === 'resolved';\n }\n\n get handlerConfig() {\n return HANDLER_COLORS[this.action?.handler] || { dot: 'accent', label: 'Action' };\n }\n\n onBeforeRender() {\n const cfg = this.handlerConfig;\n this.dotClass = cfg.dot;\n\n if (this.isContext) {\n this._buildContextTemplate();\n } else if (this.isResolved) {\n this._buildResolvedTemplate();\n } else {\n this._buildPendingTemplate();\n }\n }\n\n onActionToggleCompact() {\n const acEl = this.element?.querySelector('.ac.resolved');\n if (acEl) acEl.classList.toggle('compact');\n }\n\n _buildContextTemplate() {\n const refs = (this.action.references || []).filter(ref => ALLOWED_REFS.has(ref.model));\n const refItems = refs.map(ref => {\n const label = esc(ref.label) || `${esc(ref.model.split('.').pop())} #${esc(ref.pk)}`;\n return `<span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(ref.model)}\" data-pk=\"${esc(ref.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }).join('');\n\n this.template = `\n <div class=\"ac ac-context\">\n <div class=\"ac-top\">\n <span class=\"ac-dot context\"></span>\n <span class=\"ac-label\">Referenced models</span>\n </div>\n <div class=\"ac-detail\">${refItems}</div>\n </div>\n `;\n }\n\n _buildResolvedTemplate() {\n const resolution = this.action.resolution || 'approved';\n const badgeClass = resolution === 'approved' ? 'approved' : 'denied';\n const badgeText = resolution === 'approved' ? 'Approved' : 'Denied';\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<div class=\"ac-detail\"><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span></div>`;\n }\n this.template = `\n <div class=\"ac resolved compact\">\n <div class=\"ac-top\" data-action=\"toggle-compact\" title=\"Click to toggle\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n <span class=\"ac-badge ${badgeClass}\">${badgeText}</span>\n <i class=\"bi bi-chevron-down ac-chevron\"></i>\n </div>\n ${refHtml}\n </div>\n `;\n }\n\n _buildPendingTemplate() {\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<br><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }\n const detail = esc(this.action.context?.detail);\n const disabledAttr = this.isClosed ? ' disabled' : '';\n this.template = `\n <div class=\"ac\">\n <div class=\"ac-top\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n </div>\n <div class=\"ac-detail\">${detail}${refHtml}</div>\n <div class=\"ac-foot\">\n <button class=\"btn-approve\" data-action=\"approve\"${disabledAttr}>Approve</button>\n <button class=\"btn-deny\" data-action=\"deny\"${disabledAttr}>Deny</button>\n </div>\n </div>\n `;\n }\n\n async onActionOpenRef(_event, el) {\n const modelRef = el.dataset.model;\n const pk = el.dataset.pk;\n if (!ALLOWED_REFS.has(modelRef) || !/^\\d+$/.test(pk)) return;\n const app = this.getApp();\n const ModelClass = app?.getModelByRef(modelRef);\n if (ModelClass?.VIEW_CLASS) {\n Modal.showModel(new ModelClass({ id: pk }));\n }\n }\n\n onActionApprove() {\n this.emit('action:respond', { noteId: this.noteId, action: 'approve', handler: this.action.handler, context: this.action.context });\n }\n\n onActionDeny() {\n this.emit('action:respond', { noteId: this.noteId, action: 'deny', handler: this.action.handler, context: this.action.context });\n }\n}\n\nexport { HANDLER_COLORS };\nexport default ActionCardView;\n","import View from '@core/View.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Modal from '@core/views/feedback/Modal.js';\nimport { Ticket, TicketForms, TicketCategories } from '@ext/admin/models/Tickets.js';\nimport { Incident } from '@ext/admin/models/Incident.js';\nimport { GroupList } from '@core/models/Group.js';\nimport { UserList } from '@core/models/User.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport ActionCardView from './ActionCardView.js';\nimport { openAssistantChat } from '../assistant/AssistantContextChat.js';\nimport rest from '@core/Rest.js';\n\n\nconst STATUS_OPTIONS = ['new', 'open', 'in_progress', 'pending', 'resolved', 'qa', 'closed', 'ignored'];\nconst STATUS_PILL = {\n new: 'pill-new',\n open: 'pill-open',\n in_progress: 'pill-prog',\n pending: 'pill-prog',\n resolved: 'pill-resolved',\n qa: 'pill-open',\n closed: 'pill-closed',\n ignored: 'pill-closed'\n};\nconst PRIORITY_OPTIONS = [\n { value: 10, label: 'P10 — Critical' },\n { value: 9, label: 'P9 — Severe' },\n { value: 8, label: 'P8 — High' },\n { value: 7, label: 'P7 — Elevated' },\n { value: 5, label: 'P5 — Normal' },\n { value: 3, label: 'P3 — Low' },\n { value: 1, label: 'P1 — Info' }\n];\n\nclass TicketPanelView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-panel-view',\n ...options\n });\n this.model = options.model || new Ticket(options.data || {});\n }\n\n onBeforeRender() {\n const status = this.model.get('status') || 'new';\n this.statusPill = STATUS_PILL[status] || 'pill-closed';\n this.statusLabel = (status || '').replace(/_/g, ' ');\n this.priorityLabel = `P${this.model.get('priority') || 5}`;\n this.assigneeName = this.model.get('assignee.display_name') || this.model.get('assignee') || 'Unassigned';\n this.categoryLabel = this.model.get('category') || 'ticket';\n this.groupName = this.model.get('group.name') || this.model.get('group') || 'None';\n this.hasDescription = !!this.model.get('description');\n this.hasIncident = !!(this.model.get('incident') && typeof this.model.get('incident') === 'object' && this.model.get('incident').id);\n this.priorityColor = (this.model.get('priority') || 5) >= 7 ? 'var(--bs-danger)' : 'var(--bs-secondary-color)';\n\n this.template = `\n <style>\n .ticket-panel-view { height: 100%; display: flex; flex-direction: column; }\n .tp-header { padding: 10px 16px 6px; border-bottom: 1px solid var(--bs-border-color-translucent); flex-shrink: 0; }\n .tp-title-row { display: flex; align-items: flex-start; gap: 6px; }\n .tp-title { font-size: 0.88rem; font-weight: 600; color: var(--bs-emphasis-color); line-height: 1.3; flex: 1; min-width: 0; cursor: ${this.hasDescription ? 'pointer' : 'default'}; transition: color 0.12s; }\n ${this.hasDescription ? '.tp-title:hover { color: var(--bs-primary); }' : ''}\n .tp-title i { font-size: 0.6rem; vertical-align: middle; margin-left: 3px; opacity: 0; transition: opacity 0.12s; }\n .tp-title:hover i { opacity: 0.6; }\n .tp-btns { display: flex; gap: 2px; align-items: center; flex-shrink: 0; }\n .tp-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: none; background: none; color: var(--bs-secondary-color); border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.12s; }\n .tp-btn:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }\n .tp-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n .tp-id { font-family: var(--bs-font-monospace); font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.66rem; font-weight: 500; cursor: pointer; transition: filter 0.12s; }\n .tp-pill:hover { filter: brightness(0.9); }\n .tp-pill-new { background: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }\n .tp-pill-open { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-pill-prog { background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }\n .tp-pill-closed { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }\n .tp-pill-resolved { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-time { font-size: 0.66rem; color: var(--bs-secondary-color); }\n .tp-desc-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--bs-primary); cursor: pointer; padding: 2px 8px; border-radius: 5px; background: rgba(var(--bs-primary-rgb), 0.08); transition: all 0.12s; }\n .tp-desc-chip:hover { background: rgba(var(--bs-primary-rgb), 0.16); }\n .tp-desc-chip i { font-size: 0.7rem; }\n .tp-meta { display: flex; align-items: center; gap: 3px; margin-top: 5px; }\n .tp-fields { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }\n .tp-field { display: inline-flex; align-items: center; gap: 4px; font-size: 0.72rem; color: var(--bs-secondary-color); padding: 2px 7px; border-radius: 5px; cursor: pointer; transition: all 0.12s; border: 1px solid transparent; }\n .tp-field:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }\n .tp-field i { font-size: 0.68rem; }\n .tp-field .caret { font-size: 0.55rem; opacity: 0; transition: opacity 0.12s; margin-left: -1px; }\n .tp-field:hover .caret { opacity: 0.6; }\n .tp-sep { color: var(--bs-secondary-color); font-size: 0.6rem; margin: 0 1px; user-select: none; }\n\n .tp-linked { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--bs-border-color-translucent); font-size: 0.75rem; color: var(--bs-secondary-color); flex-shrink: 0; }\n .tp-linked i { color: var(--bs-warning); font-size: 0.72rem; }\n .tp-linked a { color: var(--bs-primary); text-decoration: none; font-weight: 500; }\n .tp-linked a:hover { text-decoration: underline; }\n .tp-linked .lpill { font-size: 0.62rem; padding: 0 5px; border-radius: 3px; background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); font-weight: 500; }\n\n .tp-conv { flex: 1; overflow-y: auto; min-height: 0; }\n .tp-conv .chat-container { border: none; border-radius: 0; }\n .tp-conv .chat-messages { padding: 6px 0; }\n .tp-conv .chat-input-wrapper { display: none; }\n .tp-conv .message-item { position: relative; }\n .tp-conv .tp-edit-btn { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; display: none; align-items: center; justify-content: center; border: none; background: var(--bs-tertiary-bg); color: var(--bs-secondary-color); border-radius: 5px; cursor: pointer; font-size: 0.72rem; transition: all 0.12s; }\n .tp-conv .tp-edit-btn:hover { background: var(--bs-secondary-bg); color: var(--bs-body-color); }\n .tp-conv .message-item:hover .tp-edit-btn { display: flex; }\n .tp-conv .message-text { white-space: normal; }\n .tp-conv .message-text p { margin-bottom: 6px; }\n .tp-conv .message-text p:last-child { margin-bottom: 0; }\n .tp-conv .message-text h1,\n .tp-conv .message-text h2,\n .tp-conv .message-text h3,\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-weight: 600; margin-top: 10px; margin-bottom: 4px; line-height: 1.3; }\n .tp-conv .message-text h1 { font-size: 1.05rem; }\n .tp-conv .message-text h2 { font-size: 1rem; }\n .tp-conv .message-text h3 { font-size: 0.95rem; }\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-size: 0.88rem; }\n .tp-conv .message-text h1:first-child,\n .tp-conv .message-text h2:first-child,\n .tp-conv .message-text h3:first-child { margin-top: 0; }\n .tp-conv .message-text hr { margin: 4px 0; opacity: 0.15; }\n .tp-conv .message-text ul,\n .tp-conv .message-text ol { padding-left: 20px; margin-top: 2px; margin-bottom: 6px; }\n .tp-conv .message-text li { margin-bottom: 2px; }\n .tp-conv .message-text pre { background: var(--bs-tertiary-bg); border-radius: 6px; padding: 10px 14px; margin: 8px 0; font-size: 0.8rem; overflow-x: auto; }\n .tp-conv .message-text code { font-size: 0.85em; padding: 1px 5px; background: var(--bs-tertiary-bg); border-radius: 4px; }\n .tp-conv .message-text pre code { padding: 0; background: none; }\n .tp-conv .message-text table { width: 100%; margin: 8px 0; border-collapse: collapse; font-size: 0.82rem; }\n .tp-conv .message-text th,\n .tp-conv .message-text td { padding: 5px 8px; border: 1px solid var(--bs-border-color); text-align: left; }\n .tp-conv .message-text th { background: var(--bs-tertiary-bg); font-weight: 600; }\n .tp-conv .message-text blockquote { margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--bs-border-color); color: var(--bs-secondary-color); }\n\n .tp-action-area { padding: 0; }\n\n .tp-input { border-top: 1px solid var(--bs-border-color-translucent); padding: 10px 16px; flex-shrink: 0; }\n .tp-input-wrap { display: flex; align-items: flex-end; gap: 8px; }\n .tp-input textarea { flex: 1; font-size: 0.8rem; border: 1px solid var(--bs-border-color); border-radius: 8px; padding: 7px 10px; resize: none; background: var(--bs-body-bg); color: var(--bs-body-color); outline: none; transition: border-color 0.15s; font-family: inherit; max-height: 160px; overflow-y: auto; }\n .tp-input textarea:focus { border-color: var(--bs-primary); }\n .tp-input textarea::placeholder { color: var(--bs-secondary-color); }\n .tp-send-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: var(--bs-primary); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 0.85rem; transition: filter 0.12s; }\n .tp-send-btn:hover { filter: brightness(1.1); }\n .tp-input-hint { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-input-hint i { font-size: 0.75rem; }\n .tp-input.tp-dragover { background: rgba(var(--bs-primary-rgb), 0.04); }\n .tp-input.tp-dragover textarea { border-color: var(--bs-primary); border-style: dashed; }\n .tp-attachments { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }\n .tp-attach-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 6px; font-size: 0.72rem; color: var(--bs-body-color); }\n .tp-attach-chip i { font-size: 0.68rem; }\n .tp-attach-chip .remove { cursor: pointer; color: var(--bs-secondary-color); margin-left: 2px; }\n .tp-attach-chip .remove:hover { color: var(--bs-danger); }\n .tp-attach-chip.uploading { opacity: 0.6; }\n .tp-conv .message-content.tp-collapsed { max-height: var(--tp-collapse-h, 52px); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); mask-image: linear-gradient(to bottom, black 60%, transparent 100%); }\n .tp-conv .message-item:has(.tp-show-more) { flex-wrap: wrap; }\n .tp-show-more { background: none; border: none; padding: 2px 0; margin: -2px 0 0 48px; font-size: 0.7rem; color: var(--bs-secondary-color); cursor: pointer; text-align: left; width: calc(100% - 48px); }\n .tp-show-more:hover { color: var(--bs-body-color); }\n </style>\n\n <div class=\"tp-header\">\n <div class=\"tp-title-row\">\n <div class=\"tp-title\" ${this.hasDescription ? 'data-action=\"show-description\"' : ''} ${this.hasDescription ? 'title=\"View full description\"' : ''}>\n {{model.title}}${this.hasDescription ? ' <i class=\"bi bi-arrow-up-right-square\"></i>' : ''}\n </div>\n <div class=\"tp-btns\">\n <div data-container=\"panel-menu\"></div>\n </div>\n </div>\n <div class=\"tp-sub\">\n <span class=\"tp-id\">#{{model.id}}</span>\n <span class=\"tp-pill tp-${this.statusPill}\" data-action=\"change-status\" title=\"Change status\">{{statusLabel}} <i class=\"bi bi-chevron-down\" style=\"font-size:0.5rem;\"></i></span>\n <span class=\"tp-time\"><i class=\"bi bi-clock\"></i> {{model.created|relative}}</span>\n <span class=\"tp-desc-chip\" data-action=\"show-description\" title=\"${this.hasDescription ? 'View / edit description' : 'Add description'}\"><i class=\"bi bi-file-text\"></i> ${this.hasDescription ? 'Description' : 'Add description'}</span>\n </div>\n <div class=\"tp-meta\">\n <div class=\"tp-fields\">\n <span class=\"tp-field\" data-action=\"change-priority\" title=\"Change priority\">\n <i class=\"bi bi-flag-fill\" style=\"color:${this.priorityColor};\"></i>{{priorityLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-assignee\" title=\"Assign\">\n <i class=\"bi bi-person\"></i>{{assigneeName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-category\" title=\"Change category\">\n <i class=\"bi bi-tag\"></i>{{categoryLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-group\" title=\"Change group\">\n <i class=\"bi bi-people\"></i>{{groupName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n </div>\n </div>\n </div>\n\n {{#hasIncident|bool}}\n <div class=\"tp-linked\">\n <i class=\"bi bi-exclamation-triangle-fill\"></i>\n <a href=\"#\" data-action=\"view-incident\">Incident #{{model.incident.id}}</a>\n {{#model.incident.status}}<span class=\"lpill\">{{model.incident.status}}</span>{{/model.incident.status}}\n {{#model.incident.event_count}}<span>&middot; {{model.incident.event_count}} events</span>{{/model.incident.event_count}}\n </div>\n {{/hasIncident|bool}}\n\n <div class=\"tp-conv\">\n <div data-container=\"chat-area\"></div>\n </div>\n\n <div class=\"tp-input\" data-ref=\"drop-zone\">\n <div class=\"tp-attachments\" data-ref=\"attachments\"></div>\n <div class=\"tp-input-wrap\">\n <textarea rows=\"2\" placeholder=\"Add a note...\" data-ref=\"note-textarea\"></textarea>\n <button class=\"tp-send-btn\" data-action=\"send-note\" title=\"Send\"><i class=\"bi bi-arrow-up\"></i></button>\n </div>\n <div class=\"tp-input-hint\">\n <i class=\"bi bi-paperclip\"></i> Drag &amp; drop files to attach\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.adapter = new TicketNoteAdapter(this.model.get('id'));\n\n this.chatView = new ChatView({\n containerId: 'chat-area',\n adapter: this.adapter,\n theme: 'compact',\n currentUserId: this._getCurrentUserId(),\n showInput: false\n });\n this.addChild(this.chatView);\n\n const menu = new ContextMenu({\n containerId: 'panel-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots',\n btnClass: 'tp-btn',\n items: [\n { label: 'Ask AI', action: 'ask-ai', icon: 'bi-robot' },\n { type: 'divider' },\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Refresh Notes', action: 'refresh-notes', icon: 'bi-arrow-clockwise' },\n { type: 'divider' },\n { label: 'Close Window', action: 'close', icon: 'bi-x-lg' }\n ]\n }\n });\n this.addChild(menu);\n }\n\n async onAfterRender() {\n this._setupTextarea();\n this._setupDragDrop();\n // Wait one microtask so child message-view DOM is settled before we\n // place inline action cards and edit buttons.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async _loadActionCards() {\n this._cleanupActionCards();\n\n const messages = this.chatView.messages || [];\n if (!messages.length) return;\n\n for (const msg of messages) {\n if (!msg.action || typeof msg.action !== 'object') continue;\n\n const msgView = this.chatView.messageViews.get(msg.id);\n if (!msgView?.element) continue;\n\n const card = new ActionCardView({\n action: msg.action,\n noteId: msg.id,\n ticketStatus: this.model.get('status')\n });\n // Pending approvals get the respond handler\n if (msg.action.type !== 'context' && !msg.action.resolved) {\n card.on('action:respond', (data) => this._handleActionResponse(data));\n }\n this.addChild(card);\n await card.render();\n msgView.element.after(card.element);\n }\n }\n\n _cleanupActionCards() {\n for (const id in this.children) {\n const child = this.children[id];\n if (child instanceof ActionCardView) {\n this.removeChild(child);\n }\n }\n }\n\n async _handleActionResponse(data) {\n const actionNote = { action: { handler: data.handler, context: data.context } };\n const app = this.getApp();\n app?.showLoading();\n try {\n await this.adapter.addActionResponse(actionNote, data.action);\n await this.model.fetch();\n await this.chatView.refresh();\n this.render();\n } finally {\n app?.hideLoading();\n }\n }\n\n _getCurrentUserId() {\n const app = this.getApp();\n return app?.activeUser?.id || app?.getActiveUser?.()?.id || null;\n }\n\n // ── Actions ──\n\n onActionClose() {\n this.emit('panel:close');\n }\n\n async onActionSendNote() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n const text = textarea?.value?.trim();\n const files = this._stagedFiles || [];\n if (!text && !files.length) return;\n textarea.value = '';\n textarea.style.height = '';\n this._stagedFiles = [];\n this._renderAttachments();\n await this.adapter.addNote({ text: text || '', files });\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n _setupTextarea() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n if (!textarea) return;\n\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n textarea.scrollTop = textarea.scrollHeight;\n };\n\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n\n // Enter → submit (unless Shift held)\n if (e.key === 'Enter' && !e.shiftKey && !mod) {\n e.preventDefault();\n this.onActionSendNote();\n return;\n }\n\n // Shift+Enter → auto-continue list bullets\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n\n // ``` auto-close: after typing third backtick, insert fence pair\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n\n // Tab indent/dedent inside code fences\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n\n // Ctrl/Cmd+B → bold\n if (mod && e.key === 'b') {\n e.preventDefault();\n _wrapSelection('**');\n return;\n }\n\n // Ctrl/Cmd+I → italic\n if (mod && e.key === 'i') {\n e.preventDefault();\n _wrapSelection('*');\n return;\n }\n });\n\n // Auto-close pairs: ( [ \"\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n\n textarea.addEventListener('input', () => {\n textarea.style.height = '';\n textarea.style.height = Math.min(textarea.scrollHeight, 160) + 'px';\n });\n }\n\n _setupDragDrop() {\n const zone = this.element?.querySelector('[data-ref=\"drop-zone\"]');\n if (!zone) return;\n this._stagedFiles = this._stagedFiles || [];\n let counter = 0;\n zone.addEventListener('dragenter', (e) => { e.preventDefault(); counter++; zone.classList.add('tp-dragover'); });\n zone.addEventListener('dragleave', () => { counter--; if (counter <= 0) { counter = 0; zone.classList.remove('tp-dragover'); } });\n zone.addEventListener('dragover', (e) => e.preventDefault());\n zone.addEventListener('drop', async (e) => {\n e.preventDefault();\n counter = 0;\n zone.classList.remove('tp-dragover');\n const files = Array.from(e.dataTransfer?.files || []);\n if (!files.length) return;\n const { File: FileModel } = await import('@core/models/Files.js');\n for (const file of files) {\n const chipId = Date.now() + Math.random();\n this._addAttachChip(chipId, file.name, true);\n try {\n const fileModel = new FileModel();\n await fileModel.upload({ file, showToast: false });\n this._stagedFiles.push(fileModel);\n this._updateAttachChip(chipId, fileModel.get('name') || file.name, fileModel);\n } catch (err) {\n console.error('File upload failed:', err);\n this._removeAttachChip(chipId);\n this.getApp()?.toast?.error?.('Upload failed: ' + file.name);\n }\n }\n });\n }\n\n _addAttachChip(id, name, uploading) {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (!container) return;\n const chip = document.createElement('span');\n chip.className = 'tp-attach-chip' + (uploading ? ' uploading' : '');\n chip.dataset.chipId = id;\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}` +\n (uploading ? '' : '<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>');\n if (!uploading) {\n chip.querySelector('.remove').addEventListener('click', () => {\n this._removeAttachChip(id);\n });\n }\n container.appendChild(chip);\n }\n\n _updateAttachChip(id, name, fileModel) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (!chip) return;\n chip.classList.remove('uploading');\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>`;\n chip.querySelector('.remove').addEventListener('click', () => {\n this._stagedFiles = (this._stagedFiles || []).filter(f => f !== fileModel);\n chip.remove();\n });\n }\n\n _removeAttachChip(id) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (chip) chip.remove();\n }\n\n _renderAttachments() {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (container) container.innerHTML = '';\n }\n\n _escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n _addEditButtons() {\n const userId = this._getCurrentUserId();\n if (!userId || !this.chatView?.messageViews) return;\n const msgById = new Map((this.chatView.messages || []).map(m => [m.id, m]));\n this.chatView.messageViews.forEach((view, msgId) => {\n const msg = msgById.get(msgId);\n const item = view?.element?.querySelector('.message-item');\n if (!item) return;\n item.querySelectorAll('.tp-edit-btn').forEach(b => b.remove());\n if (!msg || msg.author?.id !== userId) return;\n const btn = document.createElement('button');\n btn.className = 'tp-edit-btn';\n btn.title = 'Edit note';\n btn.innerHTML = '<i class=\"bi bi-pencil\"></i>';\n btn.addEventListener('click', (e) => { e.stopPropagation(); this._editNote(msg); });\n item.appendChild(btn);\n });\n }\n\n _setupCollapsible() {\n const container = this.element?.querySelector('[data-container=\"chat-area\"]');\n if (!container) return;\n container.querySelectorAll('.tp-show-more').forEach(b => b.remove());\n container.querySelectorAll('.tp-collapsed').forEach(el => el.classList.remove('tp-collapsed'));\n setTimeout(() => {\n const MAX = 52;\n container.querySelectorAll('.message-content').forEach(body => {\n if (body.scrollHeight <= MAX) return;\n body.classList.add('tp-collapsed');\n body.style.setProperty('--tp-collapse-h', MAX + 'px');\n const btn = document.createElement('button');\n btn.className = 'tp-show-more';\n btn.textContent = 'Show more';\n btn.addEventListener('click', () => {\n const collapsed = body.classList.toggle('tp-collapsed');\n btn.textContent = collapsed ? 'Show more' : 'Show less';\n });\n body.after(btn);\n });\n }, 150);\n }\n\n async _editNote(msg) {\n const metaJson = Object.keys(msg._metadata || {}).length\n ? JSON.stringify(msg._metadata, null, 2) : '';\n const data = await Modal.form({\n title: 'Edit Note',\n icon: 'bi-pencil',\n size: 'lg',\n fields: [{\n type: 'tabset',\n tabs: [\n {\n label: 'Note',\n fields: [{\n name: 'note', type: 'textarea', label: 'Note',\n required: true, cols: 12, rows: 8,\n value: msg._rawContent || msg.content\n }]\n },\n {\n label: 'Metadata',\n fields: [{\n name: 'metadata_json', type: 'json', label: 'Metadata (JSON)',\n cols: 12, rows: 10,\n value: metaJson,\n help: 'Action metadata — e.g. { \"action\": { \"handler\": \"incident.rule_approval\", \"label\": \"...\", \"context\": { ... } } }'\n }]\n }\n ]\n }]\n });\n if (!data) return;\n const { TicketNote } = await import('@ext/admin/models/Tickets.js');\n const note = new TicketNote({ id: msg.id });\n const payload = { note: data.note };\n if (data.metadata_json) {\n payload.metadata = typeof data.metadata_json === 'string'\n ? JSON.parse(data.metadata_json) : data.metadata_json;\n }\n await note.save(payload);\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n async _saveAndSync(patch) {\n // Some endpoints don't echo the updated record on PUT — Model.save() only\n // merges response.data.data into local state. Re-fetch so the panel and\n // the shared list collection both see the new values. Also refresh notes\n // since the backend may create a status_change note as a side effect.\n await this.model.save(patch);\n await this.model.fetch();\n if (this.chatView) {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n }\n\n async onActionChangeStatus(event) {\n const items = STATUS_OPTIONS.map(s => ({\n label: s.replace(/_/g, ' '),\n value: s,\n active: s === this.model.get('status')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ status: result });\n this.render();\n }\n\n async onActionChangePriority(event) {\n const items = PRIORITY_OPTIONS.map(p => ({\n label: p.label,\n value: p.value,\n active: p.value === this.model.get('priority')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ priority: parseInt(result) });\n this.render();\n }\n\n async onActionChangeAssignee() {\n const data = await Modal.form({\n title: 'Assign User',\n icon: 'bi-person-plus',\n size: 'sm',\n fields: [{\n name: 'assignee', type: 'collection', label: 'User',\n Collection: UserList, labelField: 'display_name', valueField: 'id',\n required: true, cols: 12,\n value: this.model.get('assignee')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ assignee: data.assignee });\n this.render();\n }\n\n async onActionChangeCategory(event) {\n const items = Object.entries(TicketCategories).map(([key, label]) => ({\n label,\n value: key,\n active: key === this.model.get('category')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ category: result });\n this.render();\n }\n\n async onActionChangeGroup() {\n const data = await Modal.form({\n title: 'Change Group',\n icon: 'bi-people',\n size: 'sm',\n fields: [{\n name: 'group', type: 'collection', label: 'Group',\n Collection: GroupList, labelField: 'name', valueField: 'id',\n required: false, cols: 12,\n value: this.model.get('group')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ group: data.group });\n this.render();\n }\n\n async onActionShowDescription() {\n const raw = this.model.get('description') || '';\n if (!raw) {\n // No description yet → jump straight to edit\n return this._editDescription();\n }\n let rendered = false;\n let html = '';\n try {\n const resp = await rest.post('/api/docit/render', { markdown: raw });\n html = resp?.data?.data?.html || resp?.data?.html || '';\n rendered = !!html;\n } catch (_e) { /* fallback to escaped text */ }\n if (!rendered) {\n const div = document.createElement('div');\n div.textContent = raw;\n html = `<pre style=\"white-space:pre-wrap;\">${div.innerHTML}</pre>`;\n }\n const choice = await Modal.dialog({\n title: `Ticket #${this.model.get('id')} — Description`,\n body: `<div style=\"font-size:0.85rem; line-height:1.65;\">${html}</div>`,\n size: 'lg',\n buttons: [\n { text: 'Edit', class: 'btn-primary', value: 'edit' },\n { text: 'Close', class: 'btn-secondary', value: 'close' }\n ]\n });\n if (choice === 'edit') await this._editDescription();\n }\n\n async _editDescription() {\n const id = this.model.get('id');\n const current = this.model.get('description') || '';\n const escaped = current\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const body = `\n <div class=\"tp-desc-edit\">\n <textarea data-ref=\"desc-textarea\" rows=\"16\" placeholder=\"Description (markdown supported)...\"\n style=\"width:100%; font-family: var(--bs-font-monospace); font-size: 0.85rem; padding: 10px 12px; border: 1px solid var(--bs-border-color); border-radius: 8px; background: var(--bs-body-bg); color: var(--bs-body-color); resize: vertical; outline: none;\">${escaped}</textarea>\n <div class=\"text-muted small mt-1\">\n Markdown supported. Cmd/Ctrl+B = bold · Cmd/Ctrl+I = italic · Shift+Enter continues lists · \\`\\`\\` opens a code block\n </div>\n </div>\n `;\n // Modal.dialog only mounts strings or Views — pass HTML string and find\n // the textarea after the modal renders, then wire shortcuts.\n const wireUp = async () => {\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 50));\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n if (ta) { this._wireMarkdownTextarea(ta); return ta; }\n }\n return null;\n };\n const wirePromise = wireUp();\n const choice = await Modal.dialog({\n title: `Ticket #${id} — Edit Description`,\n body,\n size: 'lg',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', value: null },\n {\n text: 'Save', class: 'btn-primary',\n handler: () => {\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n return ta ? ta.value : null;\n }\n }\n ]\n });\n await wirePromise; // ensure cleanup of the polling\n if (choice === null || choice === undefined) return;\n await this._saveAndSync({ description: choice });\n this.render();\n }\n\n _wireMarkdownTextarea(textarea) {\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n };\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n // Shift+Enter → auto-continue list bullets (plain Enter saves the form,\n // so list continuation moves to Shift+Enter here too)\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n if (mod && e.key === 'b') { e.preventDefault(); _wrapSelection('**'); return; }\n if (mod && e.key === 'i') { e.preventDefault(); _wrapSelection('*'); return; }\n });\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n }\n\n async onActionViewIncident() {\n const incident = this.model.get('incident');\n if (incident?.id) {\n Modal.showModel(new Incident({ id: incident.id }));\n }\n }\n\n async onActionEditTicket() {\n const resp = await Modal.modelForm({\n title: `Edit Ticket #${this.model.get('id')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) this.render();\n }\n\n async onActionRefreshNotes() {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n this.getApp()?.toast?.success('Notes refreshed');\n }\n\n async onActionAskAi() {\n await openAssistantChat(this, 'incident.Ticket');\n }\n\n async _showInlineSelect(items, event) {\n return new Promise((resolve) => {\n let picked = false;\n const menu = new ContextMenu({\n config: {\n // Each item needs a unique action — ContextMenu identifies the\n // clicked item by data-item-action, and find() returns the FIRST\n // match, so reusing one action name maps every click to item[0].\n items: items.map((item, i) => ({\n label: item.label,\n action: `pick-${i}`,\n class: item.active ? 'fw-bold' : '',\n handler: () => {\n picked = true;\n this.removeChild(menu);\n resolve(item.value);\n }\n }))\n }\n });\n const origClose = menu.closeDropdown.bind(menu);\n menu.closeDropdown = () => {\n origClose();\n if (!picked) {\n this.removeChild(menu);\n resolve(null);\n }\n };\n this.addChild(menu);\n menu.openAt(event.clientX, event.clientY);\n });\n }\n\n async _afterChatRefresh() {\n // ChatView.refresh() doesn't await each message view's render — its inner\n // _renderChildren calls messageView.render(false) without await. Wait one\n // microtask so the message-item DOM is in place before we touch it.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async setTicket(ticket) {\n this.model = ticket;\n this.adapter = new TicketNoteAdapter(ticket.get('id'));\n this.chatView.adapter = this.adapter;\n this.chatView.clearMessages();\n await this.render();\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n}\n\nexport default TicketPanelView;\n"],"names":["esc","s","String","replace","ALLOWED_REFS","Set","HANDLER_COLORS","dot","label","ActionCardView","View","constructor","options","super","className","this","action","noteId","ticketStatus","isResolved","resolved","isContext","type","isClosed","handlerConfig","handler","onBeforeRender","cfg","dotClass","_buildContextTemplate","_buildResolvedTemplate","_buildPendingTemplate","onActionToggleCompact","acEl","element","querySelector","classList","toggle","refItems","references","filter","ref","has","model","map","split","pop","pk","join","template","resolution","badgeClass","badgeText","target","context","refHtml","detail","disabledAttr","onActionOpenRef","_event","el","modelRef","dataset","test","app","getApp","ModelClass","getModelByRef","VIEW_CLASS","Modal","showModel","id","onActionApprove","emit","onActionDeny","STATUS_OPTIONS","STATUS_PILL","new","open","in_progress","pending","qa","closed","ignored","PRIORITY_OPTIONS","value","TicketPanelView","Ticket","data","status","get","statusPill","statusLabel","priorityLabel","assigneeName","categoryLabel","groupName","hasDescription","hasIncident","priorityColor","onInit","adapter","TicketNoteAdapter","chatView","ChatView","containerId","theme","currentUserId","_getCurrentUserId","showInput","addChild","menu","ContextMenu","config","icon","btnClass","items","onAfterRender","_setupTextarea","_setupDragDrop","Promise","r","setTimeout","_loadActionCards","_addEditButtons","_setupCollapsible","_cleanupActionCards","messages","length","msg","msgView","messageViews","card","on","_handleActionResponse","render","after","children","child","removeChild","actionNote","showLoading","addActionResponse","fetch","refresh","hideLoading","activeUser","getActiveUser","onActionClose","onActionSendNote","textarea","text","trim","files","_stagedFiles","style","height","_renderAttachments","addNote","_afterChatRefresh","_insert","cursorOffset","start","selectionStart","end","selectionEnd","setRangeText","dispatchEvent","Event","scrollTop","scrollHeight","_wrapSelection","wrapper","sel","substring","startsWith","endsWith","slice","_lineAt","pos","lineStart","lastIndexOf","_inCodeFence","match","addEventListener","e","mod","ctrlKey","metaKey","key","shiftKey","preventDefault","indent","bullet","next","parseInt","stripped","PAIRS","altKey","close","Math","min","zone","counter","add","remove","async","Array","from","dataTransfer","File","FileModel","resolve","then","require","n","Files","file","chipId","Date","now","random","_addAttachChip","name","fileModel","upload","showToast","push","_updateAttachChip","err","console","error","_removeAttachChip","toast","uploading","container","chip","document","createElement","innerHTML","_escapeHtml","appendChild","f","str","div","textContent","userId","msgById","Map","m","forEach","view","msgId","item","querySelectorAll","b","author","btn","title","stopPropagation","_editNote","body","setProperty","MAX","collapsed","metaJson","Object","keys","_metadata","JSON","stringify","form","size","fields","tabs","required","cols","rows","_rawContent","content","help","TicketNote","Tickets","note","payload","metadata_json","metadata","parse","save","_saveAndSync","patch","onActionChangeStatus","event","active","result","_showInlineSelect","onActionChangePriority","p","priority","onActionChangeAssignee","Collection","UserList","labelField","valueField","assignee","onActionChangeCategory","entries","TicketCategories","category","onActionChangeGroup","GroupList","group","onActionShowDescription","raw","_editDescription","rendered","html","resp","rest","post","markdown","_e","dialog","buttons","class","wirePromise","i","ta","_wireMarkdownTextarea","wireUp","choice","description","onActionViewIncident","incident","Incident","onActionEditTicket","modelForm","TicketForms","edit","onActionRefreshNotes","success","onActionAskAi","openAssistantChat","picked","origClose","closeDropdown","bind","openAt","clientX","clientY","setTicket","ticket","clearMessages"],"mappings":"iXAGA,SAASA,EAAIC,GACT,OAAKA,EACEC,OAAOD,GAAGE,QAAQ,KAAM,SAASA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,UADnF,EAEnB,CAEA,MAAMC,qBAAmBC,IAAI,CACzB,mBAAoB,oBAAqB,iBACzC,kBAAmB,yBAGjBC,EAAiB,CACnB,yBAA0B,CAAEC,IAAK,SAAUC,MAAO,QAClD,yBAA0B,CAAED,IAAK,MAAOC,MAAO,SAC/C,uBAA0B,CAAED,IAAK,QAASC,MAAO,UACjD,oBAA0B,CAAED,IAAK,QAASC,MAAO,aAGrD,MAAMC,uBAAuBC,EAAAA,KACzB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFC,UAAW,sBACRF,IAEPG,KAAKC,OAASJ,EAAQI,OACtBD,KAAKE,OAASL,EAAQK,OACtBF,KAAKG,aAAeN,EAAQM,YAChC,CAEA,cAAIC,GACA,QAASJ,KAAKC,QAAQI,QAC1B,CAEA,aAAIC,GACA,MAA6B,YAAtBN,KAAKC,QAAQM,IACxB,CAEA,YAAIC,GACA,MAA6B,WAAtBR,KAAKG,cAAmD,aAAtBH,KAAKG,YAClD,CAEA,iBAAIM,GACA,OAAOlB,EAAeS,KAAKC,QAAQS,UAAY,CAAElB,IAAK,SAAUC,MAAO,SAC3E,CAEA,cAAAkB,GACI,MAAMC,EAAMZ,KAAKS,cACjBT,KAAKa,SAAWD,EAAIpB,IAEhBQ,KAAKM,UACLN,KAAKc,wBACEd,KAAKI,WACZJ,KAAKe,yBAELf,KAAKgB,uBAEb,CAEA,qBAAAC,GACI,MAAMC,EAAOlB,KAAKmB,SAASC,cAAc,gBACrCF,GAAMA,EAAKG,UAAUC,OAAO,UACpC,CAEA,qBAAAR,GACI,MACMS,GADQvB,KAAKC,OAAOuB,YAAc,IAAIC,OAAOC,GAAOrC,EAAasC,IAAID,EAAIE,QACzDC,IAAIH,IACtB,MAAMjC,EAAQR,EAAIyC,EAAIjC,QAAU,GAAGR,EAAIyC,EAAIE,MAAME,MAAM,KAAKC,WAAW9C,EAAIyC,EAAIM,MAC/E,MAAO,2DAA2D/C,EAAIyC,EAAIE,oBAAoB3C,EAAIyC,EAAIM,gDAAgDvC,aACvJwC,KAAK,IAERjC,KAAKkC,SAAW,kRAMiBX,uCAGrC,CAEA,sBAAAR,GACI,MAAMoB,EAAanC,KAAKC,OAAOkC,YAAc,WACvCC,EAA4B,aAAfD,EAA4B,WAAa,SACtDE,EAA2B,aAAfF,EAA4B,WAAa,SACrDG,EAAStC,KAAKC,OAAOsC,SAASD,OACpC,IAAIE,EAAU,GACd,GAAIF,GAAUjD,EAAasC,IAAIW,EAAOV,OAAQ,CAC1C,MAAMnC,EAAQR,EAAIe,KAAKC,OAAOsC,QAAQ9C,QAAU,GAAGR,EAAIqD,EAAOV,MAAME,MAAM,KAAKC,WAAW9C,EAAIqD,EAAON,MACrGQ,EAAU,kFAAkFvD,EAAIqD,EAAOV,oBAAoB3C,EAAIqD,EAAON,gDAAgDvC,gBAC1L,CACAO,KAAKkC,SAAW,uLAGkBlC,KAAKa,iEACF5B,EAAIe,KAAKC,OAAOR,QAAU,8DAC3B2C,MAAeC,wHAGzCG,iCAGd,CAEA,qBAAAxB,GACI,MAAMsB,EAAStC,KAAKC,OAAOsC,SAASD,OACpC,IAAIE,EAAU,GACd,GAAIF,GAAUjD,EAAasC,IAAIW,EAAOV,OAAQ,CAC1C,MAAMnC,EAAQR,EAAIe,KAAKC,OAAOsC,QAAQ9C,QAAU,GAAGR,EAAIqD,EAAOV,MAAME,MAAM,KAAKC,WAAW9C,EAAIqD,EAAON,MACrGQ,EAAU,+DAA+DvD,EAAIqD,EAAOV,oBAAoB3C,EAAIqD,EAAON,gDAAgDvC,UACvK,CACA,MAAMgD,EAASxD,EAAIe,KAAKC,OAAOsC,SAASE,QAClCC,EAAe1C,KAAKQ,SAAW,YAAc,GACnDR,KAAKkC,SAAW,iHAGkBlC,KAAKa,iEACF5B,EAAIe,KAAKC,OAAOR,QAAU,mFAE9BgD,IAASD,wHAEqBE,sFACNA,uEAI7D,CAEA,qBAAMC,CAAgBC,EAAQC,GAC1B,MAAMC,EAAWD,EAAGE,QAAQnB,MACtBI,EAAKa,EAAGE,QAAQf,GACtB,IAAK3C,EAAasC,IAAImB,KAAc,QAAQE,KAAKhB,GAAK,OACtD,MAAMiB,EAAMjD,KAAKkD,SACXC,EAAaF,GAAKG,cAAcN,GAClCK,GAAYE,YACZC,EAAAA,MAAMC,UAAU,IAAIJ,EAAW,CAAEK,GAAIxB,IAE7C,CAEA,eAAAyB,GACIzD,KAAK0D,KAAK,iBAAkB,CAAExD,OAAQF,KAAKE,OAAQD,OAAQ,UAAWS,QAASV,KAAKC,OAAOS,QAAS6B,QAASvC,KAAKC,OAAOsC,SAC7H,CAEA,YAAAoB,GACI3D,KAAK0D,KAAK,iBAAkB,CAAExD,OAAQF,KAAKE,OAAQD,OAAQ,OAAQS,QAASV,KAAKC,OAAOS,QAAS6B,QAASvC,KAAKC,OAAOsC,SAC1H,ECrIJ,MAAMqB,EAAiB,CAAC,MAAO,OAAQ,cAAe,UAAW,WAAY,KAAM,SAAU,WACvFC,EAAc,CAChBC,IAAa,WACbC,KAAa,YACbC,YAAa,YACbC,QAAa,YACb5D,SAAa,gBACb6D,GAAa,YACbC,OAAa,cACbC,QAAa,eAEXC,EAAmB,CACrB,CAAEC,MAAO,GAAI7E,MAAO,kBACpB,CAAE6E,MAAO,EAAI7E,MAAO,eACpB,CAAE6E,MAAO,EAAI7E,MAAO,aACpB,CAAE6E,MAAO,EAAI7E,MAAO,iBACpB,CAAE6E,MAAO,EAAI7E,MAAO,eACpB,CAAE6E,MAAO,EAAI7E,MAAO,YACpB,CAAE6E,MAAO,EAAI7E,MAAO,cAGxB,MAAM8E,wBAAwB5E,EAAAA,KAC1B,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFC,UAAW,uBACRF,IAEPG,KAAK4B,MAAQ/B,EAAQ+B,OAAS,IAAI4C,EAAAA,OAAO3E,EAAQ4E,MAAQ,GAC7D,CAEA,cAAA9D,GACI,MAAM+D,EAAS1E,KAAK4B,MAAM+C,IAAI,WAAa,MAC3C3E,KAAK4E,WAAaf,EAAYa,IAAW,cACzC1E,KAAK6E,YAAeH,EAActF,QAAQ,KAAM,KAChDY,KAAK8E,cAAgB,IAAI9E,KAAK4B,MAAM+C,IAAI,aAAe,IACvD3E,KAAK+E,aAAe/E,KAAK4B,MAAM+C,IAAI,0BAA4B3E,KAAK4B,MAAM+C,IAAI,aAAe,aAC7F3E,KAAKgF,cAAgBhF,KAAK4B,MAAM+C,IAAI,aAAe,SACnD3E,KAAKiF,UAAYjF,KAAK4B,MAAM+C,IAAI,eAAiB3E,KAAK4B,MAAM+C,IAAI,UAAY,OAC5E3E,KAAKkF,iBAAmBlF,KAAK4B,MAAM+C,IAAI,eACvC3E,KAAKmF,eAAiBnF,KAAK4B,MAAM+C,IAAI,aAAqD,iBAA/B3E,KAAK4B,MAAM+C,IAAI,cAA4B3E,KAAK4B,MAAM+C,IAAI,YAAYnB,IACjIxD,KAAKoF,eAAiBpF,KAAK4B,MAAM+C,IAAI,aAAe,IAAM,EAAI,mBAAqB,4BAEnF3E,KAAKkC,SAAW,qeAK8HlC,KAAKkF,eAAiB,UAAY,0DACtKlF,KAAKkF,eAAiB,gDAAkD,spTAoG9ClF,KAAKkF,eAAiB,iCAAmC,MAAMlF,KAAKkF,eAAiB,gCAAkC,+CAC1HlF,KAAKkF,eAAiB,+CAAiD,mVAQlElF,KAAK4E,oVAEoC5E,KAAKkF,eAAiB,0BAA4B,sDAAsDlF,KAAKkF,eAAiB,cAAgB,oTAK/JlF,KAAKoF,m8EA8CvE,CAEA,YAAMC,GACFrF,KAAKsF,QAAU,IAAIC,EAAAA,kBAAkBvF,KAAK4B,MAAM+C,IAAI,OAEpD3E,KAAKwF,SAAW,IAAIC,WAAS,CACzBC,YAAa,YACbJ,QAAStF,KAAKsF,QACdK,MAAO,UACPC,cAAe5F,KAAK6F,oBACpBC,WAAW,IAEf9F,KAAK+F,SAAS/F,KAAKwF,UAEnB,MAAMQ,EAAO,IAAIC,cAAY,CACzBP,YAAa,aACb3F,UAAW,yCACXwC,QAASvC,KAAK4B,MACdsE,OAAQ,CACJC,KAAM,gBACNC,SAAU,SACVC,MAAO,CACH,CAAE5G,MAAO,SAAUQ,OAAQ,SAAUkG,KAAM,YAC3C,CAAE5F,KAAM,WACR,CAAEd,MAAO,cAAeQ,OAAQ,cAAekG,KAAM,aACrD,CAAE1G,MAAO,gBAAiBQ,OAAQ,gBAAiBkG,KAAM,sBACzD,CAAE5F,KAAM,WACR,CAAEd,MAAO,eAAgBQ,OAAQ,QAASkG,KAAM,eAI5DnG,KAAK+F,SAASC,EAClB,CAEA,mBAAMM,GACFtG,KAAKuG,iBACLvG,KAAKwG,uBAGC,IAAIC,QAAQC,GAAKC,WAAWD,EAAG,UAC/B1G,KAAK4G,mBACX5G,KAAK6G,kBACL7G,KAAK8G,mBACT,CAEA,sBAAMF,GACF5G,KAAK+G,sBAEL,MAAMC,EAAWhH,KAAKwF,SAASwB,UAAY,GAC3C,GAAKA,EAASC,OAEd,IAAA,MAAWC,KAAOF,EAAU,CACxB,IAAKE,EAAIjH,QAAgC,iBAAfiH,EAAIjH,OAAqB,SAEnD,MAAMkH,EAAUnH,KAAKwF,SAAS4B,aAAazC,IAAIuC,EAAI1D,IACnD,IAAK2D,GAAShG,QAAS,SAEvB,MAAMkG,EAAO,IAAI3H,eAAe,CAC5BO,OAAQiH,EAAIjH,OACZC,OAAQgH,EAAI1D,GACZrD,aAAcH,KAAK4B,MAAM+C,IAAI,YAGT,YAApBuC,EAAIjH,OAAOM,MAAuB2G,EAAIjH,OAAOI,UAC7CgH,EAAKC,GAAG,iBAAmB7C,GAASzE,KAAKuH,sBAAsB9C,IAEnEzE,KAAK+F,SAASsB,SACRA,EAAKG,SACXL,EAAQhG,QAAQsG,MAAMJ,EAAKlG,QAC/B,CACJ,CAEA,mBAAA4F,GACI,IAAA,MAAWvD,KAAMxD,KAAK0H,SAAU,CAC5B,MAAMC,EAAQ3H,KAAK0H,SAASlE,GACxBmE,aAAiBjI,gBACjBM,KAAK4H,YAAYD,EAEzB,CACJ,CAEA,2BAAMJ,CAAsB9C,GACxB,MAAMoD,EAAa,CAAE5H,OAAQ,CAAES,QAAS+D,EAAK/D,QAAS6B,QAASkC,EAAKlC,UAC9DU,EAAMjD,KAAKkD,SACjBD,GAAK6E,cACL,UACU9H,KAAKsF,QAAQyC,kBAAkBF,EAAYpD,EAAKxE,cAChDD,KAAK4B,MAAMoG,cACXhI,KAAKwF,SAASyC,UACpBjI,KAAKwH,QACT,CAAA,QACIvE,GAAKiF,aACT,CACJ,CAEA,iBAAArC,GACI,MAAM5C,EAAMjD,KAAKkD,SACjB,OAAOD,GAAKkF,YAAY3E,IAAMP,GAAKmF,mBAAmB5E,IAAM,IAChE,CAIA,aAAA6E,GACIrI,KAAK0D,KAAK,cACd,CAEA,sBAAM4E,GACF,MAAMC,EAAWvI,KAAKmB,SAASC,cAAc,8BACvCoH,EAAOD,GAAUjE,OAAOmE,OACxBC,EAAQ1I,KAAK2I,cAAgB,IAC9BH,GAASE,EAAMzB,UACpBsB,EAASjE,MAAQ,GACjBiE,EAASK,MAAMC,OAAS,GACxB7I,KAAK2I,aAAe,GACpB3I,KAAK8I,2BACC9I,KAAKsF,QAAQyD,QAAQ,CAAEP,KAAMA,GAAQ,GAAIE,gBACzC1I,KAAKwF,SAASyC,gBACdjI,KAAKgJ,oBACf,CAEA,cAAAzC,GACI,MAAMgC,EAAWvI,KAAKmB,SAASC,cAAc,8BAC7C,IAAKmH,EAAU,OAEf,MAAMU,EAAU,CAACT,EAAMU,EAAeV,EAAKvB,UACvC,MAAMkC,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACrBf,EAASgB,aAAaf,EAAMW,EAAOE,EAAK,OACxCd,EAASa,eAAiBb,EAASe,aAAeH,EAAQD,EAC1DX,EAASiB,cAAc,IAAIC,MAAM,UACjClB,EAASmB,UAAYnB,EAASoB,cAG5BC,EAAkBC,IACpB,MAAMV,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACfQ,EAAMvB,EAASjE,MAAMyF,UAAUZ,EAAOE,GACxCS,EAAIE,WAAWH,IAAYC,EAAIG,SAASJ,IACxCtB,EAASgB,aAAaO,EAAII,MAAML,EAAQ5C,QAAS4C,EAAQ5C,QAASkC,EAAOE,EAAK,OAC9Ed,EAASa,eAAiBD,EAC1BZ,EAASe,aAAeD,EAAuB,EAAjBQ,EAAQ5C,SAEtCsB,EAASgB,aAAaM,EAAUC,EAAMD,EAASV,EAAOE,EAAK,OAC3Dd,EAASa,eAAiBD,EAAQU,EAAQ5C,OAC1CsB,EAASe,aAAeD,EAAMQ,EAAQ5C,SAIxCkD,EAAWC,IACb,MACMC,EADS9B,EAASjE,MAAMyF,UAAU,EAAGK,GAClBE,YAAY,MAAQ,EAC7C,MAAO,CAAEnB,MAAOkB,EAAW7B,KAAMD,EAASjE,MAAMyF,UAAUM,EAAWD,KAGnEG,EAAgBH,IACH7B,EAASjE,MAAMyF,UAAU,EAAGK,GACpBI,MAAM,WAAa,IAAIvD,OAC9B,GAAM,EAG1BsB,EAASkC,iBAAiB,UAAYC,IAClC,MAAMC,EAAMD,EAAEE,SAAWF,EAAEG,QAG3B,GAAc,UAAVH,EAAEI,MAAoBJ,EAAEK,WAAaJ,EAGrC,OAFAD,EAAEM,sBACFhL,KAAKsI,mBAKT,GAAc,UAAVoC,EAAEI,KAAmBJ,EAAEK,SAAU,CACjC,MAAM5B,MAAEA,EAAAX,KAAOA,GAAS2B,EAAQ5B,EAASa,gBACnCoB,EAAQhC,EAAKgC,MAAM,wBACzB,GAAIA,EAAO,CACPE,EAAEM,iBACF,MAAMC,EAAST,EAAM,GACfU,EAASV,EAAM,GACrB,GAAIhC,EAAKC,SAAWyC,EAChB3C,EAASgB,aAAa,GAAIJ,EAAOZ,EAASa,eAAgB,WACvD,CACH,MAAM+B,EAAO,SAASnI,KAAKkI,GAAU,GAAGE,SAASF,GAAU,KAAOA,EAClEjC,EAAQ,KAAKgC,IAASE,KAC1B,CACA,MACJ,CACJ,CAGA,GAAc,MAAVT,EAAEI,MAAgBH,EAAK,CACvB,MAAMP,EAAM7B,EAASa,eAErB,GADeb,EAASjE,MAAMyF,UAAU,EAAGK,GAChCH,SAAS,QAAUM,EAAaH,EAAM,GAG7C,OAFAM,EAAEM,sBACF/B,EAAQ,WAAY,EAG5B,CAGA,GAAc,QAAVyB,EAAEI,MAAiBP,EAAahC,EAASa,gBAc7C,OAAIuB,GAAiB,MAAVD,EAAEI,KACTJ,EAAEM,sBACFpB,EAAe,OAKfe,GAAiB,MAAVD,EAAEI,KACTJ,EAAEM,sBACFpB,EAAe,WAFnB,EAnBI,GADAc,EAAEM,iBACEN,EAAEK,SAAU,CACZ,MAAM5B,MAAEA,EAAAX,KAAOA,GAAS2B,EAAQ5B,EAASa,gBACnCiC,EAAW7C,EAAKpJ,QAAQ,UAAW,IACzCmJ,EAASgB,aAAa8B,EAAUlC,EAAOA,EAAQX,EAAKvB,OAAQ,OAC5DsB,EAASa,eAAiBb,EAASe,aAAeH,EAAQkC,EAASpE,MACvE,MACIgC,EAAQ,QAqBpB,MAAMqC,EAAQ,CAAE,IAAK,IAAK,IAAK,IAAK,IAAK,KACzC/C,EAASkC,iBAAiB,UAAYC,IAClC,GAAIA,EAAEE,SAAWF,EAAEG,SAAWH,EAAEa,OAAQ,OACxC,MAAMC,EAAQF,EAAMZ,EAAEI,KACtB,IAAKU,EAAO,OACZ,MAAMrC,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACrB,GAAIH,IAAUE,EAAK,CACfqB,EAAEM,iBACF,MAAMlB,EAAMvB,EAASjE,MAAMyF,UAAUZ,EAAOE,GAC5Cd,EAASgB,aAAamB,EAAEI,IAAMhB,EAAM0B,EAAOrC,EAAOE,EAAK,OACvDd,EAASa,eAAiBD,EAAQ,EAClCZ,EAASe,aAAeD,EAAM,CAClC,MACIqB,EAAEM,iBACF/B,EAAQyB,EAAEI,IAAMU,EAAO,KAI/BjD,EAASkC,iBAAiB,QAAS,KAC/BlC,EAASK,MAAMC,OAAS,GACxBN,EAASK,MAAMC,OAAS4C,KAAKC,IAAInD,EAASoB,aAAc,KAAO,MAEvE,CAEA,cAAAnD,GACI,MAAMmF,EAAO3L,KAAKmB,SAASC,cAAc,0BACzC,IAAKuK,EAAM,OACX3L,KAAK2I,aAAe3I,KAAK2I,cAAgB,GACzC,IAAIiD,EAAU,EACdD,EAAKlB,iBAAiB,YAAcC,IAAQA,EAAEM,iBAAkBY,IAAWD,EAAKtK,UAAUwK,IAAI,iBAC9FF,EAAKlB,iBAAiB,YAAa,KAAQmB,IAAeA,GAAW,IAAKA,EAAU,EAAGD,EAAKtK,UAAUyK,OAAO,kBAC7GH,EAAKlB,iBAAiB,WAAaC,GAAMA,EAAEM,kBAC3CW,EAAKlB,iBAAiB,OAAQsB,MAAOrB,IACjCA,EAAEM,iBACFY,EAAU,EACVD,EAAKtK,UAAUyK,OAAO,eACtB,MAAMpD,EAAQsD,MAAMC,KAAKvB,EAAEwB,cAAcxD,OAAS,IAClD,IAAKA,EAAMzB,OAAQ,OACnB,MAAQkF,KAAMC,SAAoB3F,QAAA4F,UAAAC,KAAA,IAAAC,QAAO,wBAAuBD,KAAAE,GAAAA,EAAAC,OAChE,IAAA,MAAWC,KAAQhE,EAAO,CACtB,MAAMiE,EAASC,KAAKC,MAAQpB,KAAKqB,SACjC9M,KAAK+M,eAAeJ,EAAQD,EAAKM,MAAM,GACvC,IACI,MAAMC,EAAY,IAAIb,QAChBa,EAAUC,OAAO,CAAER,OAAMS,WAAW,IAC1CnN,KAAK2I,aAAayE,KAAKH,GACvBjN,KAAKqN,kBAAkBV,EAAQM,EAAUtI,IAAI,SAAW+H,EAAKM,KAAMC,EACvE,OAASK,GACLC,QAAQC,MAAM,sBAAuBF,GACrCtN,KAAKyN,kBAAkBd,GACvB3M,KAAKkD,UAAUwK,OAAOF,QAAQ,kBAAoBd,EAAKM,KAC3D,CACJ,GAER,CAEA,cAAAD,CAAevJ,EAAIwJ,EAAMW,GACrB,MAAMC,EAAY5N,KAAKmB,SAASC,cAAc,4BAC9C,IAAKwM,EAAW,OAChB,MAAMC,EAAOC,SAASC,cAAc,QACpCF,EAAK9N,UAAY,kBAAoB4N,EAAY,aAAe,IAChEE,EAAK9K,QAAQ4J,OAASnJ,EACtBqK,EAAKG,UAAY,kCAAkChO,KAAKiO,YAAYjB,MAC/DW,EAAY,GAAK,uEACjBA,GACDE,EAAKzM,cAAc,WAAWqJ,iBAAiB,QAAS,KACpDzK,KAAKyN,kBAAkBjK,KAG/BoK,EAAUM,YAAYL,EAC1B,CAEA,iBAAAR,CAAkB7J,EAAIwJ,EAAMC,GACxB,MAAMY,EAAO7N,KAAKmB,SAASC,cAAc,kBAAkBoC,OACtDqK,IACLA,EAAKxM,UAAUyK,OAAO,aACtB+B,EAAKG,UAAY,kCAAkChO,KAAKiO,YAAYjB,wEACpEa,EAAKzM,cAAc,WAAWqJ,iBAAiB,QAAS,KACpDzK,KAAK2I,cAAgB3I,KAAK2I,cAAgB,IAAIlH,OAAO0M,GAAKA,IAAMlB,GAChEY,EAAK/B,WAEb,CAEA,iBAAA2B,CAAkBjK,GACd,MAAMqK,EAAO7N,KAAKmB,SAASC,cAAc,kBAAkBoC,OACvDqK,KAAW/B,QACnB,CAEA,kBAAAhD,GACI,MAAM8E,EAAY5N,KAAKmB,SAASC,cAAc,4BAC1CwM,MAAqBI,UAAY,GACzC,CAEA,WAAAC,CAAYG,GACR,MAAMC,EAAMP,SAASC,cAAc,OAEnC,OADAM,EAAIC,YAAcF,EACXC,EAAIL,SACf,CAEA,eAAAnH,GACI,MAAM0H,EAASvO,KAAK6F,oBACpB,IAAK0I,IAAWvO,KAAKwF,UAAU4B,aAAc,OAC7C,MAAMoH,EAAU,IAAIC,KAAKzO,KAAKwF,SAASwB,UAAY,IAAInF,OAAS,CAAC6M,EAAElL,GAAIkL,KACvE1O,KAAKwF,SAAS4B,aAAauH,QAAQ,CAACC,EAAMC,KACtC,MAAM3H,EAAMsH,EAAQ7J,IAAIkK,GAClBC,EAAOF,GAAMzN,SAASC,cAAc,iBAC1C,IAAK0N,EAAM,OAEX,GADAA,EAAKC,iBAAiB,gBAAgBJ,QAAQK,GAAKA,EAAElD,WAChD5E,GAAOA,EAAI+H,QAAQzL,KAAO+K,EAAQ,OACvC,MAAMW,EAAMpB,SAASC,cAAc,UACnCmB,EAAInP,UAAY,cAChBmP,EAAIC,MAAQ,YACZD,EAAIlB,UAAY,+BAChBkB,EAAIzE,iBAAiB,QAAUC,IAAQA,EAAE0E,kBAAmBpP,KAAKqP,UAAUnI,KAC3E4H,EAAKZ,YAAYgB,IAEzB,CAEA,iBAAApI,GACI,MAAM8G,EAAY5N,KAAKmB,SAASC,cAAc,gCACzCwM,IACLA,EAAUmB,iBAAiB,iBAAiBJ,QAAQK,GAAKA,EAAElD,UAC3D8B,EAAUmB,iBAAiB,iBAAiBJ,WAAc9L,EAAGxB,UAAUyK,OAAO,iBAC9EnF,WAAW,KAEPiH,EAAUmB,iBAAiB,oBAAoBJ,QAAQW,IACnD,GAAIA,EAAK3F,cAFD,GAEsB,OAC9B2F,EAAKjO,UAAUwK,IAAI,gBACnByD,EAAK1G,MAAM2G,YAAY,kBAAmBC,QAC1C,MAAMN,EAAMpB,SAASC,cAAc,UACnCmB,EAAInP,UAAY,eAChBmP,EAAIZ,YAAc,YAClBY,EAAIzE,iBAAiB,QAAS,KAC1B,MAAMgF,EAAYH,EAAKjO,UAAUC,OAAO,gBACxC4N,EAAIZ,YAAcmB,EAAY,YAAc,cAEhDH,EAAK7H,MAAMyH,MAEhB,KACP,CAEA,eAAMG,CAAUnI,GACZ,MAAMwI,EAAWC,OAAOC,KAAK1I,EAAI2I,WAAa,CAAA,GAAI5I,OAC5C6I,KAAKC,UAAU7I,EAAI2I,UAAW,KAAM,GAAK,GACzCpL,QAAanB,EAAAA,MAAM0M,KAAK,CAC1Bb,MAAO,YACPhJ,KAAM,YACN8J,KAAM,KACNC,OAAQ,CAAC,CACL3P,KAAM,SACN4P,KAAM,CACF,CACI1Q,MAAO,OACPyQ,OAAQ,CAAC,CACLlD,KAAM,OAAQzM,KAAM,WAAYd,MAAO,OACvC2Q,UAAU,EAAMC,KAAM,GAAIC,KAAM,EAChChM,MAAO4C,EAAIqJ,aAAerJ,EAAIsJ,WAGtC,CACI/Q,MAAO,WACPyQ,OAAQ,CAAC,CACLlD,KAAM,gBAAiBzM,KAAM,OAAQd,MAAO,kBAC5C4Q,KAAM,GAAIC,KAAM,GAChBhM,MAAOoL,EACPe,KAAM,2HAM1B,IAAKhM,EAAM,OACX,MAAMiM,WAAEA,SAAqBjK,QAAA4F,UAAAC,KAAA,IAAAC,QAAO,+BAA8BD,KAAAE,GAAAA,EAAAmE,SAC5DC,EAAO,IAAIF,EAAW,CAAElN,GAAI0D,EAAI1D,KAChCqN,EAAU,CAAED,KAAMnM,EAAKmM,MACzBnM,EAAKqM,gBACLD,EAAQE,SAAyC,iBAAvBtM,EAAKqM,cACzBhB,KAAKkB,MAAMvM,EAAKqM,eAAiBrM,EAAKqM,qBAE1CF,EAAKK,KAAKJ,SACV7Q,KAAKwF,SAASyC,gBACdjI,KAAKgJ,mBACf,CAEA,kBAAMkI,CAAaC,SAKTnR,KAAK4B,MAAMqP,KAAKE,SAChBnR,KAAK4B,MAAMoG,QACbhI,KAAKwF,iBACCxF,KAAKwF,SAASyC,gBACdjI,KAAKgJ,oBAEnB,CAEA,0BAAMoI,CAAqBC,GACvB,MAAMhL,EAAQzC,EAAe/B,IAAI3C,IAAA,CAC7BO,MAAOP,EAAEE,QAAQ,KAAM,KACvBkF,MAAOpF,EACPoS,OAAQpS,IAAMc,KAAK4B,MAAM+C,IAAI,aAE3B4M,QAAevR,KAAKwR,kBAAkBnL,EAAOgL,GAC9CE,UACCvR,KAAKkR,aAAa,CAAExM,OAAQ6M,IAClCvR,KAAKwH,SACT,CAEA,4BAAMiK,CAAuBJ,GACzB,MAAMhL,EAAQhC,EAAiBxC,IAAI6P,IAAA,CAC/BjS,MAAOiS,EAAEjS,MACT6E,MAAOoN,EAAEpN,MACTgN,OAAQI,EAAEpN,QAAUtE,KAAK4B,MAAM+C,IAAI,eAEjC4M,QAAevR,KAAKwR,kBAAkBnL,EAAOgL,GAC9CE,UACCvR,KAAKkR,aAAa,CAAES,SAAUvG,SAASmG,KAC7CvR,KAAKwH,SACT,CAEA,4BAAMoK,GACF,MAAMnN,QAAanB,EAAAA,MAAM0M,KAAK,CAC1Bb,MAAO,cACPhJ,KAAM,iBACN8J,KAAM,KACNC,OAAQ,CAAC,CACLlD,KAAM,WAAYzM,KAAM,aAAcd,MAAO,OAC7CoS,WAAYC,EAAAA,SAAUC,WAAY,eAAgBC,WAAY,KAC9D5B,UAAU,EAAMC,KAAM,GACtB/L,MAAOtE,KAAK4B,MAAM+C,IAAI,gBAGzBF,UACCzE,KAAKkR,aAAa,CAAEe,SAAUxN,EAAKwN,WACzCjS,KAAKwH,SACT,CAEA,4BAAM0K,CAAuBb,GACzB,MAAMhL,EAAQsJ,OAAOwC,QAAQC,EAAAA,kBAAkBvQ,IAAI,EAAEiJ,EAAKrL,MAAK,CAC3DA,QACA6E,MAAOwG,EACPwG,OAAQxG,IAAQ9K,KAAK4B,MAAM+C,IAAI,eAE7B4M,QAAevR,KAAKwR,kBAAkBnL,EAAOgL,GAC9CE,UACCvR,KAAKkR,aAAa,CAAEmB,SAAUd,IACpCvR,KAAKwH,SACT,CAEA,yBAAM8K,GACF,MAAM7N,QAAanB,EAAAA,MAAM0M,KAAK,CAC1Bb,MAAO,eACPhJ,KAAM,YACN8J,KAAM,KACNC,OAAQ,CAAC,CACLlD,KAAM,QAASzM,KAAM,aAAcd,MAAO,QAC1CoS,WAAYU,EAAAA,UAAWR,WAAY,OAAQC,WAAY,KACvD5B,UAAU,EAAOC,KAAM,GACvB/L,MAAOtE,KAAK4B,MAAM+C,IAAI,aAGzBF,UACCzE,KAAKkR,aAAa,CAAEsB,MAAO/N,EAAK+N,QACtCxS,KAAKwH,SACT,CAEA,6BAAMiL,GACF,MAAMC,EAAM1S,KAAK4B,MAAM+C,IAAI,gBAAkB,GAC7C,IAAK+N,EAED,OAAO1S,KAAK2S,mBAEhB,IAAIC,GAAW,EACXC,EAAO,GACX,IACI,MAAMC,QAAaC,OAAKC,KAAK,oBAAqB,CAAEC,SAAUP,IAC9DG,EAAOC,GAAMrO,MAAMA,MAAMoO,MAAQC,GAAMrO,MAAMoO,MAAQ,GACrDD,IAAaC,CACjB,OAASK,GAAqC,CAC9C,IAAKN,EAAU,CACX,MAAMvE,EAAMP,SAASC,cAAc,OACnCM,EAAIC,YAAcoE,EAClBG,EAAO,sCAAsCxE,EAAIL,iBACrD,CAUe,eATM1K,EAAAA,MAAM6P,OAAO,CAC9BhE,MAAO,WAAWnP,KAAK4B,MAAM+C,IAAI,sBACjC2K,KAAM,qDAAqDuD,UAC3D5C,KAAM,KACNmD,QAAS,CACL,CAAE5K,KAAM,OAAQ6K,MAAO,cAAe/O,MAAO,QAC7C,CAAEkE,KAAM,QAAS6K,MAAO,gBAAiB/O,MAAO,mBAG3BtE,KAAK2S,kBACtC,CAEA,sBAAMA,GACF,MAAMnP,EAAKxD,KAAK4B,MAAM+C,IAAI,MAIpB2K,EAAO,gbAHGtP,KAAK4B,MAAM+C,IAAI,gBAAkB,IAE5CvF,QAAQ,KAAM,SAASA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,2QAoB1DkU,EARSvH,WACX,IAAA,IAASwH,EAAI,EAAGA,EAAI,GAAIA,IAAK,OACnB,IAAI9M,QAAQC,GAAKC,WAAWD,EAAG,KACrC,MAAM8M,EAAK1F,SAAS1M,cAAc,0CAClC,GAAIoS,EAAsC,OAAhCxT,KAAKyT,sBAAsBD,GAAYA,CACrD,CACA,OAAO,MAESE,GACdC,QAAerQ,EAAAA,MAAM6P,OAAO,CAC9BhE,MAAO,WAAW3L,uBAClB8L,OACAW,KAAM,KACNmD,QAAS,CACL,CAAE5K,KAAM,SAAU6K,MAAO,gBAAiB/O,MAAO,MACjD,CACIkE,KAAM,OAAQ6K,MAAO,cACrB3S,QAAS,KACL,MAAM8S,EAAK1F,SAAS1M,cAAc,0CAClC,OAAOoS,EAAKA,EAAGlP,MAAQ,gBAKjCgP,EACFK,gBACE3T,KAAKkR,aAAa,CAAE0C,YAAaD,IACvC3T,KAAKwH,SACT,CAEA,qBAAAiM,CAAsBlL,GAClB,MAAMU,EAAU,CAACT,EAAMU,EAAeV,EAAKvB,UACvC,MAAMkC,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACrBf,EAASgB,aAAaf,EAAMW,EAAOE,EAAK,OACxCd,EAASa,eAAiBb,EAASe,aAAeH,EAAQD,EAC1DX,EAASiB,cAAc,IAAIC,MAAM,WAE/BG,EAAkBC,IACpB,MAAMV,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACfQ,EAAMvB,EAASjE,MAAMyF,UAAUZ,EAAOE,GACxCS,EAAIE,WAAWH,IAAYC,EAAIG,SAASJ,IACxCtB,EAASgB,aAAaO,EAAII,MAAML,EAAQ5C,QAAS4C,EAAQ5C,QAASkC,EAAOE,EAAK,OAC9Ed,EAASa,eAAiBD,EAC1BZ,EAASe,aAAeD,EAAuB,EAAjBQ,EAAQ5C,SAEtCsB,EAASgB,aAAaM,EAAUC,EAAMD,EAASV,EAAOE,EAAK,OAC3Dd,EAASa,eAAiBD,EAAQU,EAAQ5C,OAC1CsB,EAASe,aAAeD,EAAMQ,EAAQ5C,SAGxCkD,EAAWC,IACb,MACMC,EADS9B,EAASjE,MAAMyF,UAAU,EAAGK,GAClBE,YAAY,MAAQ,EAC7C,MAAO,CAAEnB,MAAOkB,EAAW7B,KAAMD,EAASjE,MAAMyF,UAAUM,EAAWD,KAEnEG,EAAgBH,IACH7B,EAASjE,MAAMyF,UAAU,EAAGK,GACpBI,MAAM,WAAa,IAAIvD,OAC9B,GAAM,EAE1BsB,EAASkC,iBAAiB,UAAYC,IAClC,MAAMC,EAAMD,EAAEE,SAAWF,EAAEG,QAG3B,GAAc,UAAVH,EAAEI,KAAmBJ,EAAEK,SAAU,CACjC,MAAM5B,MAAEA,EAAAX,KAAOA,GAAS2B,EAAQ5B,EAASa,gBACnCoB,EAAQhC,EAAKgC,MAAM,wBACzB,GAAIA,EAAO,CACPE,EAAEM,iBACF,MAAMC,EAAST,EAAM,GACfU,EAASV,EAAM,GACrB,GAAIhC,EAAKC,SAAWyC,EAChB3C,EAASgB,aAAa,GAAIJ,EAAOZ,EAASa,eAAgB,WACvD,CACH,MAAM+B,EAAO,SAASnI,KAAKkI,GAAU,GAAGE,SAASF,GAAU,KAAOA,EAClEjC,EAAQ,KAAKgC,IAASE,KAC1B,CACA,MACJ,CACJ,CACA,GAAc,MAAVT,EAAEI,MAAgBH,EAAK,CACvB,MAAMP,EAAM7B,EAASa,eAErB,GADeb,EAASjE,MAAMyF,UAAU,EAAGK,GAChCH,SAAS,QAAUM,EAAaH,EAAM,GAG7C,OAFAM,EAAEM,sBACF/B,EAAQ,WAAY,EAG5B,CACA,GAAc,QAAVyB,EAAEI,MAAiBP,EAAahC,EAASa,gBAY7C,OAAIuB,GAAiB,MAAVD,EAAEI,KAAeJ,EAAEM,sBAAkBpB,EAAe,OAC3De,GAAiB,MAAVD,EAAEI,KAAeJ,EAAEM,sBAAkBpB,EAAe,WAA/D,EAXI,GADAc,EAAEM,iBACEN,EAAEK,SAAU,CACZ,MAAM5B,MAAEA,EAAAX,KAAOA,GAAS2B,EAAQ5B,EAASa,gBACnCiC,EAAW7C,EAAKpJ,QAAQ,UAAW,IACzCmJ,EAASgB,aAAa8B,EAAUlC,EAAOA,EAAQX,EAAKvB,OAAQ,OAC5DsB,EAASa,eAAiBb,EAASe,aAAeH,EAAQkC,EAASpE,MACvE,MACIgC,EAAQ,QAOpB,MAAMqC,EAAQ,CAAE,IAAK,IAAK,IAAK,IAAK,IAAK,KACzC/C,EAASkC,iBAAiB,UAAYC,IAClC,GAAIA,EAAEE,SAAWF,EAAEG,SAAWH,EAAEa,OAAQ,OACxC,MAAMC,EAAQF,EAAMZ,EAAEI,KACtB,IAAKU,EAAO,OACZ,MAAMrC,EAAQZ,EAASa,eACjBC,EAAMd,EAASe,aACrB,GAAIH,IAAUE,EAAK,CACfqB,EAAEM,iBACF,MAAMlB,EAAMvB,EAASjE,MAAMyF,UAAUZ,EAAOE,GAC5Cd,EAASgB,aAAamB,EAAEI,IAAMhB,EAAM0B,EAAOrC,EAAOE,EAAK,OACvDd,EAASa,eAAiBD,EAAQ,EAClCZ,EAASe,aAAeD,EAAM,CAClC,MACIqB,EAAEM,iBACF/B,EAAQyB,EAAEI,IAAMU,EAAO,IAGnC,CAEA,0BAAMqI,GACF,MAAMC,EAAW9T,KAAK4B,MAAM+C,IAAI,YAC5BmP,GAAUtQ,IACVF,EAAAA,MAAMC,UAAU,IAAIwQ,EAAAA,SAAS,CAAEvQ,GAAIsQ,EAAStQ,KAEpD,CAEA,wBAAMwQ,SACiB1Q,EAAAA,MAAM2Q,UAAU,CAC/B9E,MAAO,gBAAgBnP,KAAK4B,MAAM+C,IAAI,QACtC/C,MAAO5B,KAAK4B,MACZqO,KAAM,KACNC,OAAQgE,EAAAA,YAAYC,KAAKjE,eAEd1I,QACnB,CAEA,0BAAM4M,SACIpU,KAAKwF,SAASyC,gBACdjI,KAAKgJ,oBACXhJ,KAAKkD,UAAUwK,OAAO2G,QAAQ,kBAClC,CAEA,mBAAMC,SACIC,EAAAA,kBAAkBvU,KAAM,kBAClC,CAEA,uBAAMwR,CAAkBnL,EAAOgL,GAC3B,OAAO,IAAI5K,QAAS4F,IAChB,IAAImI,GAAS,EACb,MAAMxO,EAAO,IAAIC,cAAY,CACzBC,OAAQ,CAIJG,MAAOA,EAAMxE,IAAI,CAACiN,EAAMyE,KAAA,CACpB9T,MAAOqP,EAAKrP,MACZQ,OAAQ,QAAQsT,IAChBF,MAAOvE,EAAKwC,OAAS,UAAY,GACjC5Q,QAAS,KACL8T,GAAS,EACTxU,KAAK4H,YAAY5B,GACjBqG,EAAQyC,EAAKxK,cAKvBmQ,EAAYzO,EAAK0O,cAAcC,KAAK3O,GAC1CA,EAAK0O,cAAgB,KACjBD,IACKD,IACDxU,KAAK4H,YAAY5B,GACjBqG,EAAQ,QAGhBrM,KAAK+F,SAASC,GACdA,EAAK4O,OAAOvD,EAAMwD,QAASxD,EAAMyD,UAEzC,CAEA,uBAAM9L,SAII,IAAIvC,QAAQC,GAAKC,WAAWD,EAAG,UAC/B1G,KAAK4G,mBACX5G,KAAK6G,kBACL7G,KAAK8G,mBACT,CAEA,eAAMiO,CAAUC,GACZhV,KAAK4B,MAAQoT,EACbhV,KAAKsF,QAAU,IAAIC,EAAAA,kBAAkByP,EAAOrQ,IAAI,OAChD3E,KAAKwF,SAASF,QAAUtF,KAAKsF,QAC7BtF,KAAKwF,SAASyP,sBACRjV,KAAKwH,eACLxH,KAAKwF,SAASyC,gBACdjI,KAAKgJ,mBACf"}
1
+ {"version":3,"file":"TicketPanelView-BGQZ6q2B.js","sources":["../../src/extensions/admin/incidents/ActionCardView.js","../../src/extensions/admin/incidents/TicketPanelView.js"],"sourcesContent":["import View from '@core/View.js';\nimport Modal from '@core/views/feedback/Modal.js';\n\nfunction esc(s) {\n if (!s) return '';\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nconst ALLOWED_REFS = new Set([\n 'incident.RuleSet', 'incident.Incident', 'incident.Event',\n 'incident.Ticket', 'account.GeoLocatedIP'\n]);\n\nconst HANDLER_COLORS = {\n 'incident.rule_approval': { dot: 'accent', label: 'Rule' },\n 'incident.block_confirm': { dot: 'red', label: 'Block' },\n 'incident.rule_update': { dot: 'green', label: 'Update' },\n 'incident.escalate': { dot: 'amber', label: 'Escalate' }\n};\n\nclass ActionCardView extends View {\n constructor(options = {}) {\n super({\n className: 'action-card-view',\n ...options\n });\n this.action = options.action;\n this.noteId = options.noteId;\n this.ticketStatus = options.ticketStatus;\n }\n\n get isResolved() {\n return !!this.action?.resolved;\n }\n\n get isContext() {\n return this.action?.type === 'context';\n }\n\n get isClosed() {\n return this.ticketStatus === 'closed' || this.ticketStatus === 'resolved';\n }\n\n get handlerConfig() {\n return HANDLER_COLORS[this.action?.handler] || { dot: 'accent', label: 'Action' };\n }\n\n onBeforeRender() {\n const cfg = this.handlerConfig;\n this.dotClass = cfg.dot;\n\n if (this.isContext) {\n this._buildContextTemplate();\n } else if (this.isResolved) {\n this._buildResolvedTemplate();\n } else {\n this._buildPendingTemplate();\n }\n }\n\n onActionToggleCompact() {\n const acEl = this.element?.querySelector('.ac.resolved');\n if (acEl) acEl.classList.toggle('compact');\n }\n\n _buildContextTemplate() {\n const refs = (this.action.references || []).filter(ref => ALLOWED_REFS.has(ref.model));\n const refItems = refs.map(ref => {\n const label = esc(ref.label) || `${esc(ref.model.split('.').pop())} #${esc(ref.pk)}`;\n return `<span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(ref.model)}\" data-pk=\"${esc(ref.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }).join('');\n\n this.template = `\n <div class=\"ac ac-context\">\n <div class=\"ac-top\">\n <span class=\"ac-dot context\"></span>\n <span class=\"ac-label\">Referenced models</span>\n </div>\n <div class=\"ac-detail\">${refItems}</div>\n </div>\n `;\n }\n\n _buildResolvedTemplate() {\n const resolution = this.action.resolution || 'approved';\n const badgeClass = resolution === 'approved' ? 'approved' : 'denied';\n const badgeText = resolution === 'approved' ? 'Approved' : 'Denied';\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<div class=\"ac-detail\"><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span></div>`;\n }\n this.template = `\n <div class=\"ac resolved compact\">\n <div class=\"ac-top\" data-action=\"toggle-compact\" title=\"Click to toggle\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n <span class=\"ac-badge ${badgeClass}\">${badgeText}</span>\n <i class=\"bi bi-chevron-down ac-chevron\"></i>\n </div>\n ${refHtml}\n </div>\n `;\n }\n\n _buildPendingTemplate() {\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<br><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }\n const detail = esc(this.action.context?.detail);\n const disabledAttr = this.isClosed ? ' disabled' : '';\n this.template = `\n <div class=\"ac\">\n <div class=\"ac-top\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n </div>\n <div class=\"ac-detail\">${detail}${refHtml}</div>\n <div class=\"ac-foot\">\n <button class=\"btn-approve\" data-action=\"approve\"${disabledAttr}>Approve</button>\n <button class=\"btn-deny\" data-action=\"deny\"${disabledAttr}>Deny</button>\n </div>\n </div>\n `;\n }\n\n async onActionOpenRef(_event, el) {\n const modelRef = el.dataset.model;\n const pk = el.dataset.pk;\n if (!ALLOWED_REFS.has(modelRef) || !/^\\d+$/.test(pk)) return;\n const app = this.getApp();\n const ModelClass = app?.getModelByRef(modelRef);\n if (ModelClass?.VIEW_CLASS) {\n Modal.showModel(new ModelClass({ id: pk }));\n }\n }\n\n onActionApprove() {\n this.emit('action:respond', { noteId: this.noteId, action: 'approve', handler: this.action.handler, context: this.action.context });\n }\n\n onActionDeny() {\n this.emit('action:respond', { noteId: this.noteId, action: 'deny', handler: this.action.handler, context: this.action.context });\n }\n}\n\nexport { HANDLER_COLORS };\nexport default ActionCardView;\n","import View from '@core/View.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Modal from '@core/views/feedback/Modal.js';\nimport { Ticket, TicketForms, TicketCategories } from '@ext/admin/models/Tickets.js';\nimport { Incident } from '@ext/admin/models/Incident.js';\nimport { GroupList } from '@core/models/Group.js';\nimport { UserList } from '@core/models/User.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport ActionCardView from './ActionCardView.js';\nimport { openAssistantChat } from '../assistant/AssistantContextChat.js';\nimport rest from '@core/Rest.js';\n\n\nconst STATUS_OPTIONS = ['new', 'open', 'in_progress', 'pending', 'resolved', 'qa', 'closed', 'ignored'];\nconst STATUS_PILL = {\n new: 'pill-new',\n open: 'pill-open',\n in_progress: 'pill-prog',\n pending: 'pill-prog',\n resolved: 'pill-resolved',\n qa: 'pill-open',\n closed: 'pill-closed',\n ignored: 'pill-closed'\n};\nconst PRIORITY_OPTIONS = [\n { value: 10, label: 'P10 — Critical' },\n { value: 9, label: 'P9 — Severe' },\n { value: 8, label: 'P8 — High' },\n { value: 7, label: 'P7 — Elevated' },\n { value: 5, label: 'P5 — Normal' },\n { value: 3, label: 'P3 — Low' },\n { value: 1, label: 'P1 — Info' }\n];\n\nclass TicketPanelView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-panel-view',\n ...options\n });\n this.model = options.model || new Ticket(options.data || {});\n }\n\n onBeforeRender() {\n const status = this.model.get('status') || 'new';\n this.statusPill = STATUS_PILL[status] || 'pill-closed';\n this.statusLabel = (status || '').replace(/_/g, ' ');\n this.priorityLabel = `P${this.model.get('priority') || 5}`;\n this.assigneeName = this.model.get('assignee.display_name') || this.model.get('assignee') || 'Unassigned';\n this.categoryLabel = this.model.get('category') || 'ticket';\n this.groupName = this.model.get('group.name') || this.model.get('group') || 'None';\n this.hasDescription = !!this.model.get('description');\n this.hasIncident = !!(this.model.get('incident') && typeof this.model.get('incident') === 'object' && this.model.get('incident').id);\n this.priorityColor = (this.model.get('priority') || 5) >= 7 ? 'var(--bs-danger)' : 'var(--bs-secondary-color)';\n\n this.template = `\n <style>\n .ticket-panel-view { height: 100%; display: flex; flex-direction: column; }\n .tp-header { padding: 10px 16px 6px; border-bottom: 1px solid var(--bs-border-color-translucent); flex-shrink: 0; }\n .tp-title-row { display: flex; align-items: flex-start; gap: 6px; }\n .tp-title { font-size: 0.88rem; font-weight: 600; color: var(--bs-emphasis-color); line-height: 1.3; flex: 1; min-width: 0; cursor: ${this.hasDescription ? 'pointer' : 'default'}; transition: color 0.12s; }\n ${this.hasDescription ? '.tp-title:hover { color: var(--bs-primary); }' : ''}\n .tp-title i { font-size: 0.6rem; vertical-align: middle; margin-left: 3px; opacity: 0; transition: opacity 0.12s; }\n .tp-title:hover i { opacity: 0.6; }\n .tp-btns { display: flex; gap: 2px; align-items: center; flex-shrink: 0; }\n .tp-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: none; background: none; color: var(--bs-secondary-color); border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.12s; }\n .tp-btn:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }\n .tp-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n .tp-id { font-family: var(--bs-font-monospace); font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.66rem; font-weight: 500; cursor: pointer; transition: filter 0.12s; }\n .tp-pill:hover { filter: brightness(0.9); }\n .tp-pill-new { background: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }\n .tp-pill-open { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-pill-prog { background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }\n .tp-pill-closed { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }\n .tp-pill-resolved { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-time { font-size: 0.66rem; color: var(--bs-secondary-color); }\n .tp-desc-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--bs-primary); cursor: pointer; padding: 2px 8px; border-radius: 5px; background: rgba(var(--bs-primary-rgb), 0.08); transition: all 0.12s; }\n .tp-desc-chip:hover { background: rgba(var(--bs-primary-rgb), 0.16); }\n .tp-desc-chip i { font-size: 0.7rem; }\n .tp-meta { display: flex; align-items: center; gap: 3px; margin-top: 5px; }\n .tp-fields { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }\n .tp-field { display: inline-flex; align-items: center; gap: 4px; font-size: 0.72rem; color: var(--bs-secondary-color); padding: 2px 7px; border-radius: 5px; cursor: pointer; transition: all 0.12s; border: 1px solid transparent; }\n .tp-field:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }\n .tp-field i { font-size: 0.68rem; }\n .tp-field .caret { font-size: 0.55rem; opacity: 0; transition: opacity 0.12s; margin-left: -1px; }\n .tp-field:hover .caret { opacity: 0.6; }\n .tp-sep { color: var(--bs-secondary-color); font-size: 0.6rem; margin: 0 1px; user-select: none; }\n\n .tp-linked { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--bs-border-color-translucent); font-size: 0.75rem; color: var(--bs-secondary-color); flex-shrink: 0; }\n .tp-linked i { color: var(--bs-warning); font-size: 0.72rem; }\n .tp-linked a { color: var(--bs-primary); text-decoration: none; font-weight: 500; }\n .tp-linked a:hover { text-decoration: underline; }\n .tp-linked .lpill { font-size: 0.62rem; padding: 0 5px; border-radius: 3px; background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); font-weight: 500; }\n\n .tp-conv { flex: 1; overflow-y: auto; min-height: 0; }\n .tp-conv .chat-container { border: none; border-radius: 0; }\n .tp-conv .chat-messages { padding: 6px 0; }\n .tp-conv .chat-input-wrapper { display: none; }\n .tp-conv .message-item { position: relative; }\n .tp-conv .tp-edit-btn { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; display: none; align-items: center; justify-content: center; border: none; background: var(--bs-tertiary-bg); color: var(--bs-secondary-color); border-radius: 5px; cursor: pointer; font-size: 0.72rem; transition: all 0.12s; }\n .tp-conv .tp-edit-btn:hover { background: var(--bs-secondary-bg); color: var(--bs-body-color); }\n .tp-conv .message-item:hover .tp-edit-btn { display: flex; }\n .tp-conv .message-text { white-space: normal; }\n .tp-conv .message-text p { margin-bottom: 6px; }\n .tp-conv .message-text p:last-child { margin-bottom: 0; }\n .tp-conv .message-text h1,\n .tp-conv .message-text h2,\n .tp-conv .message-text h3,\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-weight: 600; margin-top: 10px; margin-bottom: 4px; line-height: 1.3; }\n .tp-conv .message-text h1 { font-size: 1.05rem; }\n .tp-conv .message-text h2 { font-size: 1rem; }\n .tp-conv .message-text h3 { font-size: 0.95rem; }\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-size: 0.88rem; }\n .tp-conv .message-text h1:first-child,\n .tp-conv .message-text h2:first-child,\n .tp-conv .message-text h3:first-child { margin-top: 0; }\n .tp-conv .message-text hr { margin: 4px 0; opacity: 0.15; }\n .tp-conv .message-text ul,\n .tp-conv .message-text ol { padding-left: 20px; margin-top: 2px; margin-bottom: 6px; }\n .tp-conv .message-text li { margin-bottom: 2px; }\n .tp-conv .message-text pre { background: var(--bs-tertiary-bg); border-radius: 6px; padding: 10px 14px; margin: 8px 0; font-size: 0.8rem; overflow-x: auto; }\n .tp-conv .message-text code { font-size: 0.85em; padding: 1px 5px; background: var(--bs-tertiary-bg); border-radius: 4px; }\n .tp-conv .message-text pre code { padding: 0; background: none; }\n .tp-conv .message-text table { width: 100%; margin: 8px 0; border-collapse: collapse; font-size: 0.82rem; }\n .tp-conv .message-text th,\n .tp-conv .message-text td { padding: 5px 8px; border: 1px solid var(--bs-border-color); text-align: left; }\n .tp-conv .message-text th { background: var(--bs-tertiary-bg); font-weight: 600; }\n .tp-conv .message-text blockquote { margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--bs-border-color); color: var(--bs-secondary-color); }\n\n .tp-action-area { padding: 0; }\n\n .tp-input { border-top: 1px solid var(--bs-border-color-translucent); padding: 10px 16px; flex-shrink: 0; }\n .tp-input-wrap { display: flex; align-items: flex-end; gap: 8px; }\n .tp-input textarea { flex: 1; font-size: 0.8rem; border: 1px solid var(--bs-border-color); border-radius: 8px; padding: 7px 10px; resize: none; background: var(--bs-body-bg); color: var(--bs-body-color); outline: none; transition: border-color 0.15s; font-family: inherit; max-height: 160px; overflow-y: auto; }\n .tp-input textarea:focus { border-color: var(--bs-primary); }\n .tp-input textarea::placeholder { color: var(--bs-secondary-color); }\n .tp-send-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: var(--bs-primary); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 0.85rem; transition: filter 0.12s; }\n .tp-send-btn:hover { filter: brightness(1.1); }\n .tp-input-hint { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-input-hint i { font-size: 0.75rem; }\n .tp-input.tp-dragover { background: rgba(var(--bs-primary-rgb), 0.04); }\n .tp-input.tp-dragover textarea { border-color: var(--bs-primary); border-style: dashed; }\n .tp-attachments { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }\n .tp-attach-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 6px; font-size: 0.72rem; color: var(--bs-body-color); }\n .tp-attach-chip i { font-size: 0.68rem; }\n .tp-attach-chip .remove { cursor: pointer; color: var(--bs-secondary-color); margin-left: 2px; }\n .tp-attach-chip .remove:hover { color: var(--bs-danger); }\n .tp-attach-chip.uploading { opacity: 0.6; }\n .tp-conv .message-content.tp-collapsed { max-height: var(--tp-collapse-h, 52px); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); mask-image: linear-gradient(to bottom, black 60%, transparent 100%); }\n .tp-conv .message-item:has(.tp-show-more) { flex-wrap: wrap; }\n .tp-show-more { background: none; border: none; padding: 2px 0; margin: -2px 0 0 48px; font-size: 0.7rem; color: var(--bs-secondary-color); cursor: pointer; text-align: left; width: calc(100% - 48px); }\n .tp-show-more:hover { color: var(--bs-body-color); }\n </style>\n\n <div class=\"tp-header\">\n <div class=\"tp-title-row\">\n <div class=\"tp-title\" ${this.hasDescription ? 'data-action=\"show-description\"' : ''} ${this.hasDescription ? 'title=\"View full description\"' : ''}>\n {{model.title}}${this.hasDescription ? ' <i class=\"bi bi-arrow-up-right-square\"></i>' : ''}\n </div>\n <div class=\"tp-btns\">\n <div data-container=\"panel-menu\"></div>\n </div>\n </div>\n <div class=\"tp-sub\">\n <span class=\"tp-id\">#{{model.id}}</span>\n <span class=\"tp-pill tp-${this.statusPill}\" data-action=\"change-status\" title=\"Change status\">{{statusLabel}} <i class=\"bi bi-chevron-down\" style=\"font-size:0.5rem;\"></i></span>\n <span class=\"tp-time\"><i class=\"bi bi-clock\"></i> {{model.created|relative}}</span>\n <span class=\"tp-desc-chip\" data-action=\"show-description\" title=\"${this.hasDescription ? 'View / edit description' : 'Add description'}\"><i class=\"bi bi-file-text\"></i> ${this.hasDescription ? 'Description' : 'Add description'}</span>\n </div>\n <div class=\"tp-meta\">\n <div class=\"tp-fields\">\n <span class=\"tp-field\" data-action=\"change-priority\" title=\"Change priority\">\n <i class=\"bi bi-flag-fill\" style=\"color:${this.priorityColor};\"></i>{{priorityLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-assignee\" title=\"Assign\">\n <i class=\"bi bi-person\"></i>{{assigneeName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-category\" title=\"Change category\">\n <i class=\"bi bi-tag\"></i>{{categoryLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-group\" title=\"Change group\">\n <i class=\"bi bi-people\"></i>{{groupName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n </div>\n </div>\n </div>\n\n {{#hasIncident|bool}}\n <div class=\"tp-linked\">\n <i class=\"bi bi-exclamation-triangle-fill\"></i>\n <a href=\"#\" data-action=\"view-incident\">Incident #{{model.incident.id}}</a>\n {{#model.incident.status}}<span class=\"lpill\">{{model.incident.status}}</span>{{/model.incident.status}}\n {{#model.incident.event_count}}<span>&middot; {{model.incident.event_count}} events</span>{{/model.incident.event_count}}\n </div>\n {{/hasIncident|bool}}\n\n <div class=\"tp-conv\">\n <div data-container=\"chat-area\"></div>\n </div>\n\n <div class=\"tp-input\" data-ref=\"drop-zone\">\n <div class=\"tp-attachments\" data-ref=\"attachments\"></div>\n <div class=\"tp-input-wrap\">\n <textarea rows=\"2\" placeholder=\"Add a note...\" data-ref=\"note-textarea\"></textarea>\n <button class=\"tp-send-btn\" data-action=\"send-note\" title=\"Send\"><i class=\"bi bi-arrow-up\"></i></button>\n </div>\n <div class=\"tp-input-hint\">\n <i class=\"bi bi-paperclip\"></i> Drag &amp; drop files to attach\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.adapter = new TicketNoteAdapter(this.model.get('id'));\n\n this.chatView = new ChatView({\n containerId: 'chat-area',\n adapter: this.adapter,\n theme: 'compact',\n currentUserId: this._getCurrentUserId(),\n showInput: false\n });\n this.addChild(this.chatView);\n\n const menu = new ContextMenu({\n containerId: 'panel-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots',\n btnClass: 'tp-btn',\n items: [\n { label: 'Ask AI', action: 'ask-ai', icon: 'bi-robot' },\n { type: 'divider' },\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Refresh Notes', action: 'refresh-notes', icon: 'bi-arrow-clockwise' },\n { type: 'divider' },\n { label: 'Close Window', action: 'close', icon: 'bi-x-lg' }\n ]\n }\n });\n this.addChild(menu);\n }\n\n async onAfterRender() {\n this._setupTextarea();\n this._setupDragDrop();\n // Wait one microtask so child message-view DOM is settled before we\n // place inline action cards and edit buttons.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async _loadActionCards() {\n this._cleanupActionCards();\n\n const messages = this.chatView.messages || [];\n if (!messages.length) return;\n\n for (const msg of messages) {\n if (!msg.action || typeof msg.action !== 'object') continue;\n\n const msgView = this.chatView.messageViews.get(msg.id);\n if (!msgView?.element) continue;\n\n const card = new ActionCardView({\n action: msg.action,\n noteId: msg.id,\n ticketStatus: this.model.get('status')\n });\n // Pending approvals get the respond handler\n if (msg.action.type !== 'context' && !msg.action.resolved) {\n card.on('action:respond', (data) => this._handleActionResponse(data));\n }\n this.addChild(card);\n await card.render();\n msgView.element.after(card.element);\n }\n }\n\n _cleanupActionCards() {\n for (const id in this.children) {\n const child = this.children[id];\n if (child instanceof ActionCardView) {\n this.removeChild(child);\n }\n }\n }\n\n async _handleActionResponse(data) {\n const actionNote = { action: { handler: data.handler, context: data.context } };\n const app = this.getApp();\n app?.showLoading();\n try {\n await this.adapter.addActionResponse(actionNote, data.action);\n await this.model.fetch();\n await this.chatView.refresh();\n this.render();\n } finally {\n app?.hideLoading();\n }\n }\n\n _getCurrentUserId() {\n const app = this.getApp();\n return app?.activeUser?.id || app?.getActiveUser?.()?.id || null;\n }\n\n // ── Actions ──\n\n onActionClose() {\n this.emit('panel:close');\n }\n\n async onActionSendNote() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n const text = textarea?.value?.trim();\n const files = this._stagedFiles || [];\n if (!text && !files.length) return;\n textarea.value = '';\n textarea.style.height = '';\n this._stagedFiles = [];\n this._renderAttachments();\n await this.adapter.addNote({ text: text || '', files });\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n _setupTextarea() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n if (!textarea) return;\n\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n textarea.scrollTop = textarea.scrollHeight;\n };\n\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n\n // Enter → submit (unless Shift held)\n if (e.key === 'Enter' && !e.shiftKey && !mod) {\n e.preventDefault();\n this.onActionSendNote();\n return;\n }\n\n // Shift+Enter → auto-continue list bullets\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n\n // ``` auto-close: after typing third backtick, insert fence pair\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n\n // Tab indent/dedent inside code fences\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n\n // Ctrl/Cmd+B → bold\n if (mod && e.key === 'b') {\n e.preventDefault();\n _wrapSelection('**');\n return;\n }\n\n // Ctrl/Cmd+I → italic\n if (mod && e.key === 'i') {\n e.preventDefault();\n _wrapSelection('*');\n return;\n }\n });\n\n // Auto-close pairs: ( [ \"\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n\n textarea.addEventListener('input', () => {\n textarea.style.height = '';\n textarea.style.height = Math.min(textarea.scrollHeight, 160) + 'px';\n });\n }\n\n _setupDragDrop() {\n const zone = this.element?.querySelector('[data-ref=\"drop-zone\"]');\n if (!zone) return;\n this._stagedFiles = this._stagedFiles || [];\n let counter = 0;\n zone.addEventListener('dragenter', (e) => { e.preventDefault(); counter++; zone.classList.add('tp-dragover'); });\n zone.addEventListener('dragleave', () => { counter--; if (counter <= 0) { counter = 0; zone.classList.remove('tp-dragover'); } });\n zone.addEventListener('dragover', (e) => e.preventDefault());\n zone.addEventListener('drop', async (e) => {\n e.preventDefault();\n counter = 0;\n zone.classList.remove('tp-dragover');\n const files = Array.from(e.dataTransfer?.files || []);\n if (!files.length) return;\n const { File: FileModel } = await import('@core/models/Files.js');\n for (const file of files) {\n const chipId = Date.now() + Math.random();\n this._addAttachChip(chipId, file.name, true);\n try {\n const fileModel = new FileModel();\n await fileModel.upload({ file, showToast: false });\n this._stagedFiles.push(fileModel);\n this._updateAttachChip(chipId, fileModel.get('name') || file.name, fileModel);\n } catch (err) {\n console.error('File upload failed:', err);\n this._removeAttachChip(chipId);\n this.getApp()?.toast?.error?.('Upload failed: ' + file.name);\n }\n }\n });\n }\n\n _addAttachChip(id, name, uploading) {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (!container) return;\n const chip = document.createElement('span');\n chip.className = 'tp-attach-chip' + (uploading ? ' uploading' : '');\n chip.dataset.chipId = id;\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}` +\n (uploading ? '' : '<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>');\n if (!uploading) {\n chip.querySelector('.remove').addEventListener('click', () => {\n this._removeAttachChip(id);\n });\n }\n container.appendChild(chip);\n }\n\n _updateAttachChip(id, name, fileModel) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (!chip) return;\n chip.classList.remove('uploading');\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>`;\n chip.querySelector('.remove').addEventListener('click', () => {\n this._stagedFiles = (this._stagedFiles || []).filter(f => f !== fileModel);\n chip.remove();\n });\n }\n\n _removeAttachChip(id) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (chip) chip.remove();\n }\n\n _renderAttachments() {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (container) container.innerHTML = '';\n }\n\n _escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n _addEditButtons() {\n const userId = this._getCurrentUserId();\n if (!userId || !this.chatView?.messageViews) return;\n const msgById = new Map((this.chatView.messages || []).map(m => [m.id, m]));\n this.chatView.messageViews.forEach((view, msgId) => {\n const msg = msgById.get(msgId);\n const item = view?.element?.querySelector('.message-item');\n if (!item) return;\n item.querySelectorAll('.tp-edit-btn').forEach(b => b.remove());\n if (!msg || msg.author?.id !== userId) return;\n const btn = document.createElement('button');\n btn.className = 'tp-edit-btn';\n btn.title = 'Edit note';\n btn.innerHTML = '<i class=\"bi bi-pencil\"></i>';\n btn.addEventListener('click', (e) => { e.stopPropagation(); this._editNote(msg); });\n item.appendChild(btn);\n });\n }\n\n _setupCollapsible() {\n const container = this.element?.querySelector('[data-container=\"chat-area\"]');\n if (!container) return;\n container.querySelectorAll('.tp-show-more').forEach(b => b.remove());\n container.querySelectorAll('.tp-collapsed').forEach(el => el.classList.remove('tp-collapsed'));\n setTimeout(() => {\n const MAX = 52;\n container.querySelectorAll('.message-content').forEach(body => {\n if (body.scrollHeight <= MAX) return;\n body.classList.add('tp-collapsed');\n body.style.setProperty('--tp-collapse-h', MAX + 'px');\n const btn = document.createElement('button');\n btn.className = 'tp-show-more';\n btn.textContent = 'Show more';\n btn.addEventListener('click', () => {\n const collapsed = body.classList.toggle('tp-collapsed');\n btn.textContent = collapsed ? 'Show more' : 'Show less';\n });\n body.after(btn);\n });\n }, 150);\n }\n\n async _editNote(msg) {\n const metaJson = Object.keys(msg._metadata || {}).length\n ? JSON.stringify(msg._metadata, null, 2) : '';\n const data = await Modal.form({\n title: 'Edit Note',\n icon: 'bi-pencil',\n size: 'lg',\n fields: [{\n type: 'tabset',\n tabs: [\n {\n label: 'Note',\n fields: [{\n name: 'note', type: 'textarea', label: 'Note',\n required: true, cols: 12, rows: 8,\n value: msg._rawContent || msg.content\n }]\n },\n {\n label: 'Metadata',\n fields: [{\n name: 'metadata_json', type: 'json', label: 'Metadata (JSON)',\n cols: 12, rows: 10,\n value: metaJson,\n help: 'Action metadata — e.g. { \"action\": { \"handler\": \"incident.rule_approval\", \"label\": \"...\", \"context\": { ... } } }'\n }]\n }\n ]\n }]\n });\n if (!data) return;\n const { TicketNote } = await import('@ext/admin/models/Tickets.js');\n const note = new TicketNote({ id: msg.id });\n const payload = { note: data.note };\n if (data.metadata_json) {\n payload.metadata = typeof data.metadata_json === 'string'\n ? JSON.parse(data.metadata_json) : data.metadata_json;\n }\n await note.save(payload);\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n async _saveAndSync(patch) {\n // Some endpoints don't echo the updated record on PUT — Model.save() only\n // merges response.data.data into local state. Re-fetch so the panel and\n // the shared list collection both see the new values. Also refresh notes\n // since the backend may create a status_change note as a side effect.\n await this.model.save(patch);\n await this.model.fetch();\n if (this.chatView) {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n }\n\n async onActionChangeStatus(event) {\n const items = STATUS_OPTIONS.map(s => ({\n label: s.replace(/_/g, ' '),\n value: s,\n active: s === this.model.get('status')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ status: result });\n this.render();\n }\n\n async onActionChangePriority(event) {\n const items = PRIORITY_OPTIONS.map(p => ({\n label: p.label,\n value: p.value,\n active: p.value === this.model.get('priority')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ priority: parseInt(result) });\n this.render();\n }\n\n async onActionChangeAssignee() {\n const data = await Modal.form({\n title: 'Assign User',\n icon: 'bi-person-plus',\n size: 'sm',\n fields: [{\n name: 'assignee', type: 'collection', label: 'User',\n Collection: UserList, labelField: 'display_name', valueField: 'id',\n required: true, cols: 12,\n value: this.model.get('assignee')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ assignee: data.assignee });\n this.render();\n }\n\n async onActionChangeCategory(event) {\n const items = Object.entries(TicketCategories).map(([key, label]) => ({\n label,\n value: key,\n active: key === this.model.get('category')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ category: result });\n this.render();\n }\n\n async onActionChangeGroup() {\n const data = await Modal.form({\n title: 'Change Group',\n icon: 'bi-people',\n size: 'sm',\n fields: [{\n name: 'group', type: 'collection', label: 'Group',\n Collection: GroupList, labelField: 'name', valueField: 'id',\n required: false, cols: 12,\n value: this.model.get('group')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ group: data.group });\n this.render();\n }\n\n async onActionShowDescription() {\n const raw = this.model.get('description') || '';\n if (!raw) {\n // No description yet → jump straight to edit\n return this._editDescription();\n }\n let rendered = false;\n let html = '';\n try {\n const resp = await rest.post('/api/docit/render', { markdown: raw });\n html = resp?.data?.data?.html || resp?.data?.html || '';\n rendered = !!html;\n } catch (_e) { /* fallback to escaped text */ }\n if (!rendered) {\n const div = document.createElement('div');\n div.textContent = raw;\n html = `<pre style=\"white-space:pre-wrap;\">${div.innerHTML}</pre>`;\n }\n const choice = await Modal.dialog({\n title: `Ticket #${this.model.get('id')} — Description`,\n body: `<div style=\"font-size:0.85rem; line-height:1.65;\">${html}</div>`,\n size: 'lg',\n buttons: [\n { text: 'Edit', class: 'btn-primary', value: 'edit' },\n { text: 'Close', class: 'btn-secondary', value: 'close' }\n ]\n });\n if (choice === 'edit') await this._editDescription();\n }\n\n async _editDescription() {\n const id = this.model.get('id');\n const current = this.model.get('description') || '';\n const escaped = current\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const body = `\n <div class=\"tp-desc-edit\">\n <textarea data-ref=\"desc-textarea\" rows=\"16\" placeholder=\"Description (markdown supported)...\"\n style=\"width:100%; font-family: var(--bs-font-monospace); font-size: 0.85rem; padding: 10px 12px; border: 1px solid var(--bs-border-color); border-radius: 8px; background: var(--bs-body-bg); color: var(--bs-body-color); resize: vertical; outline: none;\">${escaped}</textarea>\n <div class=\"text-muted small mt-1\">\n Markdown supported. Cmd/Ctrl+B = bold · Cmd/Ctrl+I = italic · Shift+Enter continues lists · \\`\\`\\` opens a code block\n </div>\n </div>\n `;\n // Modal.dialog only mounts strings or Views — pass HTML string and find\n // the textarea after the modal renders, then wire shortcuts.\n const wireUp = async () => {\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 50));\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n if (ta) { this._wireMarkdownTextarea(ta); return ta; }\n }\n return null;\n };\n const wirePromise = wireUp();\n const choice = await Modal.dialog({\n title: `Ticket #${id} — Edit Description`,\n body,\n size: 'lg',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', value: null },\n {\n text: 'Save', class: 'btn-primary',\n handler: () => {\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n return ta ? ta.value : null;\n }\n }\n ]\n });\n await wirePromise; // ensure cleanup of the polling\n if (choice === null || choice === undefined) return;\n await this._saveAndSync({ description: choice });\n this.render();\n }\n\n _wireMarkdownTextarea(textarea) {\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n };\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n // Shift+Enter → auto-continue list bullets (plain Enter saves the form,\n // so list continuation moves to Shift+Enter here too)\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n if (mod && e.key === 'b') { e.preventDefault(); _wrapSelection('**'); return; }\n if (mod && e.key === 'i') { e.preventDefault(); _wrapSelection('*'); return; }\n });\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n }\n\n async onActionViewIncident() {\n const incident = this.model.get('incident');\n if (incident?.id) {\n Modal.showModel(new Incident({ id: incident.id }));\n }\n }\n\n async onActionEditTicket() {\n const resp = await Modal.modelForm({\n title: `Edit Ticket #${this.model.get('id')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) this.render();\n }\n\n async onActionRefreshNotes() {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n this.getApp()?.toast?.success('Notes refreshed');\n }\n\n async onActionAskAi() {\n await openAssistantChat(this, 'incident.Ticket');\n }\n\n async _showInlineSelect(items, event) {\n return new Promise((resolve) => {\n let picked = false;\n const menu = new ContextMenu({\n config: {\n // Each item needs a unique action — ContextMenu identifies the\n // clicked item by data-item-action, and find() returns the FIRST\n // match, so reusing one action name maps every click to item[0].\n items: items.map((item, i) => ({\n label: item.label,\n action: `pick-${i}`,\n class: item.active ? 'fw-bold' : '',\n handler: () => {\n picked = true;\n this.removeChild(menu);\n resolve(item.value);\n }\n }))\n }\n });\n const origClose = menu.closeDropdown.bind(menu);\n menu.closeDropdown = () => {\n origClose();\n if (!picked) {\n this.removeChild(menu);\n resolve(null);\n }\n };\n this.addChild(menu);\n menu.openAt(event.clientX, event.clientY);\n });\n }\n\n async _afterChatRefresh() {\n // ChatView.refresh() doesn't await each message view's render — its inner\n // _renderChildren calls messageView.render(false) without await. Wait one\n // microtask so the message-item DOM is in place before we touch it.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async setTicket(ticket) {\n this.model = ticket;\n this.adapter = new TicketNoteAdapter(ticket.get('id'));\n this.chatView.adapter = this.adapter;\n this.chatView.clearMessages();\n await this.render();\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n}\n\nexport default TicketPanelView;\n"],"names":["esc","s","String","replace","ALLOWED_REFS","Set","HANDLER_COLORS","dot","label","ActionCardView","View","constructor","options","super","className","this","action","noteId","ticketStatus","isResolved","resolved","isContext","type","isClosed","handlerConfig","handler","onBeforeRender","cfg","dotClass","_buildContextTemplate","_buildResolvedTemplate","_buildPendingTemplate","onActionToggleCompact","acEl","element","querySelector","classList","toggle","refItems","references","filter","ref","has","model","map","split","pop","pk","join","template","resolution","badgeClass","badgeText","target","context","refHtml","detail","disabledAttr","onActionOpenRef","_event","el","modelRef","dataset","test","app","getApp","ModelClass","getModelByRef","VIEW_CLASS","Modal","showModel","id","onActionApprove","emit","onActionDeny","STATUS_OPTIONS","STATUS_PILL","new","open","in_progress","pending","qa","closed","ignored","PRIORITY_OPTIONS","value","TicketPanelView","Ticket","data","status","get","statusPill","statusLabel","priorityLabel","assigneeName","categoryLabel","groupName","hasDescription","hasIncident","priorityColor","onInit","adapter","TicketNoteAdapter","chatView","ChatView","containerId","theme","currentUserId","_getCurrentUserId","showInput","addChild","menu","ContextMenu","config","icon","btnClass","items","onAfterRender","_setupTextarea","_setupDragDrop","Promise","r","setTimeout","_loadActionCards","_addEditButtons","_setupCollapsible","_cleanupActionCards","messages","length","msg","msgView","messageViews","card","on","_handleActionResponse","render","after","children","child","removeChild","actionNote","showLoading","addActionResponse","fetch","refresh","hideLoading","activeUser","getActiveUser","onActionClose","onActionSendNote","textarea","text","trim","files","_stagedFiles","style","height","_renderAttachments","addNote","_afterChatRefresh","_insert","cursorOffset","start","selectionStart","end","selectionEnd","setRangeText","dispatchEvent","Event","scrollTop","scrollHeight","_wrapSelection","wrapper","sel","substring","startsWith","endsWith","slice","_lineAt","pos","lineStart","lastIndexOf","_inCodeFence","match","addEventListener","e","mod","ctrlKey","metaKey","key","shiftKey","preventDefault","indent","bullet","next","parseInt","stripped","PAIRS","altKey","close","Math","min","zone","counter","add","remove","async","Array","from","dataTransfer","File","FileModel","resolve","then","require","n","Files","file","chipId","Date","now","random","_addAttachChip","name","fileModel","upload","showToast","push","_updateAttachChip","err","console","error","_removeAttachChip","toast","uploading","container","chip","document","createElement","innerHTML","_escapeHtml","appendChild","f","str","div","textContent","userId","msgById","Map","m","forEach","view","msgId","item","querySelectorAll","b","author","btn","title","stopPropagation","_editNote","body","setProperty","MAX","collapsed","metaJson","Object","keys","_metadata","JSON","stringify","form","size","fields","tabs","required","cols","rows","_rawContent","content","help","TicketNote","Tickets","note","payload","metadata_json","metadata","parse","save","_saveAndSync","patch","onActionChangeStatus","event","active","result","_showInlineSelect","onActionChangePriority","p","priority","onActionChangeAssignee","Collection","UserList","labelField","valueField","assignee","onActionChangeCategory","entries","TicketCategories","category","onActionChangeGroup","GroupList","group","onActionShowDescription","raw","_editDescription","rendered","html","resp","rest","post","markdown","_e","dialog","buttons","class","wirePromise","i","ta","_wireMarkdownTextarea","wireUp","choice","description","onActionViewIncident","incident","Incident","onActionEditTicket","modelForm","TicketForms","edit","onActionRefreshNotes","success","onActionAskAi","openAssistantChat","picked","origClose","closeDropdown","bind","openAt","clientX","clientY","setTicket","ticket","clearMessages"],"mappings":"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"}