ooxml-ui 1.12.3 → 1.14.0

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 (325) hide show
  1. package/custom-elements.json +395 -5
  2. package/dist/chrome/status-layout.d.ts +23 -0
  3. package/dist/{chunk-S6ZXU442.js → chunk-7E3HU7RL.js} +1 -1
  4. package/dist/{chunk-PXIYHFZ5.js → chunk-ANPFMQ6E.js} +1 -1
  5. package/dist/{chunk-UKXJLI3N.js → chunk-C3MXKNNK.js} +2431 -311
  6. package/dist/{chunk-MML6EKFH.js → chunk-C5F7454A.js} +5 -5
  7. package/dist/{chunk-OGNNNTKI.js → chunk-CWBSXK7P.js} +1 -1
  8. package/dist/{chunk-LHS5DYLT.js → chunk-DSQBXLMO.js} +1 -1
  9. package/dist/{chunk-L7JV5FVE.js → chunk-KNC2UEUR.js} +1 -1
  10. package/dist/{chunk-735S7V4M.js → chunk-LAVVSCRJ.js} +415 -116
  11. package/dist/{chunk-SPNWHKSR.js → chunk-LD3K2RDN.js} +7 -5
  12. package/dist/chunk-UBBC5FH7.js +31 -0
  13. package/dist/controls.d.ts +2 -1
  14. package/dist/controls.d.ts.map +1 -1
  15. package/dist/controls.js +4 -4
  16. package/dist/dialog/options-categories.d.ts +39 -0
  17. package/dist/dialog/options-categories.d.ts.map +1 -0
  18. package/dist/dialog/options-controls.d.ts +49 -0
  19. package/dist/dialog/options-controls.d.ts.map +1 -0
  20. package/dist/dialog/options-dialog.d.ts +76 -41
  21. package/dist/dialog/options-dialog.d.ts.map +1 -1
  22. package/dist/docx/auto-fit.d.ts +10 -0
  23. package/dist/docx/backstage-customize.d.ts +7 -0
  24. package/dist/docx/backstage-options.d.ts +13 -3
  25. package/dist/docx/backstage-pages.d.ts +0 -6
  26. package/dist/docx/backstage.d.ts +3 -0
  27. package/dist/docx/canvas-click.d.ts +7 -0
  28. package/dist/docx/content-control-calendar.d.ts +7 -0
  29. package/dist/docx/content-control-dialog.d.ts +7 -0
  30. package/dist/docx/content-control-view.d.ts +2 -0
  31. package/dist/docx/editor-core.d.ts +7 -0
  32. package/dist/docx/equation-commands.d.ts +17 -0
  33. package/dist/docx/equation-dialog.d.ts +7 -0
  34. package/dist/docx/format-dialogs.d.ts +1 -1
  35. package/dist/docx/insert-controller.d.ts +4 -0
  36. package/dist/docx/locales/en-app.d.ts +1 -0
  37. package/dist/docx/locales/en.d.ts +123 -0
  38. package/dist/docx/locales/objects.d.ts +37 -0
  39. package/dist/docx/locales/options.d.ts +11 -0
  40. package/dist/docx/locales/shapes.d.ts +101 -0
  41. package/dist/docx/localization-strings.d.ts +123 -0
  42. package/dist/docx/note-editor.d.ts +7 -1
  43. package/dist/docx/page-controller.d.ts +17 -3
  44. package/dist/docx/parts-controller.d.ts +27 -1
  45. package/dist/docx/print-caret-affinity.d.ts +15 -0
  46. package/dist/docx/print-editing-index.d.ts +1 -0
  47. package/dist/docx/print-editing.d.ts +4 -1
  48. package/dist/docx/print-float-geometry.d.ts +28 -0
  49. package/dist/docx/print-float-locate.d.ts +25 -0
  50. package/dist/docx/print-float-selection.d.ts +24 -0
  51. package/dist/docx/print-float-text.d.ts +8 -0
  52. package/dist/docx/print-keyboard-navigation.d.ts +4 -3
  53. package/dist/docx/print-layout-view.d.ts +8 -0
  54. package/dist/docx/print-layout.d.ts +1 -1
  55. package/dist/docx/print-note-editing.d.ts +47 -0
  56. package/dist/docx/print-page-window.d.ts +2 -1
  57. package/dist/docx/print-picture.d.ts +7 -0
  58. package/dist/docx/print-story-editing.d.ts +16 -0
  59. package/dist/docx/print-vertical-step.d.ts +7 -0
  60. package/dist/docx/ribbon-action.d.ts +15 -0
  61. package/dist/docx/ribbon-icons.d.ts +3 -0
  62. package/dist/docx/schema.d.ts +1 -1
  63. package/dist/docx/shape-commands.d.ts +40 -0
  64. package/dist/docx/shape-draw.d.ts +12 -0
  65. package/dist/docx/shape-format-ribbon.d.ts +4 -0
  66. package/dist/docx/shape-node-view.d.ts +4 -0
  67. package/dist/docx/shape-picker.d.ts +5 -0
  68. package/dist/docx/shape-text-dialog.d.ts +4 -0
  69. package/dist/docx/status-bar.d.ts +1 -1
  70. package/dist/docx/table-model-adapter.d.ts +1 -1
  71. package/dist/docx/table-navigation.d.ts +8 -0
  72. package/dist/docx.js +3578 -595
  73. package/dist/index.d.ts +1 -1
  74. package/dist/index.js +9 -9
  75. package/dist/pptx/{chart-3d-view-scene-MYQRQDNF.mjs → chart-3d-view-scene-BEAJCQIS.mjs} +2 -2
  76. package/dist/pptx/{chart-3d-view-scene-WX23DR2E.cjs → chart-3d-view-scene-CFUVOILD.cjs} +38 -38
  77. package/dist/pptx/{chunk-VFPVEVF4.cjs → chunk-2JTHIDMD.cjs} +173 -873
  78. package/dist/pptx/{chunk-ZN4T6CYA.cjs → chunk-4NBVWV6I.cjs} +134 -0
  79. package/dist/pptx/{chunk-TTX4TDHU.cjs → chunk-5DUBDWOU.cjs} +5580 -2709
  80. package/dist/pptx/{chunk-C5OJXCCB.cjs → chunk-5UGJC25J.cjs} +1 -1
  81. package/dist/pptx/{chunk-NAFUYAQP.mjs → chunk-DWUC6LZK.mjs} +1 -1
  82. package/dist/pptx/{chunk-5OMQM4GF.mjs → chunk-GUAP5VUN.mjs} +4799 -1979
  83. package/dist/pptx/{chunk-K3S2A64B.cjs → chunk-HULZNSHL.cjs} +13 -13
  84. package/dist/pptx/{chunk-FRB6BRVU.mjs → chunk-OJXPN2WX.mjs} +134 -0
  85. package/dist/pptx/{chunk-QCP2XYJW.mjs → chunk-P3YE5LT2.mjs} +1 -1
  86. package/dist/pptx/{chunk-DPOES4WM.mjs → chunk-VXKFXPCU.mjs} +116 -829
  87. package/dist/pptx/dom/index.cjs +253 -253
  88. package/dist/pptx/dom/index.mjs +8 -8
  89. package/dist/pptx/export/html2canvas-clone.cjs +2 -2
  90. package/dist/pptx/export/html2canvas-clone.mjs +1 -1
  91. package/dist/pptx/i18n/index.cjs +6 -6
  92. package/dist/pptx/i18n/index.mjs +1 -1
  93. package/dist/pptx/i18n/translations-en-table-layout.d.ts +3 -0
  94. package/dist/pptx/i18n/translations-en-table-layout.d.ts.map +1 -0
  95. package/dist/pptx/i18n/translations-en.d.ts.map +1 -1
  96. package/dist/pptx/index.cjs +3280 -3084
  97. package/dist/pptx/index.mjs +5 -5
  98. package/dist/pptx/render/chart-auto-title.d.ts +10 -15
  99. package/dist/pptx/render/chart-auto-title.d.ts.map +1 -1
  100. package/dist/pptx/render/chart-cartesian-bubbles.d.ts.map +1 -1
  101. package/dist/pptx/render/chart-combo-stock.cjs +3 -3
  102. package/dist/pptx/render/chart-combo-stock.mjs +1 -1
  103. package/dist/pptx/render/chart-data-label-text.d.ts +1 -1
  104. package/dist/pptx/render/chart-error-bars.d.ts.map +1 -1
  105. package/dist/pptx/render/chart-helpers.d.ts +3 -10
  106. package/dist/pptx/render/chart-helpers.d.ts.map +1 -1
  107. package/dist/pptx/render/chart-overlays-trendline-fit.d.ts +7 -9
  108. package/dist/pptx/render/chart-overlays-trendline-fit.d.ts.map +1 -1
  109. package/dist/pptx/render/chart-overlays.cjs +7 -7
  110. package/dist/pptx/render/chart-overlays.mjs +1 -1
  111. package/dist/pptx/render/chart-stock-candles.d.ts +0 -21
  112. package/dist/pptx/render/chart-stock-candles.d.ts.map +1 -1
  113. package/dist/pptx/render/chart-style-palette.d.ts +3 -28
  114. package/dist/pptx/render/chart-style-palette.d.ts.map +1 -1
  115. package/dist/pptx/render/chart-sunburst-hierarchy.d.ts +8 -28
  116. package/dist/pptx/render/chart-sunburst-hierarchy.d.ts.map +1 -1
  117. package/dist/pptx/render/chart-surface-treemap.cjs +4 -4
  118. package/dist/pptx/render/chart-surface-treemap.mjs +1 -1
  119. package/dist/pptx/render/chart-treemap-hierarchy.d.ts +7 -9
  120. package/dist/pptx/render/chart-treemap-hierarchy.d.ts.map +1 -1
  121. package/dist/pptx/render/chart-trendlines.d.ts +3 -17
  122. package/dist/pptx/render/chart-trendlines.d.ts.map +1 -1
  123. package/dist/pptx/render/chart-view-model-points.d.ts +2 -23
  124. package/dist/pptx/render/chart-view-model-points.d.ts.map +1 -1
  125. package/dist/pptx/render/chart-view-model.cjs +44 -44
  126. package/dist/pptx/render/chart-view-model.mjs +1 -1
  127. package/dist/pptx/render/chart-waterfall-map.cjs +6 -6
  128. package/dist/pptx/render/chart-waterfall-map.mjs +1 -1
  129. package/dist/pptx/render/comment-navigation.d.ts +51 -0
  130. package/dist/pptx/render/comment-navigation.d.ts.map +1 -0
  131. package/dist/pptx/render/customization/customization-catalog.d.ts +9 -2
  132. package/dist/pptx/render/customization/customization-catalog.d.ts.map +1 -1
  133. package/dist/pptx/render/customization/customization-controller.d.ts +9 -1
  134. package/dist/pptx/render/customization/customization-controller.d.ts.map +1 -1
  135. package/dist/pptx/render/customization/customization-descriptions.d.ts +5 -1
  136. package/dist/pptx/render/customization/customization-descriptions.d.ts.map +1 -1
  137. package/dist/pptx/render/customization/customization-entry-points.d.ts +46 -0
  138. package/dist/pptx/render/customization/customization-entry-points.d.ts.map +1 -0
  139. package/dist/pptx/render/customization/customization-reference.d.ts.map +1 -1
  140. package/dist/pptx/render/customization/customization-regions.d.ts +131 -0
  141. package/dist/pptx/render/customization/customization-regions.d.ts.map +1 -0
  142. package/dist/pptx/render/customization/customization-resolve-regions.d.ts +11 -0
  143. package/dist/pptx/render/customization/customization-resolve-regions.d.ts.map +1 -0
  144. package/dist/pptx/render/customization/customization-resolve.d.ts +21 -2
  145. package/dist/pptx/render/customization/customization-resolve.d.ts.map +1 -1
  146. package/dist/pptx/render/customization/customization-surfaces.d.ts +21 -0
  147. package/dist/pptx/render/customization/customization-surfaces.d.ts.map +1 -1
  148. package/dist/pptx/render/customization/customization-types.d.ts +28 -2
  149. package/dist/pptx/render/customization/customization-types.d.ts.map +1 -1
  150. package/dist/pptx/render/customization/index.d.ts +2 -0
  151. package/dist/pptx/render/customization/index.d.ts.map +1 -1
  152. package/dist/pptx/render/customization/ribbon-control-catalog-core.d.ts +34 -2
  153. package/dist/pptx/render/customization/ribbon-control-catalog-core.d.ts.map +1 -1
  154. package/dist/pptx/render/customization/ribbon-control-catalog-tail.d.ts +61 -0
  155. package/dist/pptx/render/customization/ribbon-control-catalog-tail.d.ts.map +1 -1
  156. package/dist/pptx/render/customization/ribbon-control-catalog.d.ts +95 -2
  157. package/dist/pptx/render/customization/ribbon-control-catalog.d.ts.map +1 -1
  158. package/dist/pptx/render/customization/ribbon-control-visibility.d.ts +6 -1
  159. package/dist/pptx/render/customization/ribbon-control-visibility.d.ts.map +1 -1
  160. package/dist/pptx/render/editor-chrome/ribbon-css.d.ts +1 -1
  161. package/dist/pptx/render/editor-chrome/ribbon-css.d.ts.map +1 -1
  162. package/dist/pptx/render/index.d.ts +3 -1
  163. package/dist/pptx/render/index.d.ts.map +1 -1
  164. package/dist/pptx/render/morph-plan.d.ts.map +1 -1
  165. package/dist/pptx/render/ole-actions.d.ts +5 -3
  166. package/dist/pptx/render/ole-actions.d.ts.map +1 -1
  167. package/dist/pptx/render/options/index.d.ts +2 -0
  168. package/dist/pptx/render/options/index.d.ts.map +1 -1
  169. package/dist/pptx/render/options/options-dialog-binding.d.ts +60 -0
  170. package/dist/pptx/render/options/options-dialog-binding.d.ts.map +1 -0
  171. package/dist/pptx/render/options/options-dialog-model.d.ts +35 -0
  172. package/dist/pptx/render/options/options-dialog-model.d.ts.map +1 -0
  173. package/dist/pptx/render/review-comment-commands.d.ts +42 -0
  174. package/dist/pptx/render/review-comment-commands.d.ts.map +1 -0
  175. package/dist/pptx/render/ribbon-galleries/gallery-placements.d.ts.map +1 -1
  176. package/dist/pptx/render/ribbon-galleries/gallery-registry.d.ts.map +1 -1
  177. package/dist/pptx/render/ribbon-galleries/gallery-types.d.ts +9 -1
  178. package/dist/pptx/render/ribbon-galleries/gallery-types.d.ts.map +1 -1
  179. package/dist/pptx/render/ribbon-galleries/index.d.ts +2 -0
  180. package/dist/pptx/render/ribbon-galleries/index.d.ts.map +1 -1
  181. package/dist/pptx/render/ribbon-galleries/table-layout-commands.d.ts +7 -0
  182. package/dist/pptx/render/ribbon-galleries/table-layout-commands.d.ts.map +1 -0
  183. package/dist/pptx/render/ribbon-galleries/table-layout-ops.d.ts +64 -0
  184. package/dist/pptx/render/ribbon-galleries/table-layout-ops.d.ts.map +1 -0
  185. package/dist/pptx/render/ribbon-galleries/table-text-direction-gallery.d.ts +3 -0
  186. package/dist/pptx/render/ribbon-galleries/table-text-direction-gallery.d.ts.map +1 -0
  187. package/dist/pptx/render/ribbon-insert-layout.d.ts +56 -0
  188. package/dist/pptx/render/ribbon-insert-layout.d.ts.map +1 -0
  189. package/dist/pptx/render/ribbon-insert-state.d.ts +42 -2
  190. package/dist/pptx/render/ribbon-insert-state.d.ts.map +1 -1
  191. package/dist/pptx/render/ribbon-review-commands.d.ts +7 -0
  192. package/dist/pptx/render/ribbon-review-commands.d.ts.map +1 -1
  193. package/dist/pptx/render/toolbar-actions.d.ts +1 -1
  194. package/dist/pptx/render/toolbar-actions.d.ts.map +1 -1
  195. package/dist/pptx/render/viewer-props-manifest.d.ts +54 -0
  196. package/dist/pptx/render/viewer-props-manifest.d.ts.map +1 -0
  197. package/dist/pptx/web-components/index.d.ts.map +1 -1
  198. package/dist/pptx/web-components/office-aliases.d.ts +13 -0
  199. package/dist/pptx/web-components/office-aliases.d.ts.map +1 -1
  200. package/dist/pptx/web-components/office-token-bridge.d.ts +1 -1
  201. package/dist/pptx/web-components/office-token-bridge.d.ts.map +1 -1
  202. package/dist/pptx/web-components/options-dialog.d.ts +7 -0
  203. package/dist/pptx/web-components/options-dialog.d.ts.map +1 -0
  204. package/dist/pptx/web-components/ribbon-insert-dom.d.ts +28 -1
  205. package/dist/pptx/web-components/ribbon-insert-dom.d.ts.map +1 -1
  206. package/dist/pptx/web-components/ribbon-insert-icons.d.ts.map +1 -1
  207. package/dist/pptx/web-components/ribbon-insert-styles.d.ts.map +1 -1
  208. package/dist/pptx/web-components/ribbon-insert-table-picker.d.ts +16 -0
  209. package/dist/pptx/web-components/ribbon-insert-table-picker.d.ts.map +1 -0
  210. package/dist/pptx/web-components/ribbon-insert-view.d.ts +2 -1
  211. package/dist/pptx/web-components/ribbon-insert-view.d.ts.map +1 -1
  212. package/dist/pptx/web-components/ribbon-overflow.d.ts.map +1 -1
  213. package/dist/presence.js +3 -3
  214. package/dist/ribbon/auto-collapse.d.ts +8 -0
  215. package/dist/ribbon/auto-collapse.d.ts.map +1 -0
  216. package/dist/ribbon/group-overflow.d.ts +7 -0
  217. package/dist/ribbon/group-overflow.d.ts.map +1 -1
  218. package/dist/ribbon/overflow.d.ts +7 -0
  219. package/dist/ribbon/overflow.d.ts.map +1 -1
  220. package/dist/ribbon/ribbon-tabs.d.ts +5 -0
  221. package/dist/ribbon/ribbon-tabs.d.ts.map +1 -1
  222. package/dist/ribbon/scroll-more.d.ts +17 -0
  223. package/dist/ribbon/scroll-more.d.ts.map +1 -0
  224. package/dist/smartart.js +3 -3
  225. package/dist/teams/activity-feed.d.ts +36 -0
  226. package/dist/teams/app/activity.d.ts +5 -0
  227. package/dist/teams/app/calendar-app.d.ts +2 -0
  228. package/dist/teams/app/chat-app.d.ts +11 -0
  229. package/dist/teams/app/chat-people-dialog.d.ts +38 -0
  230. package/dist/teams/app/controller.d.ts +3 -2
  231. package/dist/teams/app/dialog-base.d.ts +18 -0
  232. package/dist/teams/app/meeting-details.d.ts +63 -0
  233. package/dist/teams/app/meeting-dialog.d.ts +103 -0
  234. package/dist/teams/app/new-chat-dialog.d.ts +42 -0
  235. package/dist/teams/app/notify-delivery.d.ts +28 -0
  236. package/dist/teams/app/pinned-bar.d.ts +24 -0
  237. package/dist/teams/app/profile-menu.d.ts +18 -1
  238. package/dist/teams/app/settings-notifications.d.ts +9 -0
  239. package/dist/teams/app/store.d.ts +1 -1
  240. package/dist/teams/app/teams-app.d.ts +72 -5
  241. package/dist/teams/app/teams-settings.d.ts +5 -1
  242. package/dist/teams/app/thread-pane.d.ts +1 -1
  243. package/dist/teams/app-rail.d.ts +17 -3
  244. package/dist/teams/avatar.d.ts +2 -2
  245. package/dist/teams/base.d.ts +2 -0
  246. package/dist/teams/calendar-agenda.d.ts +30 -0
  247. package/dist/teams/calendar-view.d.ts +61 -0
  248. package/dist/teams/chat-composer.d.ts +32 -2
  249. package/dist/teams/chat-list.d.ts +41 -3
  250. package/dist/teams/chat-sidebar.d.ts +25 -0
  251. package/dist/teams/index.d.ts +6 -1
  252. package/dist/teams/people-picker.d.ts +37 -0
  253. package/dist/teams.js +6 -6
  254. package/dist/theme.js +2 -2
  255. package/dist/tokens.d.ts +7 -5
  256. package/dist/tokens.d.ts.map +1 -1
  257. package/dist/visio/locales/de-commands.d.ts +2 -0
  258. package/dist/visio/locales/de-text.d.ts +2 -0
  259. package/dist/visio/locales/en-commands.d.ts +2 -0
  260. package/dist/visio/locales/en-text.d.ts +2 -0
  261. package/dist/visio/locales/es-commands.d.ts +2 -0
  262. package/dist/visio/locales/es-text.d.ts +2 -0
  263. package/dist/visio/locales/fr-commands.d.ts +2 -0
  264. package/dist/visio/locales/fr-text.d.ts +2 -0
  265. package/dist/visio/locales/zh-CN-commands.d.ts +2 -0
  266. package/dist/visio/locales/zh-CN-text.d.ts +2 -0
  267. package/dist/visio/ribbon-home.d.ts +1 -1
  268. package/dist/visio/ribbon-locale.d.ts +11 -0
  269. package/dist/visio/ribbon-localize.d.ts +9 -0
  270. package/dist/visio/ribbon-tab-cue.d.ts +8 -0
  271. package/dist/visio/shapes-document.d.ts +5 -0
  272. package/dist/visio/shapes-files-ui.d.ts +13 -0
  273. package/dist/visio/shapes-master-list.d.ts +5 -0
  274. package/dist/visio/shapes-sections.d.ts +1 -4
  275. package/dist/visio/stencil-files.d.ts +36 -0
  276. package/dist/visio/styles.d.ts +1 -1
  277. package/dist/visio/viewer-chrome.d.ts +3 -3
  278. package/dist/visio/viewer-element.d.ts +2 -0
  279. package/dist/visio/viewer-gesture-state.d.ts +54 -0
  280. package/dist/visio/viewer-options-categories.d.ts +8 -0
  281. package/dist/visio/viewer-options.d.ts +3 -7
  282. package/dist/visio/viewer-touch-gestures.d.ts +17 -0
  283. package/dist/visio/viewer-touch-hit.d.ts +3 -0
  284. package/dist/visio/viewer-touch-long-press.d.ts +13 -0
  285. package/dist/visio/viewer-touch-math.d.ts +44 -0
  286. package/dist/visio.js +2425 -295
  287. package/dist/xlsx/backstage/index.d.ts +3 -0
  288. package/dist/xlsx/backstage/options-customize.d.ts +7 -0
  289. package/dist/xlsx/backstage/options-dialog.d.ts +13 -0
  290. package/dist/xlsx/commands/slicers.d.ts +9 -0
  291. package/dist/xlsx/commands/view.d.ts +3 -0
  292. package/dist/xlsx/dialogs/pivot-field-settings.d.ts +7 -0
  293. package/dist/xlsx/dialogs/pivot-group-dates.d.ts +6 -0
  294. package/dist/xlsx/dialogs/pivot-group.d.ts +3 -0
  295. package/dist/xlsx/dialogs/pivot-rows.d.ts +21 -0
  296. package/dist/xlsx/dialogs/pivot-values.d.ts +1 -1
  297. package/dist/xlsx/dialogs/scenario-manager.d.ts +2 -0
  298. package/dist/xlsx/dialogs/slicers.d.ts +9 -0
  299. package/dist/xlsx/dialogs/styles.d.ts +1 -1
  300. package/dist/xlsx/dialogs/what-if.d.ts +5 -0
  301. package/dist/xlsx/grid/page-break-layer.d.ts +3 -0
  302. package/dist/xlsx/grid/slicer.d.ts +22 -0
  303. package/dist/xlsx/grid/styles.d.ts +1 -1
  304. package/dist/xlsx/grid/timeline.d.ts +12 -0
  305. package/dist/xlsx/modules.d.ts +1 -0
  306. package/dist/xlsx/page-views/index.d.ts +7 -0
  307. package/dist/xlsx/page-views/page-layout-bands.d.ts +17 -0
  308. package/dist/xlsx/page-views/page-layout-css.d.ts +1 -0
  309. package/dist/xlsx/page-views/page-layout-editor.d.ts +21 -0
  310. package/dist/xlsx/page-views/page-layout-rulers.d.ts +3 -0
  311. package/dist/xlsx/page-views/page-layout-view.d.ts +8 -0
  312. package/dist/xlsx/print-pages.d.ts +7 -1
  313. package/dist/xlsx/ribbon/ribbon.d.ts +1 -1
  314. package/dist/xlsx/ribbon/tabs/contextual.d.ts +4 -0
  315. package/dist/xlsx/status-bar.d.ts +1 -1
  316. package/dist/xlsx.js +3015 -546
  317. package/package.json +2 -2
  318. package/dist/pptx/render/chart-number-format-pattern.d.ts +0 -35
  319. package/dist/pptx/render/chart-number-format-pattern.d.ts.map +0 -1
  320. package/dist/pptx/render/chart-number-format.d.ts +0 -21
  321. package/dist/pptx/render/chart-number-format.d.ts.map +0 -1
  322. package/dist/pptx/render/chart-waterfall-layout.d.ts +0 -20
  323. package/dist/pptx/render/chart-waterfall-layout.d.ts.map +0 -1
  324. package/dist/teams/app/navigation-drawer.d.ts +0 -26
  325. package/dist/xlsx/backstage/pages-options.d.ts +0 -7
@@ -1,12 +1,12 @@
1
- import { THEME_CSS, installOfficeUiTheme } from './chunk-S6ZXU442.js';
2
- import { defineMenuButton, defineMenuItem, registerControls, defineSwitch } from './chunk-735S7V4M.js';
1
+ import { THEME_CSS, installOfficeUiTheme } from './chunk-7E3HU7RL.js';
2
+ import { defineMenuButton, defineMenuItem, registerControls, defineSwitch } from './chunk-LAVVSCRJ.js';
3
3
  import { registerIcon, getIcon } from './chunk-M6GF5ED2.js';
4
- import { initialsOf, definePresence } from './chunk-L7JV5FVE.js';
5
- import { definer, OfficeElement, controlStyles } from './chunk-OGNNNTKI.js';
6
- import { html, nothing, LitElement, unsafeCSS, svg } from 'lit';
4
+ import { initialsOf, definePresence } from './chunk-KNC2UEUR.js';
5
+ import { definer, OfficeElement, controlStyles } from './chunk-CWBSXK7P.js';
6
+ import { nothing, html, LitElement, unsafeCSS, svg } from 'lit';
7
+ import { filterActivity, ACTIVITY_FILTERS, describeActivity, localZone, startOfDay, occurrencesBetween, DAY_MS as DAY_MS$1, dayKey, zonedToUtc, viewDays, zonedParts, shiftAnchor, layoutDay, mentionQueryAt, filterMentionTargets, insertMention, MAX_ATTACHMENTS, mentionsUser, mentionSegments, detectContentKind, contentUrl, readContent, sortFiles, filterFiles, parseServerConfig, STATUS_DURATIONS, STATUS_DURATION_LABELS, PRESENCE_LABELS, MAX_STATUS_MESSAGE_CHARS, isPresenceStatus, PRESENCE_STATUSES, isStatusDuration, IDLE_MINUTE_CHOICES, sanitizeChannelName, MAX_CHANNEL_NAME_CHARS, MAX_CHANNEL_TOPIC_CHARS, MAX_CHAT_MEMBERS, occurrenceAt, MAX_RECURRENCE_COUNT, MINUTE_MS, MAX_MEETING_TITLE, MAX_MEETING_DESCRIPTION, markdownBlocks, createTeamsClient, sanitizeAttachment, markdownInline } from 'ooxml-core/teams';
7
8
  import { repeat } from 'lit/directives/repeat.js';
8
9
  import { live } from 'lit/directives/live.js';
9
- import { MAX_ATTACHMENTS, detectContentKind, contentUrl, readContent, sortFiles, filterFiles, parseServerConfig, sanitizeChannelName, MAX_CHANNEL_NAME_CHARS, MAX_CHANNEL_TOPIC_CHARS, markdownBlocks, createTeamsClient, sanitizeAttachment, markdownInline } from 'ooxml-core/teams';
10
10
  import { keyed } from 'lit/directives/keyed.js';
11
11
 
12
12
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/reset.css?raw
@@ -42,8 +42,20 @@ function formatSize(bytes) {
42
42
  if (bytes < 1048576) return `${Math.round(bytes / 1024)} KB`;
43
43
  return `${(bytes / 1048576).toFixed(1)} MB`;
44
44
  }
45
+ var timeOnly = (ts) => new Date(ts).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
46
+ function formatWhen(start, end) {
47
+ const day = new Date(start).toLocaleDateString([], {
48
+ weekday: "short",
49
+ month: "short",
50
+ day: "numeric"
51
+ });
52
+ const sameDay = new Date(start).toDateString() === new Date(end).toDateString();
53
+ const tail = sameDay ? timeOnly(end) : `${new Date(end).toLocaleDateString([], { month: "short", day: "numeric" })}, ${timeOnly(end)}`;
54
+ return `${day}, ${timeOnly(start)} - ${tail}`;
55
+ }
45
56
  var GLYPHS = {
46
- navigation: "M3 5h14M3 10h14M3 15h14",
57
+ back: "M12.5 4 6.5 10l6 6",
58
+ pin: "M7 3h6l-1 5 3 3H5l3-3ZM10 11v6",
47
59
  send: "M3 10 17 3l-4 14-3-6Zm7 1 7-8",
48
60
  paperclip: "M15 9.5l-5.5 5.5a3.5 3.5 0 0 1-5-5l6-6a2.3 2.3 0 0 1 3.3 3.3l-6 6a1.2 1.2 0 0 1-1.7-1.7L11 6",
49
61
  smile: "M3 10a7 7 0 1 0 14 0 7 7 0 1 0-14 0M7.5 8v.5M12.5 8v.5M7 12c1.5 2 4.5 2 6 0",
@@ -61,7 +73,9 @@ var GLYPHS = {
61
73
  screenShare: "M3 4h14v9H3ZM7 17h6M10 13v4M10 10V6M8 8l2-2 2 2",
62
74
  hand: "M7 10V4.5a1 1 0 0 1 2 0V9M9 9V3.5a1 1 0 0 1 2 0V9M11 9V4.5a1 1 0 0 1 2 0V10M13 8.5a1 1 0 0 1 2 0V12a5 5 0 0 1-5 5h-.5A4.5 4.5 0 0 1 5 13.5L4 10.5a1 1 0 0 1 2-.5l1 2",
63
75
  phoneOff: "M3 11c4-3 10-3 14 0l-1.5 2.5-3-1V10a9 9 0 0 0-5 0v2.5l-3 1Z",
64
- chat: "M3 4h14v9H8l-5 4z"
76
+ chat: "M3 4h14v9H8l-5 4z",
77
+ bell: "M5 14h10l-1.5-2V8.5a3.5 3.5 0 0 0-7 0V12ZM8.5 16.5a1.5 1.5 0 0 0 3 0",
78
+ at: "M13 10a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM13 10v1.2a1.8 1.8 0 0 0 3.6 0V10a6.6 6.6 0 1 0-2.6 5.2"
65
79
  };
66
80
  for (const [name, d] of Object.entries(GLYPHS)) registerIcon(name, d);
67
81
  function icon(name) {
@@ -72,63 +86,244 @@ function icon(name) {
72
86
  </svg>`;
73
87
  }
74
88
 
89
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/activity-feed.css?raw
90
+ var activity_feed_default = ":host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-2);\n padding: var(--office-space-3) var(--office-space-3) var(--office-space-1);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n font-weight: var(--office-font-weight-bold);\n}\n.read-all {\n padding: var(--office-space-1) var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-brand);\n font-size: var(--office-font-size-sm);\n}\n.read-all:hover:not(:disabled) {\n background: var(--office-selected);\n}\n.read-all:disabled {\n color: var(--office-teams-text-subtle);\n cursor: default;\n}\n.filters {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n padding: var(--office-space-1) var(--office-space-3) var(--office-space-2);\n}\n.filters button {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-1);\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: transparent;\n font-size: var(--office-font-size-sm);\n}\n.filters button:hover {\n background: var(--office-selected);\n}\n.filters button[aria-pressed='true'] {\n border-color: var(--office-teams-brand);\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.count {\n min-width: 1.4em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-3xs);\n line-height: 1.6;\n text-align: center;\n}\nul {\n flex: 1;\n min-height: 0;\n margin: 0;\n padding: 0 var(--office-space-2) var(--office-space-2);\n overflow-y: auto;\n list-style: none;\n}\nli {\n position: relative;\n}\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--office-space-3);\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n color: inherit;\n text-align: start;\n}\n.item:hover {\n background: var(--office-selected);\n}\n.item[aria-current='true'] {\n background: var(--office-teams-brand-subtle);\n}\n.face {\n position: relative;\n flex: none;\n}\n.kind {\n position: absolute;\n inset-block-end: calc(-1 * var(--office-space-1));\n inset-inline-end: calc(-1 * var(--office-space-1));\n display: grid;\n place-items: center;\n width: 1.5em;\n height: 1.5em;\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\n.kind svg {\n width: 0.9em;\n height: 0.9em;\n}\n.text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n}\n.line {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\nli[data-unread='true'] .line {\n font-weight: var(--office-font-weight-bold);\n}\n.preview {\n overflow: hidden;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.when {\n display: flex;\n flex: none;\n flex-direction: column;\n align-items: flex-end;\n gap: var(--office-space-1);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.dot {\n width: 0.7em;\n height: 0.7em;\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.toggle {\n position: absolute;\n inset-block-end: var(--office-space-1);\n inset-inline-end: var(--office-space-2);\n display: none;\n place-items: center;\n width: 2em;\n height: 2em;\n border: 0;\n border-radius: var(--office-radius);\n background: var(--office-popover);\n color: var(--office-teams-text-subtle);\n box-shadow: var(--office-shadow-teams);\n}\nli:hover .toggle,\nli:focus-within .toggle {\n display: grid;\n}\n.empty {\n margin: 0;\n padding: var(--office-space-4);\n color: var(--office-teams-text-subtle);\n text-align: center;\n}\n@media (pointer: coarse) {\n .filters button,\n .read-all {\n min-height: var(--office-touch-target, 44px);\n }\n .toggle {\n display: grid;\n width: var(--office-touch-target, 44px);\n height: var(--office-touch-target, 44px);\n }\n}\n";
91
+
92
+ // src/teams/activity-feed.ts
93
+ var FILTER_LABEL = {
94
+ all: "All",
95
+ unread: "Unread",
96
+ mentions: "Mentions",
97
+ replies: "Replies",
98
+ reactions: "Reactions"
99
+ };
100
+ var KIND_ICON = {
101
+ mention: "at",
102
+ reply: "reply",
103
+ reaction: "smile",
104
+ pin: "pin",
105
+ meeting: "calendar"
106
+ };
107
+ var EMPTY = {
108
+ all: "No activity yet. Mentions, replies, reactions and pinned messages show up here.",
109
+ unread: "You are all caught up.",
110
+ mentions: "Nobody has mentioned you yet.",
111
+ replies: "No replies to your messages or followed threads yet.",
112
+ reactions: "No reactions to your messages yet."
113
+ };
114
+ function activityTime(ts, now = Date.now()) {
115
+ const day = dayLabel(ts, now);
116
+ if (day === "Today") return formatTime(ts);
117
+ if (day === "Yesterday") return day;
118
+ return new Date(ts).toLocaleDateString([], { month: "short", day: "numeric" });
119
+ }
120
+ var OfficeUiActivityFeed = class extends TeamsElement {
121
+ static styles = withStyles(activity_feed_default);
122
+ static properties = {
123
+ items: { attribute: false },
124
+ filter: { type: String, reflect: true },
125
+ selectedId: { type: String, attribute: "selected-id" }
126
+ };
127
+ constructor() {
128
+ super();
129
+ this.items = [];
130
+ this.filter = "all";
131
+ this.selectedId = "";
132
+ }
133
+ get unread() {
134
+ return this.items.filter((item) => !item.read).length;
135
+ }
136
+ render() {
137
+ const shown = filterActivity(this.items, this.filter);
138
+ const unread = this.unread;
139
+ return html`
140
+ <header>
141
+ <h2>Activity</h2>
142
+ <button
143
+ type="button"
144
+ class="read-all"
145
+ ?disabled=${unread === 0}
146
+ @click=${() => this.fire("office-activity-read-all", {})}
147
+ >
148
+ Mark all as read
149
+ </button>
150
+ </header>
151
+ <div class="filters" role="group" aria-label="Filter activity">
152
+ ${ACTIVITY_FILTERS.map(
153
+ (value) => html`<button
154
+ type="button"
155
+ aria-pressed=${String(this.filter === value)}
156
+ @click=${() => {
157
+ this.filter = value;
158
+ this.fire("office-activity-filter", { filter: value });
159
+ }}
160
+ >
161
+ ${FILTER_LABEL[value]}${value === "unread" && unread ? html` <span class="count">${unread}</span>` : nothing}
162
+ </button>`
163
+ )}
164
+ </div>
165
+ ${shown.length ? html`<ul aria-label="Activity feed">
166
+ ${shown.map((item) => this.row(item))}
167
+ </ul>` : html`<p class="empty" role="status">${EMPTY[this.filter]}</p>`}
168
+ `;
169
+ }
170
+ row(item) {
171
+ const line = describeActivity(item);
172
+ return html`<li data-unread=${String(!item.read)} data-kind=${item.kind}>
173
+ <button
174
+ type="button"
175
+ class="item"
176
+ aria-current=${this.selectedId === item.id ? "true" : "false"}
177
+ aria-label=${`${item.read ? "" : "Unread. "}${line}. ${item.preview}`}
178
+ @click=${() => this.fire("office-activity-open", { id: item.id })}
179
+ >
180
+ <span class="face">
181
+ <office-ui-avatar
182
+ size="md"
183
+ name=${item.actorName}
184
+ seed=${item.actorId}
185
+ ></office-ui-avatar>
186
+ <span class="kind" aria-hidden="true">${icon(KIND_ICON[item.kind])}</span>
187
+ </span>
188
+ <span class="text">
189
+ <span class="line">${line}</span>
190
+ <span class="preview">${item.preview}</span>
191
+ </span>
192
+ <span class="when">
193
+ <time datetime=${new Date(item.ts).toISOString()}>${activityTime(item.ts)}</time>
194
+ ${item.read ? nothing : html`<span class="dot" aria-hidden="true"></span>`}
195
+ </span>
196
+ </button>
197
+ <button
198
+ type="button"
199
+ class="toggle"
200
+ title=${item.read ? "Mark as unread" : "Mark as read"}
201
+ aria-label=${item.read ? "Mark as unread" : "Mark as read"}
202
+ @click=${() => this.fire("office-activity-read", { id: item.id, read: !item.read })}
203
+ >
204
+ ${icon(item.read ? "bell" : "check")}
205
+ </button>
206
+ </li>`;
207
+ }
208
+ };
209
+ var defineActivityFeed = definer("office-ui-activity-feed", () => OfficeUiActivityFeed);
210
+
75
211
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app-rail.css?raw
76
- var app_rail_default = ":host {\n display: block;\n width: 5em;\n background: var(--office-teams-rail);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nul {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--office-space-1);\n margin: 0;\n padding: var(--office-space-2) 0;\n list-style: none;\n}\nbutton {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--office-space-0);\n width: 4.4em;\n padding: var(--office-space-2) 0;\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\nbutton:hover {\n background: var(--office-selected);\n}\nbutton[aria-current='page'] {\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\nbutton[aria-current='page']::before {\n content: '';\n position: absolute;\n inset-inline-start: calc(-1 * var(--office-space-1));\n inset-block: 20%;\n width: var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.glyph {\n position: relative;\n display: grid;\n place-items: center;\n font-size: 1.3em;\n}\n.label {\n font-size: var(--office-font-size-2xs);\n}\n:host([hide-labels]) .label {\n display: none;\n}\n.badge {\n position: absolute;\n inset-block-start: -0.4em;\n inset-inline-end: -0.9em;\n min-width: 1.5em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-3xs);\n line-height: 1.6;\n text-align: center;\n}\n";
212
+ var app_rail_default = ":host {\n display: block;\n width: 5em;\n background: var(--office-teams-rail);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nul {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--office-space-1);\n margin: 0;\n padding: var(--office-space-2) 0;\n list-style: none;\n}\nbutton {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--office-space-0);\n width: 4.4em;\n padding: var(--office-space-2) 0;\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\nbutton:hover {\n background: var(--office-selected);\n}\nbutton[aria-current='page'] {\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\nbutton[aria-current='page']::before {\n content: '';\n position: absolute;\n inset-inline-start: calc(-1 * var(--office-space-1));\n inset-block: 20%;\n width: var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.glyph {\n position: relative;\n display: grid;\n place-items: center;\n font-size: 1.3em;\n}\n.label {\n font-size: var(--office-font-size-2xs);\n}\n:host([hide-labels]) .label {\n display: none;\n}\n.badge {\n position: absolute;\n inset-block-start: -0.4em;\n inset-inline-end: -0.9em;\n min-width: 1.5em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-3xs);\n line-height: 1.6;\n text-align: center;\n}\n\n.short {\n display: none;\n}\n\n/* Compact shell: the rail becomes a bottom navigation bar with short labels. */\n@media (max-width: 56em) {\n :host {\n width: auto;\n padding-block-end: env(safe-area-inset-bottom, 0);\n padding-inline: env(safe-area-inset-left, 0) env(safe-area-inset-right, 0);\n border-inline-end: 0;\n border-block-start: var(--office-border-width) solid var(--office-teams-divider);\n }\n ul {\n flex-direction: row;\n justify-content: space-around;\n align-items: stretch;\n gap: 0;\n padding: var(--office-space-1) var(--office-space-1);\n }\n li {\n flex: 1 1 0;\n min-width: 0;\n }\n button {\n width: 100%;\n min-height: var(--office-touch-target, 44px);\n padding: var(--office-space-1) 0;\n justify-content: center;\n }\n button[aria-current='page']::before {\n inset-inline: 25%;\n inset-block: 0 auto;\n width: auto;\n height: var(--office-space-1);\n }\n .label.has-short {\n display: none;\n }\n .short {\n display: inline;\n font-size: var(--office-font-size-2xs);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n }\n}\n@media (max-width: 56em) and (max-height: 30em) {\n .label,\n .short {\n display: none;\n }\n button {\n min-height: 2.4em;\n }\n}\n\n/* The More sheet exists only on the phone bar. */\n.more-item {\n display: none;\n}\n.sheet {\n box-sizing: border-box;\n width: 100%;\n max-width: 100%;\n margin: auto 0 0;\n padding: var(--office-space-3) var(--office-space-3)\n calc(var(--office-space-3) + env(safe-area-inset-bottom, 0));\n border: 0;\n border-start-start-radius: var(--office-radius-lg);\n border-start-end-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: var(--office-foreground);\n box-shadow: var(--office-shadow-teams);\n}\n.sheet::backdrop {\n background: var(--office-overlay);\n}\n.sheet ul {\n flex-direction: column;\n align-items: stretch;\n padding: 0;\n}\n.sheet li {\n flex: none;\n}\n.sheet button {\n flex-direction: row;\n justify-content: flex-start;\n gap: var(--office-space-3);\n width: 100%;\n min-height: var(--office-touch-target, 44px);\n padding: var(--office-space-2) var(--office-space-3);\n font-size: var(--office-font-size);\n}\n.sheet button.close {\n justify-content: center;\n margin-top: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n}\n.sheet .label {\n display: inline;\n font-size: var(--office-font-size);\n}\n@media (max-width: 56em) {\n li[data-secondary='true'] {\n display: none;\n }\n .more-item {\n display: block;\n flex: 1 1 0;\n min-width: 0;\n }\n .more .glyph {\n font-size: 1.3em;\n }\n}\n";
77
213
 
78
214
  // src/teams/app-rail.ts
215
+ var total = (n) => n > 99 ? "99+" : String(n);
79
216
  var OfficeUiAppRail = class extends TeamsElement {
80
217
  static styles = withStyles(app_rail_default);
81
218
  static properties = {
82
219
  items: { attribute: false },
83
220
  selected: { type: String },
84
- hideLabels: { type: Boolean, attribute: "hide-labels", reflect: true }
221
+ hideLabels: { type: Boolean, attribute: "hide-labels", reflect: true },
222
+ moreOpen: { state: true }
85
223
  };
86
224
  constructor() {
87
225
  super();
88
226
  this.items = [];
89
227
  this.selected = "";
90
228
  this.hideLabels = false;
229
+ this.moreOpen = false;
91
230
  }
92
231
  connectedCallback() {
93
232
  super.connectedCallback();
94
233
  this.setAttribute("role", "navigation");
95
234
  this.setAttribute("aria-label", "App bar");
96
235
  }
236
+ updated(changed) {
237
+ const dialog = this.renderRoot.querySelector("dialog");
238
+ if (!dialog || !changed.has("moreOpen")) return;
239
+ if (this.moreOpen && !dialog.open) dialog.showModal();
240
+ else if (!this.moreOpen && dialog.open) dialog.close();
241
+ }
242
+ closeMore() {
243
+ this.moreOpen = false;
244
+ void this.updateComplete.then(
245
+ () => this.renderRoot.querySelector(".more")?.focus()
246
+ );
247
+ }
248
+ select(id) {
249
+ this.moreOpen = false;
250
+ this.fire("office-rail-select", { id });
251
+ }
97
252
  render() {
253
+ const secondary = this.items.filter((i) => i.secondary);
254
+ const badge = secondary.reduce((n, i) => n + (i.badge ?? 0), 0);
255
+ const inMore = secondary.some((i) => i.id === this.selected);
98
256
  return html`
99
257
  <ul>
100
258
  ${this.items.map(
101
259
  (item) => html`
102
- <li>
103
- <button
104
- type="button"
105
- aria-label=${item.label}
106
- title=${item.label}
107
- aria-current=${this.selected === item.id ? "page" : "false"}
108
- @click=${() => this.fire("office-rail-select", { id: item.id })}
109
- >
110
- <span class="glyph">
111
- ${icon(item.icon)}
112
- ${item.badge ? html`<span class="badge">${item.badge > 99 ? "99+" : item.badge}</span>` : ""}
113
- </span>
114
- <span class="label">${item.label}</span>
115
- </button>
116
- </li>
260
+ <li data-secondary=${String(item.secondary === true)}>${this.button(item)}</li>
117
261
  `
118
262
  )}
263
+ ${secondary.length ? html`<li class="more-item">
264
+ <button
265
+ type="button"
266
+ class="more"
267
+ aria-label=${badge ? `More, ${badge} unread` : "More"}
268
+ aria-haspopup="dialog"
269
+ aria-expanded=${String(this.moreOpen)}
270
+ aria-current=${inMore ? "page" : "false"}
271
+ @click=${() => this.moreOpen = true}
272
+ >
273
+ <span class="glyph">
274
+ ${icon("more")}
275
+ ${badge ? html`<span class="badge">${total(badge)}</span>` : nothing}
276
+ </span>
277
+ <span class="short" aria-hidden="true">More</span>
278
+ </button>
279
+ </li>` : nothing}
119
280
  </ul>
281
+ ${secondary.length ? html`<dialog
282
+ class="sheet"
283
+ aria-label="More apps"
284
+ @cancel=${(e) => {
285
+ e.preventDefault();
286
+ this.closeMore();
287
+ }}
288
+ @click=${(e) => {
289
+ if (e.target === e.currentTarget) this.closeMore();
290
+ }}
291
+ >
292
+ <ul>
293
+ ${secondary.map((item) => html`<li>${this.button(item, true)}</li>`)}
294
+ </ul>
295
+ <button type="button" class="close" @click=${() => this.closeMore()}>Close</button>
296
+ </dialog>` : nothing}
120
297
  `;
121
298
  }
299
+ button(item, sheet = false) {
300
+ return html`<button
301
+ type="button"
302
+ aria-label=${item.label}
303
+ title=${item.label}
304
+ aria-current=${this.selected === item.id ? "page" : "false"}
305
+ @click=${() => this.select(item.id)}
306
+ >
307
+ <span class="glyph">
308
+ ${icon(item.icon)}
309
+ ${item.badge ? html`<span class="badge">${total(item.badge)}</span>` : nothing}
310
+ </span>
311
+ <span class=${item.shortLabel && !sheet ? "label has-short" : "label"}>${item.label}</span>
312
+ ${item.shortLabel && !sheet ? html`<span class="short" aria-hidden="true">${item.shortLabel}</span>` : nothing}
313
+ </button>`;
314
+ }
122
315
  };
123
316
  var defineAppRail = definer("office-ui-app-rail", () => OfficeUiAppRail);
124
317
 
125
318
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/avatar.css?raw
126
- var avatar_default = ":host {\n --_size: 2.3em;\n position: relative;\n display: inline-block;\n width: var(--_size);\n height: var(--_size);\n flex: none;\n}\n:host([size='sm']) {\n --_size: 1.7em;\n}\n:host([size='lg']) {\n --_size: 3em;\n}\n:host([size='xl']) {\n --_size: 6em;\n}\n\n.face {\n display: grid;\n place-items: center;\n width: 100%;\n height: 100%;\n border-radius: var(--office-radius-full);\n color: var(--office-call-foreground);\n font-size: calc(var(--_size) * 0.38);\n font-weight: var(--office-font-weight-bold);\n user-select: none;\n}\n\n.presence {\n position: absolute;\n inset-inline-end: 0;\n inset-block-end: 0;\n width: 32%;\n height: 32%;\n min-width: 0.5em;\n min-height: 0.5em;\n border-radius: var(--office-radius-full);\n border: var(--office-border-width-thick) solid var(--office-background);\n background: var(--office-presence-offline);\n}\n.presence[data-presence='available'] {\n background: var(--office-presence-available);\n}\n.presence[data-presence='busy'] {\n background: var(--office-presence-busy);\n}\n.presence[data-presence='away'] {\n background: var(--office-presence-away);\n}\n";
319
+ var avatar_default = ":host {\n --_size: 2.3em;\n position: relative;\n display: inline-block;\n width: var(--_size);\n height: var(--_size);\n flex: none;\n}\n:host([size='sm']) {\n --_size: 1.7em;\n}\n:host([size='lg']) {\n --_size: 3em;\n}\n:host([size='xl']) {\n --_size: 6em;\n}\n\n.face {\n display: grid;\n place-items: center;\n width: 100%;\n height: 100%;\n border-radius: var(--office-radius-full);\n color: var(--office-call-foreground);\n font-size: calc(var(--_size) * 0.38);\n font-weight: var(--office-font-weight-bold);\n user-select: none;\n}\n\n.presence {\n position: absolute;\n inset-inline-end: 0;\n inset-block-end: 0;\n width: 32%;\n height: 32%;\n min-width: 0.5em;\n min-height: 0.5em;\n border-radius: var(--office-radius-full);\n border: var(--office-border-width-thick) solid var(--office-background);\n background: var(--office-presence-offline);\n}\n.presence[data-presence='available'] {\n background: var(--office-presence-available);\n}\n.presence[data-presence='busy'] {\n background: var(--office-presence-busy);\n}\n.presence[data-presence='away'] {\n background: var(--office-presence-away);\n}\n.presence[data-presence='dnd'] {\n background: var(--office-presence-busy);\n}\n.presence[data-presence='brb'] {\n background: var(--office-presence-away);\n}\n/* Do not disturb carries a white bar; Away and Be right back share the clock hands. */\n.presence[data-presence='dnd']::after,\n.presence[data-presence='away']::after,\n.presence[data-presence='brb']::after {\n content: '';\n position: absolute;\n inset: 0;\n margin: auto;\n background: var(--office-background);\n}\n.presence[data-presence='dnd']::after {\n width: 60%;\n height: 16%;\n border-radius: var(--office-radius-full);\n}\n.presence[data-presence='away']::after,\n.presence[data-presence='brb']::after {\n width: 14%;\n height: 50%;\n inset-block: 10% auto;\n border-radius: var(--office-radius-full);\n}\n/* Appear offline is a hollow grey ring. */\n.presence[data-presence='offline'] {\n background: var(--office-background);\n border-color: var(--office-presence-offline);\n box-shadow: inset 0 0 0 var(--office-border-width-thick) var(--office-presence-offline);\n}\n";
127
320
 
128
321
  // src/teams/avatar.ts
129
322
  var PRESENCE_LABEL = {
130
323
  available: "Available",
131
324
  busy: "Busy",
325
+ dnd: "Do not disturb",
326
+ brb: "Be right back",
132
327
  away: "Away",
133
328
  offline: "Offline"
134
329
  };
@@ -175,10 +370,10 @@ var call_controls_default = ":host {\n display: flex;\n align-items: center;\n j
175
370
 
176
371
  // src/teams/call-controls.ts
177
372
  var formatElapsed = (ms) => {
178
- const total = Math.max(0, Math.floor(ms / 1e3));
373
+ const total2 = Math.max(0, Math.floor(ms / 1e3));
179
374
  const pad = (n) => String(n).padStart(2, "0");
180
- const h = Math.floor(total / 3600);
181
- return `${h ? `${h}:` : ""}${pad(Math.floor(total % 3600 / 60))}:${pad(total % 60)}`;
375
+ const h = Math.floor(total2 / 3600);
376
+ return `${h ? `${h}:` : ""}${pad(Math.floor(total2 % 3600 / 60))}:${pad(total2 % 60)}`;
182
377
  };
183
378
  var OfficeUiCallControls = class extends TeamsElement {
184
379
  static styles = withStyles(call_controls_default);
@@ -262,6 +457,346 @@ var OfficeUiCallControls = class extends TeamsElement {
262
457
  };
263
458
  var defineCallControls = definer("office-ui-call-controls", () => OfficeUiCallControls);
264
459
 
460
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/calendar-agenda.css?raw
461
+ var calendar_agenda_default = ":host {\n display: block;\n overflow: auto;\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-3) var(--office-space-3) var(--office-space-2) var(--office-space-4);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nh3 {\n margin: var(--office-space-3) var(--office-space-4) var(--office-space-1);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n font-weight: var(--office-font-weight-bold);\n}\n.new {\n display: grid;\n place-items: center;\n width: 2.2em;\n height: 2.2em;\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n color: var(--office-teams-brand);\n cursor: pointer;\n}\n.new:hover {\n background: var(--office-selected);\n}\n.empty {\n margin: var(--office-space-4);\n color: var(--office-teams-text-subtle);\n}\nul {\n display: grid;\n gap: var(--office-space-1);\n margin: 0;\n padding: 0 var(--office-space-2);\n list-style: none;\n}\nli {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n}\n.row {\n display: grid;\n flex: 1;\n min-width: 0;\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-inline-start: var(--office-border-width-thick) solid var(--office-teams-brand);\n border-radius: var(--office-radius);\n background: var(--office-teams-bubble);\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n.row:hover {\n background: var(--office-selected);\n}\n.row[data-live='true'] {\n border-inline-start-color: var(--office-call-live);\n}\n.time,\n.who {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.title {\n overflow: hidden;\n font-weight: var(--office-font-weight-bold);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.join {\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font: inherit;\n cursor: pointer;\n}\n.join:hover {\n background: var(--office-teams-brand-hover);\n}\n@media (pointer: coarse) {\n .new,\n .row,\n .join {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n";
462
+
463
+ // src/teams/calendar-agenda.ts
464
+ var HORIZON_DAYS = 14;
465
+ var JOIN_WINDOW_MS = 15 * 6e4;
466
+ var OfficeUiCalendarAgenda = class extends TeamsElement {
467
+ static styles = withStyles(calendar_agenda_default);
468
+ static properties = {
469
+ meetings: { attribute: false },
470
+ zone: { type: String },
471
+ now: { type: Number }
472
+ };
473
+ constructor() {
474
+ super();
475
+ this.meetings = [];
476
+ this.zone = localZone();
477
+ this.now = 0;
478
+ }
479
+ render() {
480
+ const now = this.now || Date.now();
481
+ const from = startOfDay(now, this.zone);
482
+ const rows = this.meetings.flatMap(
483
+ (m) => occurrencesBetween(m, from, from + HORIZON_DAYS * DAY_MS$1).map((o) => ({ o, m }))
484
+ ).filter(({ o }) => o.end > now).sort((a, b) => a.o.start - b.o.start);
485
+ const groups = /* @__PURE__ */ new Map();
486
+ for (const row of rows) {
487
+ const key = dayKey(row.o.start, this.zone);
488
+ let group = groups.get(key);
489
+ if (!group) {
490
+ const label = new Intl.DateTimeFormat([], {
491
+ weekday: "long",
492
+ month: "short",
493
+ day: "numeric",
494
+ timeZone: this.zone
495
+ }).format(row.o.start);
496
+ groups.set(
497
+ key,
498
+ group = { label: key === dayKey(now, this.zone) ? `Today, ${label}` : label, rows: [] }
499
+ );
500
+ }
501
+ group.rows.push(row);
502
+ }
503
+ return html`
504
+ <header>
505
+ <h2>Calendar</h2>
506
+ <button
507
+ class="new"
508
+ type="button"
509
+ title="New meeting"
510
+ aria-label="New meeting"
511
+ @click=${() => this.fire("office-agenda-new", {})}
512
+ >
513
+ ${icon("plus")}
514
+ </button>
515
+ </header>
516
+ ${groups.size === 0 ? html`<p class="empty">No upcoming meetings. Schedule one with the plus button.</p>` : [...groups.values()].map(
517
+ (g) => html`<section>
518
+ <h3>${g.label}</h3>
519
+ <ul>
520
+ ${g.rows.map(({ o, m }) => this.row(o, m, now))}
521
+ </ul>
522
+ </section>`
523
+ )}
524
+ `;
525
+ }
526
+ row(o, m, now) {
527
+ const title = o.title ?? m.title;
528
+ const time = `${this.clock(o.start)} - ${this.clock(o.end)}`;
529
+ const joinable = m.live || o.start - now <= JOIN_WINDOW_MS && o.end > now;
530
+ return html`<li>
531
+ <button
532
+ type="button"
533
+ class="row"
534
+ data-live=${String(m.live)}
535
+ aria-label=${`${title}, ${time}`}
536
+ @click=${() => this.fire("office-agenda-open", { meetingId: m.id, start: o.start })}
537
+ >
538
+ <span class="time">${time}</span>
539
+ <span class="title">${title}</span>
540
+ <span class="who">${m.mine ? "You organized" : m.organizerName}</span>
541
+ </button>
542
+ ${joinable ? html`<button
543
+ type="button"
544
+ class="join"
545
+ aria-label=${`Join ${title}`}
546
+ @click=${() => this.fire("office-agenda-join", { meetingId: m.id })}
547
+ >
548
+ ${m.live ? "Join now" : "Join"}
549
+ </button>` : ""}
550
+ </li>`;
551
+ }
552
+ clock(ms) {
553
+ return new Date(ms).toLocaleTimeString([], {
554
+ hour: "numeric",
555
+ minute: "2-digit",
556
+ timeZone: this.zone
557
+ });
558
+ }
559
+ };
560
+ var defineCalendarAgenda = definer(
561
+ "office-ui-calendar-agenda",
562
+ () => OfficeUiCalendarAgenda
563
+ );
564
+
565
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/calendar-view.css?raw
566
+ var calendar_view_default = ":host {\n --_hour: 4em;\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n background: var(--office-background);\n}\n.bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-2);\n padding: var(--office-space-3) var(--office-space-4);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.range {\n flex: 1 1 12em;\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\nbutton:hover {\n background: var(--office-selected);\n}\n.step {\n display: grid;\n place-items: center;\n padding: var(--office-space-2);\n}\n.step.next .modes {\n display: inline-flex;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n overflow: hidden;\n}\n.modes button {\n border: 0;\n border-radius: 0;\n}\n.modes button[aria-pressed='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.new {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-2);\n border-color: transparent;\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\n.new:hover {\n background: var(--office-teams-brand-hover);\n}\n.step.next svg {\n transform: scaleX(-1);\n}\n\n.scroller {\n flex: 1;\n min-height: 0;\n overflow: auto;\n}\n.grid {\n display: grid;\n grid-template-columns: 4em repeat(var(--cols), minmax(0, 1fr));\n grid-template-rows: auto auto;\n min-width: 0;\n}\n.corner,\n.head {\n position: sticky;\n inset-block-start: 0;\n z-index: 3;\n background: var(--office-background);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.head {\n display: grid;\n justify-items: center;\n padding: var(--office-space-2) 0;\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n}\n.dow {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.dom {\n display: grid;\n place-items: center;\n min-width: 1.8em;\n height: 1.8em;\n border-radius: var(--office-radius-full);\n font-size: var(--office-font-size-lg);\n}\n.head[data-today='true'] .dom {\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\n.hours {\n display: grid;\n grid-template-rows: repeat(24, var(--_hour));\n}\n.hours span {\n white-space: nowrap;\n padding-inline-end: var(--office-space-2);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n text-align: end;\n transform: translateY(-0.6em);\n}\n.column {\n position: relative;\n height: calc(24 * var(--_hour));\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n background-image: linear-gradient(\n to bottom,\n var(--office-teams-divider) var(--office-border-width),\n transparent var(--office-border-width)\n );\n background-size: 100% var(--_hour);\n}\n.column[data-today='true'] {\n background-color: var(--office-teams-brand-subtle);\n}\n.event {\n position: absolute;\n inset-block-start: calc(var(--top) * 1%);\n inset-inline-start: calc(var(--col) / var(--span) * 100%);\n box-sizing: border-box;\n width: calc(100% / var(--span));\n height: calc(var(--height) * 1%);\n min-height: 1.6em;\n display: grid;\n align-content: start;\n padding: var(--office-space-1) var(--office-space-2);\n overflow: hidden;\n border: var(--office-border-width) solid var(--office-teams-brand);\n border-inline-start-width: var(--office-border-width-thick);\n border-radius: var(--office-radius);\n background: var(--office-popover);\n text-align: start;\n z-index: 1;\n}\n.event[data-mine='true'] {\n background: var(--office-teams-bubble-own);\n}\n.event[data-live='true'] {\n border-color: var(--office-call-live);\n}\n.event-title {\n overflow: hidden;\n font-weight: var(--office-font-weight-bold);\n font-size: var(--office-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.event[data-short='true'] {\n align-content: center;\n}\n.event[data-short='true'] .event-time {\n display: none;\n}\n.event-time {\n overflow: hidden;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.now {\n position: absolute;\n inset-block-start: calc(var(--top) * 1%);\n inset-inline: 0;\n z-index: 2;\n height: var(--office-border-width-thick);\n background: var(--office-danger);\n pointer-events: none;\n}\n.now::before {\n content: '';\n position: absolute;\n inset-inline-start: calc(-1 * var(--office-space-1));\n inset-block-start: calc(-1 * var(--office-space-1));\n width: var(--office-space-2);\n height: var(--office-space-2);\n border-radius: var(--office-radius-full);\n background: var(--office-danger);\n}\n@media (max-width: 40em) {\n :host {\n --_hour: 4em;\n }\n .grid {\n grid-template-columns: 3.8em repeat(var(--cols), minmax(0, 1fr));\n }\n .bar button {\n min-height: 2.75em;\n }\n .modes button {\n padding-inline: var(--office-space-2);\n }\n}\n";
567
+
568
+ // src/teams/calendar-view.ts
569
+ var MODES = [
570
+ { id: "workweek", label: "Work week" },
571
+ { id: "week", label: "Week" },
572
+ { id: "day", label: "Day" }
573
+ ];
574
+ var SLOT_MINUTES = 30;
575
+ var FIRST_VISIBLE_HOUR = 8;
576
+ var OfficeUiCalendarView = class extends TeamsElement {
577
+ static styles = withStyles(calendar_view_default);
578
+ static properties = {
579
+ meetings: { attribute: false },
580
+ zone: { type: String },
581
+ now: { type: Number },
582
+ mode: { type: String, reflect: true },
583
+ anchor: { state: true },
584
+ tick: { state: true }
585
+ };
586
+ timer;
587
+ scrolled = false;
588
+ resize;
589
+ constructor() {
590
+ super();
591
+ this.meetings = [];
592
+ this.zone = localZone();
593
+ this.now = 0;
594
+ this.mode = "workweek";
595
+ this.anchor = 0;
596
+ this.tick = 0;
597
+ }
598
+ clock() {
599
+ return this.now || Date.now();
600
+ }
601
+ connectedCallback() {
602
+ super.connectedCallback();
603
+ this.setAttribute("role", "region");
604
+ this.setAttribute("aria-label", "Calendar");
605
+ if (!this.anchor) this.anchor = this.clock();
606
+ if (globalThis.matchMedia?.("(max-width: 40em)").matches && this.mode === "workweek")
607
+ this.mode = "day";
608
+ this.timer = setInterval(() => this.tick = this.clock(), 6e4);
609
+ if (typeof ResizeObserver !== "undefined") {
610
+ this.resize = new ResizeObserver(() => this.scrollToWorkday());
611
+ this.resize.observe(this);
612
+ }
613
+ }
614
+ disconnectedCallback() {
615
+ clearInterval(this.timer);
616
+ this.resize?.disconnect();
617
+ super.disconnectedCallback();
618
+ }
619
+ updated() {
620
+ this.scrollToWorkday();
621
+ }
622
+ /** Start the day at 8:00, once the grid has a size. */
623
+ scrollToWorkday() {
624
+ const scroller = this.renderRoot.querySelector(".scroller");
625
+ if (this.scrolled || !scroller || scroller.clientHeight === 0) return;
626
+ scroller.scrollTop = scroller.scrollHeight / 24 * FIRST_VISIBLE_HOUR;
627
+ this.scrolled = true;
628
+ }
629
+ willUpdate(changed) {
630
+ if (changed.has("now") && this.now && changed.get("now") === 0) this.anchor = this.now;
631
+ }
632
+ setMode(mode) {
633
+ this.mode = mode;
634
+ this.fire("office-calendar-mode", { mode });
635
+ }
636
+ range(days) {
637
+ const first = days[0];
638
+ const last = days[days.length - 1];
639
+ const fmt = (opts, ms) => new Intl.DateTimeFormat([], { ...opts, timeZone: this.zone }).format(ms);
640
+ if (days.length === 1)
641
+ return fmt({ weekday: "long", month: "long", day: "numeric", year: "numeric" }, first.start);
642
+ return new Intl.DateTimeFormat([], {
643
+ month: "long",
644
+ day: "numeric",
645
+ year: "numeric",
646
+ timeZone: this.zone
647
+ }).formatRange(first.start, last.start);
648
+ }
649
+ newAt(day, event) {
650
+ const column = event.currentTarget;
651
+ const box = column.getBoundingClientRect();
652
+ const fraction = box.height ? (event.clientY - box.top) / box.height : 0;
653
+ const minute = Math.floor(Math.max(0, fraction) * 1440 / SLOT_MINUTES) * SLOT_MINUTES;
654
+ const start = zonedToUtc(
655
+ { year: day.year, month: day.month, day: day.day, hour: 0, minute },
656
+ this.zone
657
+ );
658
+ this.fire("office-calendar-new", { start });
659
+ }
660
+ render() {
661
+ const now = this.clock();
662
+ const days = viewDays(this.anchor || now, this.mode, this.zone);
663
+ const todayStart = startOfDay(now, this.zone);
664
+ const nowParts = zonedParts(now, this.zone);
665
+ return html`
666
+ <div class="bar">
667
+ <h2 class="range" aria-live="polite">${this.range(days)}</h2>
668
+ <button type="button" class="today" @click=${() => this.anchor = this.clock()}>
669
+ Today
670
+ </button>
671
+ <button
672
+ type="button"
673
+ class="step"
674
+ aria-label=${this.mode === "day" ? "Previous day" : "Previous week"}
675
+ @click=${() => this.anchor = shiftAnchor(this.anchor || now, this.mode, -1, this.zone)}
676
+ >
677
+ ${icon("back")}
678
+ </button>
679
+ <button
680
+ type="button"
681
+ class="step next"
682
+ aria-label=${this.mode === "day" ? "Next day" : "Next week"}
683
+ @click=${() => this.anchor = shiftAnchor(this.anchor || now, this.mode, 1, this.zone)}
684
+ >
685
+ ${icon("back")}
686
+ </button>
687
+ <div class="modes" role="group" aria-label="Calendar view">
688
+ ${MODES.map(
689
+ (m) => html`<button
690
+ type="button"
691
+ aria-pressed=${String(this.mode === m.id)}
692
+ @click=${() => this.setMode(m.id)}
693
+ >
694
+ ${m.label}
695
+ </button>`
696
+ )}
697
+ </div>
698
+ <button
699
+ type="button"
700
+ class="new"
701
+ @click=${() => this.fire("office-calendar-new", { start: 0 })}
702
+ >
703
+ ${icon("plus")} New meeting
704
+ </button>
705
+ </div>
706
+ <div class="scroller">
707
+ <div class="grid" style=${`--cols:${days.length}`}>
708
+ <div class="corner"></div>
709
+ ${days.map((d) => this.dayHead(d, d.start === todayStart))}
710
+ <div class="hours" aria-hidden="true">
711
+ ${Array.from({ length: 24 }, (_, h) => html`<span>${this.hourLabel(h)}</span>`)}
712
+ </div>
713
+ ${days.map((d) => this.column(d, d.start === todayStart, nowParts.hour * 60 + nowParts.minute))}
714
+ </div>
715
+ </div>
716
+ `;
717
+ }
718
+ hourLabel(hour) {
719
+ if (hour === 0) return "";
720
+ return new Date(Date.UTC(2e3, 0, 1, hour)).toLocaleTimeString([], {
721
+ hour: "numeric",
722
+ timeZone: "UTC"
723
+ });
724
+ }
725
+ dayHead(day, today) {
726
+ const name = new Intl.DateTimeFormat([], { weekday: "short", timeZone: this.zone }).format(
727
+ day.start
728
+ );
729
+ return html`<div class="head" data-today=${String(today)}>
730
+ <span class="dow">${name}</span>
731
+ <span class="dom">${day.day}</span>
732
+ </div>`;
733
+ }
734
+ column(day, today, nowMinute) {
735
+ const occurrences = this.meetings.flatMap(
736
+ (m) => occurrencesBetween(m, day.start, day.end).map((o) => ({ o, m }))
737
+ );
738
+ const placed = layoutDay(
739
+ occurrences.map((x) => x.o),
740
+ day,
741
+ this.zone
742
+ );
743
+ const byKey = new Map(occurrences.map((x) => [x.o.key, x.m]));
744
+ const label = new Intl.DateTimeFormat([], {
745
+ weekday: "long",
746
+ month: "long",
747
+ day: "numeric",
748
+ timeZone: this.zone
749
+ }).format(day.start);
750
+ return html`<div
751
+ class="column"
752
+ role="group"
753
+ aria-label=${label}
754
+ data-today=${String(today)}
755
+ @click=${(e) => {
756
+ if (e.target === e.currentTarget) this.newAt(day, e);
757
+ }}
758
+ >
759
+ ${placed.map((p) => {
760
+ const meeting = byKey.get(p.occurrence.key);
761
+ const title = p.occurrence.title ?? meeting.title;
762
+ const time = `${this.clockText(p.occurrence.start)} - ${this.clockText(p.occurrence.end)}`;
763
+ return html`<button
764
+ type="button"
765
+ class="event"
766
+ data-live=${String(meeting.live)}
767
+ data-mine=${String(meeting.mine)}
768
+ data-short=${String(p.endMinute - p.startMinute <= 30)}
769
+ aria-label=${`${title}, ${time}`}
770
+ style=${`--top:${p.startMinute / 1440 * 100};--height:${(p.endMinute - p.startMinute) / 1440 * 100};--col:${p.column};--span:${p.columns}`}
771
+ @click=${(e) => {
772
+ e.stopPropagation();
773
+ this.fire("office-calendar-open", {
774
+ meetingId: meeting.id,
775
+ start: p.occurrence.start
776
+ });
777
+ }}
778
+ >
779
+ <span class="event-title">${title}</span>
780
+ <span class="event-time">${time}</span>
781
+ </button>`;
782
+ })}
783
+ ${today ? html`<span
784
+ class="now"
785
+ style=${`--top:${nowMinute / 1440 * 100}`}
786
+ aria-hidden="true"
787
+ ></span>` : nothing}
788
+ </div>`;
789
+ }
790
+ clockText(ms) {
791
+ return new Date(ms).toLocaleTimeString([], {
792
+ hour: "numeric",
793
+ minute: "2-digit",
794
+ timeZone: this.zone
795
+ });
796
+ }
797
+ };
798
+ var defineCalendarView = definer("office-ui-calendar-view", () => OfficeUiCalendarView);
799
+
265
800
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/call-grid.css?raw
266
801
  var call_grid_default = ":host {\n display: block;\n min-height: 0;\n background: var(--office-call-background);\n color: var(--office-call-foreground);\n}\n.empty {\n display: grid;\n place-items: center;\n height: 100%;\n margin: 0;\n color: var(--office-call-muted-foreground);\n}\n.grid {\n display: grid;\n gap: var(--office-space-2);\n height: 100%;\n padding: var(--office-space-2);\n grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));\n grid-auto-rows: minmax(0, 1fr);\n}\n.grid[data-spotlight='true'] {\n grid-template-columns: minmax(0, 4fr) minmax(0, 1fr);\n grid-template-rows: repeat(var(--rows, 1), minmax(0, 1fr));\n}\n.grid[data-spotlight='true'] .tile[data-main='true'] {\n grid-row: 1 / span var(--rows, 1);\n}\n\n.tile {\n position: relative;\n display: grid;\n place-items: center;\n min-height: 6em;\n overflow: hidden;\n border-radius: var(--office-radius-lg);\n background: var(--office-call-tile);\n}\nvideo {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: var(--office-call-background);\n}\n.tile[data-screen='true'] video {\n object-fit: contain;\n}\n.tile[data-self='true'] video {\n transform: scaleX(-1);\n}\n.tile[data-self='true'][data-screen='true'] video {\n transform: none;\n}\n\n.name {\n position: absolute;\n inset-inline-start: var(--office-space-2);\n inset-block-end: var(--office-space-2);\n display: flex;\n align-items: center;\n gap: var(--office-space-1-5);\n max-width: calc(100% - 2 * var(--office-space-2));\n padding: var(--office-space-0) var(--office-space-2);\n border-radius: var(--office-radius-md);\n background: var(--office-call-scrim);\n font-size: var(--office-font-size-sm);\n}\n.name span:last-child {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.muted {\n display: grid;\n color: var(--office-call-danger);\n}\n.hand {\n position: absolute;\n inset-block-start: var(--office-space-2);\n inset-inline-end: var(--office-space-2);\n font-size: 1.5em;\n}\n.status {\n position: absolute;\n inset-block-start: var(--office-space-2);\n inset-inline-start: var(--office-space-2);\n padding: 0 var(--office-space-2);\n border-radius: var(--office-radius-md);\n background: var(--office-call-scrim);\n font-size: var(--office-font-size-xs);\n}\n";
267
802
 
@@ -329,7 +864,7 @@ var OfficeUiCallGrid = class extends TeamsElement {
329
864
  var defineCallGrid = definer("office-ui-call-grid", () => OfficeUiCallGrid);
330
865
 
331
866
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/channel-list.css?raw
332
- var channel_list_default = ":host {\n display: block;\n overflow: auto;\n}\n.team {\n display: flex;\n align-items: center;\n padding: var(--office-space-1) var(--office-space-2) var(--office-space-1) var(--office-space-1);\n}\n.toggle {\n display: flex;\n flex: 1;\n align-items: center;\n gap: var(--office-space-1);\n min-width: 0;\n padding: var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n font-weight: var(--office-font-weight-bold);\n text-align: start;\n}\n.toggle:hover,\n.add:hover {\n background: var(--office-selected);\n}\n.chevron {\n display: grid;\n transition: transform var(--office-duration-fast);\n}\n.chevron[data-open='true'] {\n transform: rotate(90deg);\n}\n.name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.add {\n display: grid;\n place-items: center;\n width: 2em;\n height: 2em;\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\nul {\n margin: 0;\n padding: 0 var(--office-space-2);\n list-style: none;\n}\nul[hidden] {\n display: none;\n}\n.row {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3) var(--office-space-2) var(--office-space-5);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n text-align: start;\n}\n.row:hover {\n background: var(--office-selected);\n}\n.row[aria-current='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.row[aria-current='true']::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inset-block: 20%;\n width: var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.row[data-unread='true'] {\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.title {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.live {\n display: grid;\n color: var(--office-call-live);\n}\n.count {\n min-width: 1.6em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-xs);\n line-height: 1.6;\n text-align: center;\n}\n";
867
+ var channel_list_default = ":host {\n display: block;\n overflow: auto;\n}\n.team {\n display: flex;\n align-items: center;\n padding: var(--office-space-1) var(--office-space-2) var(--office-space-1) var(--office-space-1);\n}\n.toggle {\n display: flex;\n flex: 1;\n align-items: center;\n gap: var(--office-space-1);\n min-width: 0;\n padding: var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n font-weight: var(--office-font-weight-bold);\n text-align: start;\n}\n.toggle:hover,\n.add:hover {\n background: var(--office-selected);\n}\n.chevron {\n display: grid;\n transition: transform var(--office-duration-fast);\n}\n.chevron[data-open='true'] {\n transform: rotate(90deg);\n}\n.name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.add {\n display: grid;\n place-items: center;\n width: 2em;\n height: 2em;\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\nul {\n margin: 0;\n padding: 0 var(--office-space-2);\n list-style: none;\n}\nul[hidden] {\n display: none;\n}\n.row {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3) var(--office-space-2) var(--office-space-5);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n text-align: start;\n}\n.row:hover {\n background: var(--office-selected);\n}\n.row[aria-current='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.row[aria-current='true']::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inset-block: 20%;\n width: var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.row[data-unread='true'] {\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.title {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.live {\n display: grid;\n color: var(--office-call-live);\n}\n.count {\n min-width: 1.6em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-xs);\n line-height: 1.6;\n text-align: center;\n}\n\n@media (pointer: coarse) {\n .toggle,\n .add,\n .row {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n";
333
868
 
334
869
  // src/teams/channel-list.ts
335
870
  var OfficeUiChannelList = class extends TeamsElement {
@@ -395,7 +930,7 @@ var OfficeUiChannelList = class extends TeamsElement {
395
930
  var defineChannelList = definer("office-ui-channel-list", () => OfficeUiChannelList);
396
931
 
397
932
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/chat-composer.css?raw
398
- var chat_composer_default = ":host {\n display: block;\n min-width: 0;\n flex: none;\n padding: 0 var(--office-space-5) var(--office-space-4);\n}\n.typing {\n min-height: 1.4em;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.box {\n position: relative;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-block-end: var(--office-border-width-thick) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-field-background);\n}\n.box:focus-within {\n border-block-end-color: var(--office-teams-brand);\n}\n.banner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-1) var(--office-space-3);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n background: var(--office-teams-panel);\n font-size: var(--office-font-size-sm);\n}\ntextarea {\n display: block;\n width: 100%;\n min-height: 3.2em;\n max-height: 12em;\n padding: var(--office-space-3);\n border: 0;\n outline: 0;\n resize: none;\n background: transparent;\n color: inherit;\n font: inherit;\n overflow-y: auto;\n line-height: 1.5;\n}\n.bar {\n display: flex;\n align-items: center;\n gap: var(--office-space-1);\n padding: 0 var(--office-space-2) var(--office-space-2);\n}\n.spacer {\n flex: 1;\n}\n.bar button,\n.banner button,\n.pending button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1);\n min-width: 2.2em;\n height: 2.2em;\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\n.bar button:hover:not(:disabled),\n.banner button:hover {\n background: var(--office-selected);\n color: var(--office-foreground);\n}\n.bar button:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bar .send:not(:disabled) {\n color: var(--office-teams-brand);\n}\n.pending {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n margin: 0;\n padding: 0 var(--office-space-3);\n list-style: none;\n}\n.pending button {\n padding: 0 var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n background: var(--office-teams-panel);\n font-size: var(--office-font-size-sm);\n color: var(--office-foreground);\n}\n.emoji {\n position: relative;\n}\n.picker {\n position: absolute;\n inset-block-end: calc(100% + var(--office-space-1));\n inset-inline-start: 0;\n z-index: 2;\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: var(--office-space-0);\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.picker button {\n font-size: 1.3em;\n}\n\ntextarea:focus-visible {\n outline: none;\n}\n.box:focus-within {\n border-color: var(--office-teams-brand);\n}\n.pending {\n max-height: 7em;\n overflow-y: auto;\n padding-block-end: var(--office-space-2);\n}\n.pending li {\n min-width: 0;\n max-width: 100%;\n}\n.pending button {\n max-width: 100%;\n height: auto;\n min-height: 2.4em;\n}\n.file-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n@media (max-width: 600px) {\n :host {\n padding: 0 var(--office-space-2) var(--office-space-2);\n }\n textarea {\n font-size: var(--office-font-size-touch);\n min-height: 3em;\n }\n .bar button {\n min-width: var(--office-touch-target);\n height: var(--office-touch-target);\n }\n}\n";
933
+ var chat_composer_default = ":host {\n display: block;\n min-width: 0;\n flex: none;\n padding: 0 var(--office-space-5) var(--office-space-4);\n}\n.typing {\n min-height: 1.4em;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.box {\n position: relative;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-block-end: var(--office-border-width-thick) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-field-background);\n}\n.box:focus-within {\n border-block-end-color: var(--office-teams-brand);\n}\n.banner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-1) var(--office-space-3);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n background: var(--office-teams-panel);\n font-size: var(--office-font-size-sm);\n}\ntextarea {\n display: block;\n width: 100%;\n min-height: 3.2em;\n max-height: 12em;\n padding: var(--office-space-3);\n border: 0;\n outline: 0;\n resize: none;\n background: transparent;\n color: inherit;\n font: inherit;\n overflow-y: auto;\n line-height: 1.5;\n}\n.bar {\n display: flex;\n align-items: center;\n gap: var(--office-space-1);\n padding: 0 var(--office-space-2) var(--office-space-2);\n}\n.spacer {\n flex: 1;\n}\n.bar button,\n.banner button,\n.pending button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1);\n min-width: 2.2em;\n height: 2.2em;\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n}\n.bar button:hover:not(:disabled),\n.banner button:hover {\n background: var(--office-selected);\n color: var(--office-foreground);\n}\n.bar button:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bar .send:not(:disabled) {\n color: var(--office-teams-brand);\n}\n.pending {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n margin: 0;\n padding: 0 var(--office-space-3);\n list-style: none;\n}\n.pending button {\n padding: 0 var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n background: var(--office-teams-panel);\n font-size: var(--office-font-size-sm);\n color: var(--office-foreground);\n}\n.emoji {\n position: relative;\n}\n.picker {\n position: absolute;\n inset-block-end: calc(100% + var(--office-space-1));\n inset-inline-start: 0;\n z-index: 2;\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: var(--office-space-0);\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.picker button {\n font-size: 1.3em;\n}\n\ntextarea:focus-visible {\n outline: none;\n}\n.box:focus-within {\n border-color: var(--office-teams-brand);\n}\n.pending {\n max-height: 7em;\n overflow-y: auto;\n padding-block-end: var(--office-space-2);\n}\n.pending li {\n min-width: 0;\n max-width: 100%;\n}\n.pending button {\n max-width: 100%;\n height: auto;\n min-height: 2.4em;\n}\n.file-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n@media (max-width: 600px) {\n :host {\n padding: 0 var(--office-space-2) var(--office-space-2);\n }\n textarea {\n font-size: var(--office-font-size-touch);\n min-height: 3em;\n }\n}\n@media (pointer: coarse) {\n .bar button,\n .banner button,\n .pending button,\n .picker button {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n\n/* The @ picker: a listbox of people above the compose box. */\n.mentions {\n position: absolute;\n inset-block-end: calc(100% + var(--office-space-1));\n inset-inline: 0;\n z-index: 3;\n max-height: 16em;\n margin: 0;\n padding: var(--office-space-1);\n overflow-y: auto;\n list-style: none;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.mentions li {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-height: 2.6em;\n padding: var(--office-space-1) var(--office-space-3);\n border-radius: var(--office-radius);\n cursor: pointer;\n}\n.mentions li[aria-selected='true'],\n.mentions li:hover {\n background: var(--office-selected);\n}\n.mentions .name {\n font-weight: 600;\n}\n.mentions .hint {\n margin-inline-start: auto;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.mentions .everyone {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2em;\n height: 2em;\n border-radius: 50%;\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n font-weight: 700;\n}\n@media (pointer: coarse) {\n .mentions li {\n min-height: var(--office-touch-target, 44px);\n }\n}\n";
399
934
  var EMOJI = [
400
935
  "\u{1F600}",
401
936
  "\u{1F604}",
@@ -430,8 +965,12 @@ var OfficeUiChatComposer = class extends TeamsElement {
430
965
  value: { type: String },
431
966
  files: { state: true },
432
967
  missingFiles: { attribute: false },
433
- emojiOpen: { state: true }
968
+ emojiOpen: { state: true },
969
+ mentionTargets: { attribute: false },
970
+ picker: { state: true },
971
+ pickerIndex: { state: true }
434
972
  };
973
+ inserted = [];
435
974
  lastTyping = 0;
436
975
  constructor() {
437
976
  super();
@@ -444,6 +983,9 @@ var OfficeUiChatComposer = class extends TeamsElement {
444
983
  this.files = [];
445
984
  this.missingFiles = [];
446
985
  this.emojiOpen = false;
986
+ this.mentionTargets = [];
987
+ this.picker = null;
988
+ this.pickerIndex = 0;
447
989
  }
448
990
  focusMessage() {
449
991
  void this.updateComplete.then(
@@ -468,15 +1010,19 @@ var OfficeUiChatComposer = class extends TeamsElement {
468
1010
  }
469
1011
  submit() {
470
1012
  if (!this.canSend) return;
471
- const { value: text, files } = this;
1013
+ const { value: text, files, inserted: mentions } = this;
472
1014
  this.value = "";
473
1015
  this.files = [];
1016
+ this.inserted = [];
474
1017
  this.emojiOpen = false;
475
- this.fire("office-chat-send", { text, files });
1018
+ this.picker = null;
1019
+ const detail = { text, files, ...mentions.length ? { mentions } : {} };
1020
+ this.fire("office-chat-send", detail);
476
1021
  this.focusMessage();
477
1022
  }
478
1023
  onInput(event) {
479
1024
  this.value = event.target.value;
1025
+ this.syncPicker();
480
1026
  this.saveDraft();
481
1027
  const now = Date.now();
482
1028
  if (now - this.lastTyping > 2e3) {
@@ -484,7 +1030,51 @@ var OfficeUiChatComposer = class extends TeamsElement {
484
1030
  this.fire("office-chat-typing", {});
485
1031
  }
486
1032
  }
1033
+ /** Open the picker while the caret sits in an `@query` that somebody matches. */
1034
+ syncPicker() {
1035
+ const area = this.renderRoot.querySelector("textarea");
1036
+ const query = area ? mentionQueryAt(this.value, area.selectionStart) : null;
1037
+ if (!query || filterMentionTargets(this.mentionTargets, query.query).length === 0) {
1038
+ this.picker = null;
1039
+ return;
1040
+ }
1041
+ if (this.picker?.start !== query.start || this.picker.query !== query.query)
1042
+ this.pickerIndex = 0;
1043
+ this.picker = query;
1044
+ }
1045
+ pickerOptions() {
1046
+ return this.picker ? filterMentionTargets(this.mentionTargets, this.picker.query) : [];
1047
+ }
1048
+ chooseMention(target) {
1049
+ const area = this.renderRoot.querySelector("textarea");
1050
+ const picker = this.picker;
1051
+ if (!area || !picker) return;
1052
+ const next = insertMention(this.value, picker, area.selectionStart, target);
1053
+ this.value = next.text;
1054
+ this.inserted = [...this.inserted, target];
1055
+ this.picker = null;
1056
+ this.saveDraft();
1057
+ void this.updateComplete.then(() => {
1058
+ area.focus({ preventScroll: true });
1059
+ area.setSelectionRange(next.caret, next.caret);
1060
+ });
1061
+ }
1062
+ /** Picker keys; true when the key was used. */
1063
+ pickerKey(event) {
1064
+ const options = this.pickerOptions();
1065
+ if (!this.picker || options.length === 0) return false;
1066
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1067
+ const step = event.key === "ArrowDown" ? 1 : -1;
1068
+ this.pickerIndex = (this.pickerIndex + step + options.length) % options.length;
1069
+ } else if (event.key === "Enter" && !event.shiftKey && !event.isComposing || event.key === "Tab") {
1070
+ this.chooseMention(options[Math.min(this.pickerIndex, options.length - 1)]);
1071
+ } else if (event.key === "Escape") this.picker = null;
1072
+ else return false;
1073
+ event.preventDefault();
1074
+ return true;
1075
+ }
487
1076
  onKeydown(event) {
1077
+ if (this.pickerKey(event)) return;
488
1078
  if (event.key === "Escape" && this.emojiOpen) {
489
1079
  this.emojiOpen = false;
490
1080
  this.focusMessage();
@@ -523,6 +1113,34 @@ var OfficeUiChatComposer = class extends TeamsElement {
523
1113
  if (n.length === 0) return "";
524
1114
  return n.length === 1 ? `${n[0]} is typing\u2026` : `${n.join(", ")} are typing\u2026`;
525
1115
  }
1116
+ mentionList() {
1117
+ const options = this.pickerOptions();
1118
+ if (options.length === 0) return nothing;
1119
+ return html`<ul
1120
+ id="mention-list"
1121
+ class="mentions"
1122
+ role="listbox"
1123
+ aria-label="People to mention"
1124
+ @mousedown=${(event) => event.preventDefault()}
1125
+ >
1126
+ ${options.map(
1127
+ (target, index) => html`<li
1128
+ id=${`mention-option-${index}`}
1129
+ role="option"
1130
+ aria-selected=${String(index === this.pickerIndex)}
1131
+ @click=${() => this.chooseMention(target)}
1132
+ >
1133
+ ${target.kind === "user" ? html`<office-ui-avatar
1134
+ size="sm"
1135
+ name=${target.name}
1136
+ seed=${target.id}
1137
+ ></office-ui-avatar>` : html`<span class="everyone" aria-hidden="true">@</span>`}
1138
+ <span class="name">${target.name}</span>
1139
+ ${target.kind === "user" ? nothing : html`<span class="hint">${target.kind === "channel" ? "Notify everyone in this channel" : "Notify the whole team"}</span>`}
1140
+ </li>`
1141
+ )}
1142
+ </ul>`;
1143
+ }
526
1144
  render() {
527
1145
  const banner = this.editing ? "Editing message" : this.replyingTo !== null ? `Replying to ${this.replyingTo}` : "";
528
1146
  return html`
@@ -572,9 +1190,13 @@ var OfficeUiChatComposer = class extends TeamsElement {
572
1190
  ${icon("close")}
573
1191
  </button>
574
1192
  </div>` : nothing}
1193
+ ${this.mentionList()}
575
1194
  <textarea
576
1195
  rows="1"
577
1196
  aria-label="Message"
1197
+ aria-haspopup="listbox"
1198
+ aria-controls=${this.pickerOptions().length ? "mention-list" : nothing}
1199
+ aria-activedescendant=${this.pickerOptions().length ? `mention-option-${this.pickerIndex}` : nothing}
578
1200
  .value=${live(this.value)}
579
1201
  placeholder=${this.placeholder}
580
1202
  ?disabled=${this.disabled}
@@ -586,6 +1208,11 @@ var OfficeUiChatComposer = class extends TeamsElement {
586
1208
  }
587
1209
  }}
588
1210
  @keydown=${this.onKeydown}
1211
+ @keyup=${(event) => {
1212
+ if (["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) this.syncPicker();
1213
+ }}
1214
+ @click=${this.syncPicker}
1215
+ @blur=${() => this.picker = null}
589
1216
  ></textarea>
590
1217
  ${this.files.length ? html`<ul class="pending">
591
1218
  ${this.files.map(
@@ -668,7 +1295,7 @@ var OfficeUiChatComposer = class extends TeamsElement {
668
1295
  var defineChatComposer = definer("office-ui-chat-composer", () => OfficeUiChatComposer);
669
1296
 
670
1297
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/chat-list.css?raw
671
- var chat_list_default = ":host {\n display: block;\n min-height: 0;\n overflow: auto;\n padding: var(--office-space-2) var(--office-space-5);\n}\n.thread-link {\n align-self: flex-start;\n font: inherit;\n color: var(--office-foreground);\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2);\n cursor: pointer;\n}\n.empty {\n margin: var(--office-space-6) 0;\n color: var(--office-teams-text-subtle);\n text-align: center;\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n margin: var(--office-space-4) 0 var(--office-space-2);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.day::before,\n.day::after {\n content: '';\n flex: 1;\n height: var(--office-border-width);\n background: var(--office-teams-divider);\n}\n\n.msg {\n position: relative;\n display: grid;\n grid-template-columns: 2.3em minmax(0, 1fr);\n column-gap: var(--office-space-3);\n padding: var(--office-space-0) 0;\n}\n.msg[data-grouped='false'] {\n margin-top: var(--office-teams-message-gap, var(--office-space-3));\n}\n.gutter {\n padding-top: var(--office-space-1);\n}\nheader {\n display: flex;\n align-items: baseline;\n gap: var(--office-space-2);\n margin-bottom: var(--office-space-0);\n}\n.author {\n font-weight: var(--office-font-weight-bold);\n}\ntime,\n.edited,\n.file-size {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n\n.bubble {\n display: inline-block;\n max-width: min(100%, 44em);\n padding: var(--office-teams-message-padding, var(--office-space-2) var(--office-space-3));\n border-radius: var(--office-radius-md);\n background: var(--office-teams-bubble);\n line-height: var(--office-line-height);\n overflow-wrap: anywhere;\n}\n.text {\n white-space: pre-wrap;\n}\n.msg[data-own='true'] .bubble {\n background: var(--office-teams-bubble-own);\n}\n.deleted {\n color: var(--office-teams-text-subtle);\n font-style: italic;\n}\nblockquote {\n margin: 0 0 var(--office-space-1);\n padding-inline-start: var(--office-space-2);\n border-inline-start: var(--office-border-width-thick) solid var(--office-teams-brand);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n white-space: normal;\n}\nblockquote strong {\n display: block;\n color: var(--office-foreground);\n}\n\n.files {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-2);\n margin-top: var(--office-space-2);\n white-space: normal;\n}\n.file {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-width: 14em;\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n text-align: start;\n}\n.file:hover {\n border-color: var(--office-teams-brand);\n}\n.badge {\n display: grid;\n place-items: center;\n width: 2.2em;\n height: 2.2em;\n border-radius: var(--office-radius);\n background: var(--office-file-other);\n color: var(--office-call-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.badge[data-kind='docx'] {\n background: var(--office-file-docx);\n}\n.badge[data-kind='xlsx'] {\n background: var(--office-file-xlsx);\n}\n.badge[data-kind='pptx'] {\n background: var(--office-file-pptx);\n}\n.badge[data-kind='vsdx'] {\n background: var(--office-file-vsdx);\n}\n.meta {\n display: grid;\n}\n\n.reactions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n margin-top: var(--office-space-1);\n}\n.pill {\n display: inline-flex;\n gap: var(--office-space-1);\n padding: 0 var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: var(--office-background);\n font-size: var(--office-font-size-sm);\n}\n.pill[aria-pressed='true'] {\n border-color: var(--office-teams-brand);\n background: var(--office-teams-brand-subtle);\n}\n\n.toolbar {\n position: absolute;\n inset-block-start: calc(-1 * var(--office-space-3));\n inset-inline-end: 0;\n display: none;\n gap: var(--office-space-0);\n padding: var(--office-space-0);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.msg:hover .toolbar,\n.msg:focus-within .toolbar {\n display: flex;\n}\n.toolbar button {\n display: grid;\n place-items: center;\n min-width: 2em;\n height: 2em;\n border: 0;\n border-radius: var(--office-radius-sm);\n background: transparent;\n}\n.toolbar button:hover {\n background: var(--office-selected);\n}\n";
1298
+ var chat_list_default = ":host {\n display: block;\n min-height: 0;\n overflow: auto;\n padding: var(--office-space-2) var(--office-space-5);\n}\n.thread-link {\n align-self: flex-start;\n font: inherit;\n color: var(--office-foreground);\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2);\n cursor: pointer;\n}\n.empty {\n margin: var(--office-space-6) 0;\n color: var(--office-teams-text-subtle);\n text-align: center;\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n margin: var(--office-space-4) 0 var(--office-space-2);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.day::before,\n.day::after {\n content: '';\n flex: 1;\n height: var(--office-border-width);\n background: var(--office-teams-divider);\n}\n\n.msg {\n position: relative;\n display: grid;\n grid-template-columns: 2.3em minmax(0, 1fr);\n column-gap: var(--office-space-3);\n padding: var(--office-space-0) 0;\n}\n.msg[data-grouped='false'] {\n margin-top: var(--office-teams-message-gap, var(--office-space-3));\n}\n.gutter {\n padding-top: var(--office-space-1);\n}\nheader {\n display: flex;\n align-items: baseline;\n gap: var(--office-space-2);\n margin-bottom: var(--office-space-0);\n}\n.author {\n font-weight: var(--office-font-weight-bold);\n}\ntime,\n.edited,\n.file-size {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n\n.bubble {\n display: inline-block;\n max-width: min(100%, 44em);\n padding: var(--office-teams-message-padding, var(--office-space-2) var(--office-space-3));\n border-radius: var(--office-radius-md);\n background: var(--office-teams-bubble);\n line-height: var(--office-line-height);\n overflow-wrap: anywhere;\n}\n.text {\n white-space: pre-wrap;\n}\n.msg[data-own='true'] .bubble {\n background: var(--office-teams-bubble-own);\n}\n.deleted {\n color: var(--office-teams-text-subtle);\n font-style: italic;\n}\nblockquote {\n margin: 0 0 var(--office-space-1);\n padding-inline-start: var(--office-space-2);\n border-inline-start: var(--office-border-width-thick) solid var(--office-teams-brand);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n white-space: normal;\n}\nblockquote strong {\n display: block;\n color: var(--office-foreground);\n}\n\n.files {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-2);\n margin-top: var(--office-space-2);\n white-space: normal;\n}\n.file {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-width: 14em;\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n text-align: start;\n}\n.file:hover {\n border-color: var(--office-teams-brand);\n}\n.badge {\n display: grid;\n place-items: center;\n width: 2.2em;\n height: 2.2em;\n border-radius: var(--office-radius);\n background: var(--office-file-other);\n color: var(--office-call-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.badge[data-kind='docx'] {\n background: var(--office-file-docx);\n}\n.badge[data-kind='xlsx'] {\n background: var(--office-file-xlsx);\n}\n.badge[data-kind='pptx'] {\n background: var(--office-file-pptx);\n}\n.badge[data-kind='vsdx'] {\n background: var(--office-file-vsdx);\n}\n.meta {\n display: grid;\n}\n\n.reactions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n margin-top: var(--office-space-1);\n}\n.pill {\n display: inline-flex;\n gap: var(--office-space-1);\n padding: 0 var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: var(--office-background);\n font-size: var(--office-font-size-sm);\n}\n.pill[aria-pressed='true'] {\n border-color: var(--office-teams-brand);\n background: var(--office-teams-brand-subtle);\n}\n\n.toolbar {\n position: absolute;\n inset-block-start: calc(-1 * var(--office-space-3));\n inset-inline-end: 0;\n display: none;\n gap: var(--office-space-0);\n padding: var(--office-space-0);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.msg:hover .toolbar,\n.msg:focus-within .toolbar {\n display: flex;\n}\n.toolbar button {\n display: grid;\n place-items: center;\n min-width: 2em;\n height: 2em;\n border: 0;\n border-radius: var(--office-radius-sm);\n background: transparent;\n}\n.toolbar button:hover {\n background: var(--office-selected);\n}\n\n.msg:focus {\n outline: none;\n}\n.pin-mark {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-1);\n color: var(--office-teams-brand);\n font-size: var(--office-font-size-xs);\n}\n.pin-mark svg {\n width: 1em;\n height: 1em;\n}\n@media (pointer: coarse) {\n .toolbar button {\n min-width: var(--office-touch-target, 44px);\n height: var(--office-touch-target, 44px);\n }\n}\n\n/* @mentions: the name in the brand colour; a mention of you (or of everyone) gets a chip. */\n.mention {\n color: var(--office-teams-brand);\n font-weight: 600;\n}\n.mention[data-self='true'] {\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius);\n background: var(--office-teams-brand-subtle);\n}\n.msg[data-mentions-you='true'] .bubble {\n box-shadow: inset var(--office-border-width-thick) 0 0 var(--office-teams-brand);\n}\n\n/* A message opened from Activity or the pinned bar flashes once. */\n.msg[data-highlight='true'] .bubble {\n animation: flash 2.4s ease-out;\n}\n@keyframes flash {\n from {\n background: var(--office-teams-brand-subtle);\n outline: var(--office-border-width-thick) solid var(--office-teams-brand);\n }\n to {\n outline: var(--office-border-width-thick) solid transparent;\n }\n}\n\n.system {\n margin: var(--office-space-3) 0;\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n text-align: center;\n}\n.system:focus {\n outline: none;\n}\n\n.meeting-card {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto auto;\n align-items: center;\n gap: var(--office-space-2) var(--office-space-3);\n min-width: min(22em, 100%);\n margin-top: var(--office-space-2);\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-inline-start: var(--office-border-width-thick) solid var(--office-teams-brand);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n white-space: normal;\n}\n.meeting-card[data-gone='true'] {\n grid-template-columns: auto minmax(0, 1fr);\n color: var(--office-teams-text-subtle);\n}\n.meeting-icon {\n display: grid;\n place-items: center;\n width: 2.2em;\n height: 2.2em;\n border-radius: var(--office-radius);\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n}\n.meeting-meta {\n display: grid;\n min-width: 0;\n}\n.meeting-title {\n font-weight: var(--office-font-weight-bold);\n}\n.meeting-time {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.meeting-open,\n.meeting-join {\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.meeting-join {\n border-color: transparent;\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\n.meeting-join:hover {\n background: var(--office-teams-brand-hover);\n}\n@media (max-width: 40em) {\n .meeting-card {\n grid-template-columns: auto minmax(0, 1fr);\n }\n .meeting-open,\n .meeting-join {\n grid-column: 1 / -1;\n min-height: 2.75em;\n }\n}\n";
672
1299
 
673
1300
  // src/teams/chat-list.ts
674
1301
  var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F389}"];
@@ -685,7 +1312,10 @@ var OfficeUiChatList = class extends TeamsElement {
685
1312
  static properties = {
686
1313
  messages: { attribute: false },
687
1314
  selfId: { type: String, attribute: "self-id" },
688
- replyCounts: { attribute: false }
1315
+ replyCounts: { attribute: false },
1316
+ pinnedIds: { attribute: false },
1317
+ highlightId: { type: String, attribute: "highlight-id" },
1318
+ meetings: { attribute: false }
689
1319
  };
690
1320
  stickToBottom = true;
691
1321
  constructor() {
@@ -693,6 +1323,9 @@ var OfficeUiChatList = class extends TeamsElement {
693
1323
  this.messages = [];
694
1324
  this.selfId = "";
695
1325
  this.replyCounts = {};
1326
+ this.pinnedIds = [];
1327
+ this.highlightId = "";
1328
+ this.meetings = {};
696
1329
  }
697
1330
  connectedCallback() {
698
1331
  super.connectedCallback();
@@ -712,6 +1345,11 @@ var OfficeUiChatList = class extends TeamsElement {
712
1345
  const rows = [];
713
1346
  let previous;
714
1347
  for (const m of this.messages) {
1348
+ if (m.system && !m.deleted) {
1349
+ rows.push(html`<div class="system" data-message-id=${m.id} tabindex="-1">${m.text}</div>`);
1350
+ previous = void 0;
1351
+ continue;
1352
+ }
715
1353
  if (!previous || dayLabel(previous.ts) !== dayLabel(m.ts))
716
1354
  rows.push(html`<div class="day"><span>${dayLabel(m.ts)}</span></div>`);
717
1355
  const grouped = previous !== void 0 && previous.authorId === m.authorId && m.ts - previous.ts < GROUP_MS && !m.replyTo && dayLabel(previous.ts) === dayLabel(m.ts);
@@ -726,9 +1364,12 @@ var OfficeUiChatList = class extends TeamsElement {
726
1364
  return html`
727
1365
  <article
728
1366
  class="msg"
1367
+ tabindex="-1"
729
1368
  data-message-id=${m.id}
730
1369
  data-own=${String(own)}
1370
+ data-highlight=${String(m.id === this.highlightId)}
731
1371
  data-grouped=${String(grouped)}
1372
+ data-mentions-you=${String(!m.deleted && mentionsUser(m, this.selfId) && !own)}
732
1373
  >
733
1374
  <div class="gutter">
734
1375
  ${grouped ? nothing : html`<office-ui-avatar name=${m.authorName} seed=${m.authorId}></office-ui-avatar>`}
@@ -738,12 +1379,14 @@ var OfficeUiChatList = class extends TeamsElement {
738
1379
  <span class="author">${m.authorName}</span>
739
1380
  <time>${formatTime(m.ts)}</time>
740
1381
  </header>`}
1382
+ ${this.pinMark(m)}
741
1383
  <div class="bubble">
742
1384
  ${parent ? html`<blockquote>
743
1385
  <strong>${parent.authorName}</strong
744
1386
  >${parent.deleted ? "This message was deleted" : parent.text.slice(0, 120)}
745
1387
  </blockquote>` : nothing}
746
- ${m.deleted ? html`<span class="deleted">This message was deleted</span>` : html`<span class="text">${m.text}</span>${m.editedAt ? html` <span class="edited">(edited)</span>` : nothing}`}
1388
+ ${m.deleted ? html`<span class="deleted">This message was deleted</span>` : html`<span class="text">${this.body(m)}</span>${m.editedAt ? html` <span class="edited">(edited)</span>` : nothing}`}
1389
+ ${!m.deleted && m.meetingId ? this.meetingCard(m.meetingId) : nothing}
747
1390
  ${!m.deleted && m.attachments?.length ? this.files(m.attachments) : nothing}
748
1391
  ${!m.deleted && m.tabId ? html`<button class="thread-link" type="button" @click=${() => this.fire("office-chat-open-tab", { tabId: m.tabId })}>Open tab</button>` : nothing}
749
1392
  </div>
@@ -768,6 +1411,47 @@ var OfficeUiChatList = class extends TeamsElement {
768
1411
  </article>
769
1412
  `;
770
1413
  }
1414
+ /** Plain runs and highlighted mentions (the name without its `@`, as in Teams), with no whitespace between them. */
1415
+ body(m) {
1416
+ return mentionSegments(m.text, m.mentions).map(
1417
+ (segment) => segment.mention ? html`<span
1418
+ class="mention"
1419
+ data-self=${String(segment.mention.kind !== "user" || segment.mention.id === this.selfId)}
1420
+ >${segment.text.slice(1)}</span
1421
+ >` : segment.text
1422
+ );
1423
+ }
1424
+ meetingCard(meetingId) {
1425
+ const card = this.meetings[meetingId];
1426
+ if (!card)
1427
+ return html`<div class="meeting-card" data-gone="true">
1428
+ <span class="meeting-icon">${icon("calendar")}</span>
1429
+ <span class="meeting-meta">
1430
+ <span class="meeting-title">This meeting was canceled or is not in your calendar</span>
1431
+ </span>
1432
+ </div>`;
1433
+ return html`<div class="meeting-card">
1434
+ <span class="meeting-icon">${icon("calendar")}</span>
1435
+ <span class="meeting-meta">
1436
+ <span class="meeting-title">${card.title}</span>
1437
+ <span class="meeting-time">${formatWhen(card.start, card.end)}</span>
1438
+ </span>
1439
+ <button
1440
+ type="button"
1441
+ class="meeting-open"
1442
+ @click=${() => this.fire("office-chat-meeting", { meetingId, action: "open" })}
1443
+ >
1444
+ Details
1445
+ </button>
1446
+ <button
1447
+ type="button"
1448
+ class="meeting-join"
1449
+ @click=${() => this.fire("office-chat-meeting", { meetingId, action: "join" })}
1450
+ >
1451
+ ${card.live ? "Join now" : "Join"}
1452
+ </button>
1453
+ </div>`;
1454
+ }
771
1455
  toolbar(m, own) {
772
1456
  return html`
773
1457
  <div class="toolbar" role="toolbar" aria-label="Message actions">
@@ -788,6 +1472,7 @@ var OfficeUiChatList = class extends TeamsElement {
788
1472
  >
789
1473
  ${icon("reply")}
790
1474
  </button>
1475
+ ${this.pinButton(m)}
791
1476
  ${own ? html`<button
792
1477
  type="button"
793
1478
  title="Edit"
@@ -807,6 +1492,22 @@ var OfficeUiChatList = class extends TeamsElement {
807
1492
  </div>
808
1493
  `;
809
1494
  }
1495
+ pinMark(m) {
1496
+ return !m.deleted && this.pinnedIds.includes(m.id) ? html`<span class="pin-mark">${icon("pin")} Pinned</span>` : nothing;
1497
+ }
1498
+ pinButton(m) {
1499
+ const pinned = this.pinnedIds.includes(m.id);
1500
+ const label = pinned ? "Unpin" : "Pin";
1501
+ return html`<button
1502
+ type="button"
1503
+ title=${label}
1504
+ aria-label=${label}
1505
+ aria-pressed=${String(pinned)}
1506
+ @click=${() => this.fire(pinned ? "office-chat-unpin" : "office-chat-pin", { messageId: m.id })}
1507
+ >
1508
+ ${icon("pin")}
1509
+ </button>`;
1510
+ }
810
1511
  files(files) {
811
1512
  return html`<div class="files">
812
1513
  ${files.map(
@@ -830,6 +1531,161 @@ var OfficeUiChatList = class extends TeamsElement {
830
1531
  };
831
1532
  var defineChatList = definer("office-ui-chat-list", () => OfficeUiChatList);
832
1533
 
1534
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/chat-sidebar.css?raw
1535
+ var chat_sidebar_default = ":host {\n display: block;\n overflow: auto;\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-3) var(--office-space-3) var(--office-space-2) var(--office-space-4);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\n.new {\n display: grid;\n place-items: center;\n width: 2.2em;\n height: 2.2em;\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n color: var(--office-teams-brand);\n}\n.new:hover {\n background: var(--office-selected);\n}\nul {\n margin: 0;\n padding: 0 var(--office-space-2);\n list-style: none;\n}\n.empty {\n margin: var(--office-space-4);\n color: var(--office-teams-text-subtle);\n}\n.row {\n position: relative;\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-teams-text-subtle);\n text-align: start;\n}\n.row:hover {\n background: var(--office-selected);\n}\n.row[aria-current='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-foreground);\n}\n.row[aria-current='true']::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inset-block: 20%;\n width: var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n}\n.group {\n display: grid;\n place-items: center;\n flex: none;\n width: 2.3em;\n height: 2.3em;\n border-radius: var(--office-radius-full);\n background: var(--office-teams-bubble);\n color: var(--office-teams-brand);\n}\n.text {\n display: grid;\n flex: 1;\n min-width: 0;\n}\n.line {\n display: flex;\n align-items: baseline;\n gap: var(--office-space-2);\n}\n.title {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n color: var(--office-foreground);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\ntime {\n font-size: var(--office-font-size-xs);\n}\n.preview {\n overflow: hidden;\n font-size: var(--office-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.row[data-unread='true'] .title,\n.row[data-unread='true'] .preview {\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.live {\n display: grid;\n color: var(--office-call-live);\n}\n.count {\n min-width: 1.6em;\n padding: 0 var(--office-space-1);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: var(--office-font-size-xs);\n line-height: 1.6;\n text-align: center;\n}\n@media (pointer: coarse) {\n .new,\n .row {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n";
1536
+
1537
+ // src/teams/chat-sidebar.ts
1538
+ function stamp(ts) {
1539
+ if (!ts) return "";
1540
+ return dayLabel(ts) === "Today" ? formatTime(ts) : new Date(ts).toLocaleDateString([], { month: "short", day: "numeric" });
1541
+ }
1542
+ var OfficeUiChatSidebar = class extends TeamsElement {
1543
+ static styles = withStyles(chat_sidebar_default);
1544
+ static properties = {
1545
+ chats: { attribute: false },
1546
+ selectedId: { type: String, attribute: "selected-id" }
1547
+ };
1548
+ constructor() {
1549
+ super();
1550
+ this.chats = [];
1551
+ this.selectedId = "";
1552
+ }
1553
+ render() {
1554
+ return html`
1555
+ <header>
1556
+ <h2>Chat</h2>
1557
+ <button
1558
+ class="new"
1559
+ type="button"
1560
+ title="New chat"
1561
+ aria-label="New chat"
1562
+ @click=${() => this.fire("office-chat-new", {})}
1563
+ >
1564
+ ${icon("plus")}
1565
+ </button>
1566
+ </header>
1567
+ ${this.chats.length === 0 ? html`<p class="empty">No chats yet. Start one with the plus button.</p>` : html`<ul>
1568
+ ${this.chats.map((c) => this.row(c))}
1569
+ </ul>`}
1570
+ `;
1571
+ }
1572
+ row(c) {
1573
+ const other = c.kind === "direct" ? c.members.find((m) => !m.self) : void 0;
1574
+ const preview = c.lastMessage ? c.lastMessage.system ? c.lastMessage.text : c.lastMessage.mine ? `You: ${c.lastMessage.text}` : c.kind === "direct" ? c.lastMessage.text : `${c.lastMessage.authorName}: ${c.lastMessage.text}` : "No messages yet";
1575
+ return html`
1576
+ <li>
1577
+ <button
1578
+ class="row"
1579
+ type="button"
1580
+ aria-current=${c.id === this.selectedId ? "true" : "false"}
1581
+ data-unread=${c.unread ? "true" : "false"}
1582
+ @click=${() => this.fire("office-chat-select", { id: c.id })}
1583
+ >
1584
+ ${other ? html`<office-ui-avatar
1585
+ name=${other.name}
1586
+ seed=${other.id}
1587
+ presence=${other.availability}
1588
+ ></office-ui-avatar>` : html`<span class="group" aria-hidden="true"
1589
+ >${icon(c.meetingId ? "calendar" : "users")}</span
1590
+ >`}
1591
+ <span class="text">
1592
+ <span class="line">
1593
+ <span class="title">${c.title}</span>
1594
+ <time>${stamp(c.ts)}</time>
1595
+ </span>
1596
+ <span class="preview">${preview}</span>
1597
+ </span>
1598
+ ${c.live ? html`<span class="live" title="Call in progress">${icon("video")}</span>` : nothing}
1599
+ ${c.unread ? html`<span class="count" aria-label=${`${c.unread} unread`}
1600
+ >${c.unread > 99 ? "99+" : c.unread}</span
1601
+ >` : nothing}
1602
+ </button>
1603
+ </li>
1604
+ `;
1605
+ }
1606
+ };
1607
+ var defineChatSidebar = definer("office-ui-chat-sidebar", () => OfficeUiChatSidebar);
1608
+
1609
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/people-picker.css?raw
1610
+ var people_picker_default = ":host {\n display: grid;\n gap: var(--office-space-2);\n min-width: 0;\n}\ninput[type='search'] {\n min-width: 0;\n box-sizing: border-box;\n padding: var(--office-space-3);\n font: inherit;\n color: inherit;\n background: var(--office-field-background);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius-md);\n}\n.chips,\n.list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1);\n}\n.chip {\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: var(--office-teams-brand-subtle);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.list {\n max-height: 14em;\n overflow: auto;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n}\n.list label {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-2) var(--office-space-3);\n cursor: pointer;\n}\n.list label:hover {\n background: var(--office-selected);\n}\n.name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.none {\n margin: 0;\n color: var(--office-teams-text-subtle);\n}\n@media (pointer: coarse) {\n .list label,\n .chip {\n min-height: var(--office-touch-target, 44px);\n }\n}\n";
1611
+
1612
+ // src/teams/people-picker.ts
1613
+ var OfficeUiPeoplePicker = class extends TeamsElement {
1614
+ static styles = withStyles(people_picker_default);
1615
+ static properties = {
1616
+ contacts: { attribute: false },
1617
+ selected: { attribute: false },
1618
+ exclude: { attribute: false },
1619
+ label: { type: String },
1620
+ query: { state: true }
1621
+ };
1622
+ constructor() {
1623
+ super();
1624
+ this.contacts = [];
1625
+ this.selected = [];
1626
+ this.exclude = [];
1627
+ this.label = "People";
1628
+ this.query = "";
1629
+ }
1630
+ toggle(id) {
1631
+ const next = this.selected.includes(id) ? this.selected.filter((s) => s !== id) : [...this.selected, id];
1632
+ this.fire("office-people-change", { selected: next });
1633
+ }
1634
+ render() {
1635
+ const q = this.query.trim().toLocaleLowerCase();
1636
+ const offered = this.contacts.filter(
1637
+ (c) => !this.exclude.includes(c.id) && (!q || c.name.toLocaleLowerCase().includes(q))
1638
+ );
1639
+ const chosen = this.contacts.filter((c) => this.selected.includes(c.id));
1640
+ return html`
1641
+ ${chosen.length ? html`<ul class="chips" aria-label="Selected people">
1642
+ ${chosen.map(
1643
+ (c) => html`<li>
1644
+ <button
1645
+ type="button"
1646
+ class="chip"
1647
+ aria-label=${`Remove ${c.name}`}
1648
+ @click=${() => this.toggle(c.id)}
1649
+ >
1650
+ ${c.name} <span aria-hidden="true">×</span>
1651
+ </button>
1652
+ </li>`
1653
+ )}
1654
+ </ul>` : nothing}
1655
+ <input
1656
+ type="search"
1657
+ placeholder="Search people"
1658
+ aria-label=${`Search ${this.label.toLocaleLowerCase()}`}
1659
+ .value=${this.query}
1660
+ @input=${(e) => this.query = e.target.value}
1661
+ />
1662
+ ${offered.length === 0 ? html`<p class="none">
1663
+ ${this.contacts.length === 0 ? "Nobody else has joined this workspace yet." : "No one matches that search."}
1664
+ </p>` : html`<ul class="list" role="group" aria-label=${this.label}>
1665
+ ${offered.map(
1666
+ (c) => html`<li>
1667
+ <label>
1668
+ <input
1669
+ type="checkbox"
1670
+ .checked=${this.selected.includes(c.id)}
1671
+ @change=${() => this.toggle(c.id)}
1672
+ />
1673
+ <office-ui-avatar
1674
+ size="sm"
1675
+ name=${c.name}
1676
+ seed=${c.id}
1677
+ presence=${c.availability}
1678
+ ></office-ui-avatar>
1679
+ <span class="name">${c.name}</span>
1680
+ </label>
1681
+ </li>`
1682
+ )}
1683
+ </ul>`}
1684
+ `;
1685
+ }
1686
+ };
1687
+ var definePeoplePicker = definer("office-ui-people-picker", () => OfficeUiPeoplePicker);
1688
+
833
1689
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/prejoin.css?raw
834
1690
  var prejoin_default = ":host {\n display: grid;\n place-items: center;\n padding: var(--office-space-5);\n background: var(--office-teams-panel);\n}\n.card {\n display: grid;\n grid-template-columns: minmax(0, 3fr) minmax(16em, 2fr);\n width: min(60em, 100%);\n overflow: hidden;\n border-radius: var(--office-radius-lg);\n background: var(--office-background);\n box-shadow: var(--office-shadow-teams);\n}\n.stage {\n position: relative;\n display: grid;\n place-content: center;\n justify-items: center;\n gap: var(--office-space-3);\n min-height: 18em;\n background: var(--office-call-background);\n color: var(--office-call-muted-foreground);\n}\n.stage video {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n}\n.panel {\n display: flex;\n flex-direction: column;\n gap: var(--office-space-3);\n padding: var(--office-space-5);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\n.for {\n margin: 0;\n color: var(--office-teams-text-subtle);\n}\n.switch {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n cursor: pointer;\n}\n.what {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-2);\n}\n.switch input {\n position: absolute;\n inset: 0;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.track {\n position: relative;\n width: 2.6em;\n height: 1.4em;\n border-radius: var(--office-radius-full);\n background: var(--office-border);\n transition: background var(--office-duration-fast);\n}\n.track::after {\n content: '';\n position: absolute;\n inset-block-start: 0.2em;\n inset-inline-start: 0.2em;\n width: 1em;\n height: 1em;\n border-radius: 50%;\n background: var(--office-background);\n transition: transform var(--office-duration-fast);\n}\n.switch input:checked ~ .track {\n background: var(--office-teams-brand);\n}\n.switch input:checked ~ .track::after {\n transform: translateX(1.2em);\n}\n.switch:has(input:focus-visible) {\n outline: var(--office-focus-width) solid var(--office-ring);\n}\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: var(--office-space-2);\n margin-top: auto;\n}\n.actions button {\n padding: var(--office-space-2) var(--office-space-5);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n font-weight: var(--office-font-weight-bold);\n}\n.actions .join {\n border-color: transparent;\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\n.actions .join:hover:not(:disabled) {\n background: var(--office-teams-brand-hover);\n}\n.actions button:disabled {\n opacity: 0.6;\n cursor: wait;\n}\n@media (max-width: 767px) {\n .card {\n grid-template-columns: 1fr;\n }\n}\n";
835
1691
 
@@ -912,13 +1768,15 @@ var OfficeUiPrejoin = class extends TeamsElement {
912
1768
  };
913
1769
  var definePrejoin = definer("office-ui-prejoin", () => OfficeUiPrejoin);
914
1770
  var TeamsController = class {
915
- constructor(host, onNotice) {
1771
+ constructor(host, onNotice, onNotification = () => void 0) {
916
1772
  this.host = host;
917
1773
  this.onNotice = onNotice;
1774
+ this.onNotification = onNotification;
918
1775
  host.addController(this);
919
1776
  }
920
1777
  host;
921
1778
  onNotice;
1779
+ onNotification;
922
1780
  client = null;
923
1781
  state = null;
924
1782
  off = [];
@@ -933,7 +1791,8 @@ var TeamsController = class {
933
1791
  this.state = client.getState();
934
1792
  this.host.requestUpdate();
935
1793
  }),
936
- client.on("notice", this.onNotice)
1794
+ client.on("notice", this.onNotice),
1795
+ client.on("notification", this.onNotification)
937
1796
  ];
938
1797
  this.host.requestUpdate();
939
1798
  }
@@ -1929,7 +2788,7 @@ function defineTeamsFilesPanel() {
1929
2788
  }
1930
2789
 
1931
2790
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/teams-settings.css?raw
1932
- var teams_settings_default = ":host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n width: min(76em, 94vw);\n max-height: 90dvh;\n overflow: hidden;\n padding: 0;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: inherit;\n box-shadow: var(--office-shadow-teams);\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\n:host([embedded]) {\n display: block;\n height: 100%;\n min-height: 0;\n}\n:host([embedded]:not([open])) {\n display: none;\n}\n:host([embedded]) dialog {\n position: relative;\n width: 100%;\n height: 100%;\n max-width: none;\n max-height: none;\n margin: 0;\n border: 0;\n border-radius: 0;\n box-shadow: none;\n}\n:host([embedded]) dialog[open] {\n display: flex;\n flex-direction: column;\n}\n:host([embedded]) .settings-layout {\n flex: 1;\n height: auto;\n}\nform {\n display: grid;\n gap: var(--office-space-3);\n padding: var(--office-space-5);\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-4) var(--office-space-5);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n gap: var(--office-space-3);\n}\nheader input {\n margin-inline-start: auto;\n width: min(24em, 50%);\n min-width: 0;\n}\n.settings-results ul {\n list-style: none;\n padding: 0;\n display: grid;\n gap: var(--office-space-3);\n}\n.settings-results button {\n display: grid;\n gap: var(--office-space-2);\n width: 100%;\n text-align: start;\n}\n.settings-results button:hover {\n background: var(--office-selected);\n}\nh1 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\n.settings-layout {\n display: grid;\n grid-template-columns: 19em minmax(0, 1fr);\n height: min(42em, calc(90dvh - 5em));\n min-height: 0;\n}\nnav {\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: var(--office-space-1);\n padding: var(--office-space-3);\n background: var(--office-teams-panel);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nnav button {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-height: 3em;\n text-align: start;\n border: 0;\n background: transparent;\n}\nnav button:hover {\n background: var(--office-selected);\n}\n.icon {\n width: 1.4em;\n height: 1.4em;\n flex-shrink: 0;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nnav button[aria-selected='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.settings-content {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n}\nsection {\n padding: var(--office-space-5);\n}\nsection h3 {\n margin: var(--office-space-5) 0 var(--office-space-3);\n font-size: var(--office-font-size);\n}\nsection h2 + h3 {\n margin-top: var(--office-space-4);\n}\n[hidden] {\n display: none !important;\n}\n.description {\n color: var(--office-teams-text-subtle);\n line-height: 1.5;\n}\noffice-ui-switch {\n --office-switch-track-on: var(--office-teams-brand);\n}\n.preference {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-block: var(--office-space-3);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n font-weight: normal;\n}\nh2 {\n position: sticky;\n top: 0;\n z-index: 1;\n margin: calc(-1 * var(--office-space-5)) calc(-1 * var(--office-space-5)) var(--office-space-4);\n padding: var(--office-space-5);\n background: var(--office-popover);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n font-size: var(--office-font-size-lg);\n}\nlabel {\n display: grid;\n gap: var(--office-space-1);\n font-weight: var(--office-font-weight-bold);\n}\nsmall {\n color: var(--office-teams-text-subtle);\n font-weight: normal;\n}\ninput,\ntextarea,\nselect {\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n color: inherit;\n font: inherit;\n}\ntextarea {\n min-height: 5.5em;\n font-family: ui-monospace, monospace;\n font-size: 0.9em;\n}\n.issues {\n margin: 0;\n padding-inline-start: 1.2em;\n color: var(--office-danger);\n font-size: var(--office-font-size-sm);\n}\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: var(--office-space-2);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\nbutton.primary {\n border-color: transparent;\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\nbutton.primary:hover {\n background: var(--office-teams-brand-hover);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (max-width: 48em) {\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n min-height: 0;\n }\n nav {\n max-height: 30dvh;\n border-inline-end: 0;\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n }\n}\n";
2791
+ var teams_settings_default = ":host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n width: min(76em, 94vw);\n max-height: 90dvh;\n overflow: hidden;\n padding: 0;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: inherit;\n box-shadow: var(--office-shadow-teams);\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\n:host([embedded]) {\n display: block;\n height: 100%;\n min-height: 0;\n}\n:host([embedded]:not([open])) {\n display: none;\n}\n:host([embedded]) dialog {\n position: relative;\n width: 100%;\n height: 100%;\n max-width: none;\n max-height: none;\n margin: 0;\n border: 0;\n border-radius: 0;\n box-shadow: none;\n}\n:host([embedded]) dialog[open] {\n display: flex;\n flex-direction: column;\n}\n:host([embedded]) .settings-layout {\n flex: 1;\n height: auto;\n}\nform {\n display: grid;\n gap: var(--office-space-3);\n padding: var(--office-space-5);\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-4) var(--office-space-5);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n gap: var(--office-space-3);\n}\nheader input {\n margin-inline-start: auto;\n width: min(24em, 50%);\n min-width: 0;\n}\n.settings-results ul {\n list-style: none;\n padding: 0;\n display: grid;\n gap: var(--office-space-3);\n}\n.settings-results button {\n display: grid;\n gap: var(--office-space-2);\n width: 100%;\n text-align: start;\n}\n.settings-results button:hover {\n background: var(--office-selected);\n}\nh1 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\n.settings-layout {\n display: grid;\n grid-template-columns: 19em minmax(0, 1fr);\n height: min(42em, calc(90dvh - 5em));\n min-height: 0;\n}\nnav {\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: var(--office-space-1);\n padding: var(--office-space-3);\n background: var(--office-teams-panel);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nnav button {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-height: 3em;\n text-align: start;\n border: 0;\n background: transparent;\n}\nnav button:hover {\n background: var(--office-selected);\n}\n.icon {\n width: 1.4em;\n height: 1.4em;\n flex-shrink: 0;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nnav button[aria-selected='true'] {\n background: var(--office-teams-brand-subtle);\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.settings-content {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n}\nsection {\n padding: var(--office-space-5);\n}\nsection h3 {\n margin: var(--office-space-5) 0 var(--office-space-3);\n font-size: var(--office-font-size);\n}\nsection h2 + h3 {\n margin-top: var(--office-space-4);\n}\n[hidden] {\n display: none !important;\n}\n.description {\n color: var(--office-teams-text-subtle);\n line-height: 1.5;\n}\noffice-ui-switch {\n --office-switch-track-on: var(--office-teams-brand);\n}\n.preference {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-block: var(--office-space-3);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n font-weight: normal;\n}\nh2 {\n position: sticky;\n top: 0;\n z-index: 1;\n margin: calc(-1 * var(--office-space-5)) calc(-1 * var(--office-space-5)) var(--office-space-4);\n padding: var(--office-space-5);\n background: var(--office-popover);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n font-size: var(--office-font-size-lg);\n}\nlabel {\n display: grid;\n gap: var(--office-space-1);\n font-weight: var(--office-font-weight-bold);\n}\nsmall {\n color: var(--office-teams-text-subtle);\n font-weight: normal;\n}\ninput,\ntextarea,\nselect {\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n color: inherit;\n font: inherit;\n}\ntextarea {\n min-height: 5.5em;\n font-family: ui-monospace, monospace;\n font-size: 0.9em;\n}\n.issues {\n margin: 0;\n padding-inline-start: 1.2em;\n color: var(--office-danger);\n font-size: var(--office-font-size-sm);\n}\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: var(--office-space-2);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\nbutton.primary {\n border-color: transparent;\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n}\nbutton.primary:hover {\n background: var(--office-teams-brand-hover);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (max-width: 48em) {\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n min-height: 0;\n }\n nav {\n max-height: 30dvh;\n border-inline-end: 0;\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n }\n}\n\n.notification-level {\n display: grid;\n gap: var(--office-space-2);\n margin: var(--office-space-3) 0 0;\n padding: 0;\n border: 0;\n}\n.notification-level legend {\n margin-bottom: var(--office-space-2);\n padding: 0;\n font-weight: var(--office-font-weight-bold);\n}\n.notification-choice {\n display: flex;\n align-items: flex-start;\n gap: var(--office-space-3);\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n cursor: pointer;\n font-weight: normal;\n}\n.notification-choice:has(input:checked) {\n border-color: var(--office-teams-brand);\n background: var(--office-teams-brand-subtle);\n}\n.notification-choice input {\n margin-block-start: var(--office-space-1);\n accent-color: var(--office-teams-brand);\n}\n.notification-choice span {\n display: grid;\n gap: var(--office-space-1);\n}\n.notification-choice small {\n color: var(--office-teams-text-subtle);\n}\n";
1933
2792
 
1934
2793
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/settings-previews.css?raw
1935
2794
  var settings_previews_default = ".theme-options,\n.density-options {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-3);\n}\n.theme-options button {\n display: grid;\n gap: var(--office-space-3);\n padding: var(--office-space-2);\n text-align: start;\n min-width: 9em;\n}\n.theme-options button[aria-pressed='true'],\n.density-choice:has(input:checked) {\n border-color: var(--office-teams-brand);\n box-shadow: inset 0 0 0 var(--office-border-width) var(--office-teams-brand);\n}\n.theme-swatch {\n --preview-paper: var(--office-call-foreground);\n --preview-ink: var(--office-call-background);\n display: grid;\n grid-template-columns: 0.7em 2.4em 1fr;\n grid-template-rows: 0.7em 1fr;\n width: 9em;\n height: 5.5em;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius);\n overflow: hidden;\n background: var(--preview-paper);\n}\n.theme-swatch[data-theme='dark'] {\n --preview-paper: var(--office-call-background);\n --preview-ink: var(--office-call-foreground);\n}\n.theme-swatch[data-theme='system'] {\n background: linear-gradient(\n 90deg,\n var(--office-call-foreground) 50%,\n var(--office-call-background) 50%\n );\n}\n.preview-toolbar {\n grid-column: 1 / -1;\n background: var(--office-teams-brand);\n}\n.preview-rail,\n.preview-sidebar {\n background: color-mix(in srgb, var(--preview-ink) 8%, var(--preview-paper));\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.preview-sidebar,\n.preview-conversation {\n display: flex;\n flex-direction: column;\n gap: var(--office-space-2);\n padding: var(--office-space-2);\n}\n.preview-sidebar i,\n.preview-conversation i {\n height: 0.4em;\n border-radius: var(--office-radius);\n background: color-mix(in srgb, var(--preview-ink) 25%, var(--preview-paper));\n}\n.preview-conversation i {\n height: 0.8em;\n width: 80%;\n}\n.preview-conversation i:nth-child(2) {\n align-self: flex-end;\n background: var(--office-teams-brand-subtle);\n}\n.density-options {\n margin: var(--office-space-5) 0 0;\n padding: var(--office-space-5) 0 0;\n border: 0;\n border-top: var(--office-border-width) solid var(--office-teams-divider);\n}\n.density-options legend {\n float: inline-start;\n width: 100%;\n font-weight: var(--office-font-weight-bold);\n margin-bottom: var(--office-space-3);\n}\n.density-choice {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: var(--office-space-3);\n width: 13em;\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n cursor: pointer;\n font-weight: normal;\n}\n.density-choice input {\n position: absolute;\n inset-inline-end: var(--office-space-2);\n inset-block-end: var(--office-space-2);\n accent-color: var(--office-teams-brand);\n}\n.density-caption {\n display: grid;\n gap: var(--office-space-1);\n padding-inline-end: var(--office-space-5);\n}\n.density-preview {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: var(--office-space-3);\n height: 7em;\n padding: var(--office-space-3);\n border-radius: var(--office-radius);\n background: var(--office-teams-panel);\n}\n.preview-message {\n display: flex;\n gap: var(--office-space-2);\n align-items: flex-start;\n}\n.preview-message > i {\n width: 1.1em;\n height: 1.1em;\n border-radius: 50%;\n background: var(--office-teams-brand-subtle);\n border: var(--office-border-width) solid var(--office-teams-brand);\n}\n.preview-message > span {\n display: grid;\n gap: var(--office-space-1);\n flex: 1;\n}\n.preview-message b {\n height: 0.3em;\n width: 75%;\n background: var(--office-teams-text-subtle);\n border-radius: var(--office-radius);\n}\n.preview-message b + b {\n width: 100%;\n opacity: 0.4;\n}\n.density-preview[data-density='compact'] {\n gap: var(--office-space-1);\n}\n.density-preview[data-density='compact'] .preview-message > i {\n width: 0.7em;\n height: 0.7em;\n}\n.density-preview[data-density='compact'] .preview-message > span {\n display: flex;\n align-items: center;\n}\n@media (forced-colors: active) {\n .theme-options button[aria-pressed='true'],\n .density-choice:has(input:checked) {\n outline: var(--office-focus-width) solid Highlight;\n }\n}\n";
@@ -1998,6 +2857,53 @@ function densityChoices(density, change) {
1998
2857
  )}
1999
2858
  </fieldset>`;
2000
2859
  }
2860
+ var LEVELS = [
2861
+ ["all", "All activity", "Mentions, replies, reactions and pinned messages"],
2862
+ ["mentions", "Mentions only", "Only when someone mentions you"],
2863
+ ["off", "Off", "Activity still collects in the Activity feed"]
2864
+ ];
2865
+ var PERMISSION_TEXT = {
2866
+ granted: "Desktop notifications are on for this browser. They appear when this tab is in the background.",
2867
+ default: "Desktop notifications appear when this tab is in the background. Your browser asks first.",
2868
+ denied: "Desktop notifications are blocked for this site. Allow them in your browser settings to turn them on.",
2869
+ unsupported: "This browser does not support desktop notifications."
2870
+ };
2871
+ var desktopPermission = () => globalThis.Notification?.permission ?? "unsupported";
2872
+ function notificationChoices(settings, permission, handlers) {
2873
+ return html`<fieldset class="notification-level">
2874
+ <legend>Notify me about</legend>
2875
+ ${LEVELS.map(
2876
+ ([level, label, description]) => html`<label class="notification-choice">
2877
+ <input
2878
+ type="radio"
2879
+ name="notification-level"
2880
+ value=${level}
2881
+ .checked=${settings.level === level}
2882
+ @change=${() => handlers.change({ level })}
2883
+ />
2884
+ <span><strong>${label}</strong><small>${description}</small></span>
2885
+ </label>`
2886
+ )}
2887
+ </fieldset>
2888
+ <h3>Sound</h3>
2889
+ <label class="preference"
2890
+ ><span>Play a sound for new notifications</span
2891
+ ><office-ui-switch
2892
+ aria-label="Notification sound"
2893
+ .checked=${settings.sound}
2894
+ @change=${(event) => handlers.change({ sound: event.target.checked })}
2895
+ ></office-ui-switch
2896
+ ></label>
2897
+ <h3>Desktop notifications</h3>
2898
+ <p class="description" role="status">${PERMISSION_TEXT[permission]}</p>
2899
+ ${permission === "default" ? html`<button type="button" class="primary" @click=${handlers.requestPermission}>
2900
+ Enable desktop notifications
2901
+ </button>` : ""}
2902
+ <p class="description">
2903
+ Do not disturb silences every notification. Email and mobile push notifications are not
2904
+ available.
2905
+ </p>`;
2906
+ }
2001
2907
  var SETTINGS_CATEGORIES = [
2002
2908
  ["general", "General"],
2003
2909
  ["appearance", "Appearance and accessibility"],
@@ -2022,6 +2928,21 @@ var TOPICS = [
2022
2928
  title: "Show app names",
2023
2929
  description: "Display labels beneath app bar icons"
2024
2930
  },
2931
+ {
2932
+ category: "notifications",
2933
+ title: "Activity notifications",
2934
+ description: "Notify for all activity, mentions only or nothing"
2935
+ },
2936
+ {
2937
+ category: "notifications",
2938
+ title: "Sound",
2939
+ description: "Play a sound for new notifications"
2940
+ },
2941
+ {
2942
+ category: "notifications",
2943
+ title: "Desktop notifications",
2944
+ description: "Browser notifications while this tab is in the background, and Do not disturb"
2945
+ },
2025
2946
  {
2026
2947
  category: "notifications",
2027
2948
  title: "Followed threads",
@@ -2075,6 +2996,7 @@ var TeamsSettings = class extends LitElement {
2075
2996
  category: { state: true },
2076
2997
  theme: { attribute: false },
2077
2998
  followSettings: { attribute: false },
2999
+ notificationSettings: { attribute: false },
2078
3000
  userName: { attribute: false },
2079
3001
  fileOpenPreference: { attribute: false },
2080
3002
  chatDensity: { attribute: false },
@@ -2090,6 +3012,7 @@ var TeamsSettings = class extends LitElement {
2090
3012
  this.category = "general";
2091
3013
  this.theme = "system";
2092
3014
  this.followSettings = { started: true, replied: true };
3015
+ this.notificationSettings = { level: "all", sound: true };
2093
3016
  this.userName = "";
2094
3017
  this.fileOpenPreference = "teams";
2095
3018
  this.chatDensity = "comfy";
@@ -2279,12 +3202,22 @@ var TeamsSettings = class extends LitElement {
2279
3202
  ?hidden=${!!this.query.trim() || this.category !== "notifications"}
2280
3203
  >
2281
3204
  <h2>Notifications and activity</h2>
3205
+ <h3>Activity notifications</h3>
3206
+ ${notificationChoices(this.notificationSettings, desktopPermission(), {
3207
+ change: (detail) => this.dispatchEvent(
3208
+ new CustomEvent("teams-settings-notifications", {
3209
+ detail,
3210
+ bubbles: true,
3211
+ composed: true
3212
+ })
3213
+ ),
3214
+ requestPermission: () => void globalThis.Notification?.requestPermission().then(
3215
+ () => this.requestUpdate()
3216
+ )
3217
+ })}
2282
3218
  <h3>Followed threads</h3>
2283
3219
  <p class="description">Choose which conversations appear in Followed threads.</p>
2284
3220
  ${["started", "replied"].map((key) => html`<label class="preference"><span>${key === "started" ? "Threads I start" : "Threads I reply to"}</span><office-ui-switch aria-label=${key === "started" ? "Threads I start" : "Threads I reply to"} .checked=${this.followSettings[key]} @change=${(event) => this.dispatchEvent(new CustomEvent("teams-settings-follow", { detail: { [key]: event.target.checked }, bubbles: true, composed: true }))}></office-ui-switch></label>`)}
2285
- <p class="description">
2286
- Desktop, email and activity notifications are not available yet.
2287
- </p>
2288
3221
  </section>
2289
3222
  <section
2290
3223
  id="panel-files"
@@ -2439,8 +3372,8 @@ function loadConfig() {
2439
3372
  var saveConfig = (config) => safeStorage.setItem("teams:config", JSON.stringify(config));
2440
3373
 
2441
3374
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/teams-app.css?raw
2442
- var teams_app_default = "/* <teams-app>: the shell. Colours, spacing and radii come from the ooxml-ui tokens, so a host\n retheme or dark mode reaches the whole app. The elements inside bring their own styles. */\n:host {\n display: block;\n height: 100%;\n min-height: 28em;\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n background: var(--office-background);\n}\n:host([data-office-theme='dark']) {\n --office-background: var(--office-teams-panel);\n --office-surface: var(--office-teams-bubble);\n --office-popover: var(--office-teams-panel);\n}\n:host([data-chat-density='compact']) {\n --office-teams-message-gap: var(--office-space-1);\n --office-teams-message-padding: var(--office-space-1) var(--office-space-2);\n}\n@media (prefers-color-scheme: dark) {\n :host(:not([data-office-theme='light'])) {\n --office-background: var(--office-teams-panel);\n --office-surface: var(--office-teams-bubble);\n --office-popover: var(--office-teams-panel);\n }\n}\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\nbutton {\n font: inherit;\n color: inherit;\n cursor: pointer;\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n\n.shell {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr);\n height: 100%;\n}\n\n/* ---- top bar ---------------------------------------------------------------------------- */\n.topbar {\n display: grid;\n grid-template-columns: 16em minmax(0, 1fr) auto;\n align-items: center;\n gap: var(--office-space-4);\n padding: var(--office-space-2) var(--office-space-4);\n background: var(--office-teams-rail);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n}\n.navigation-toggle {\n display: none;\n padding: var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n}\n.navigation-toggle:hover {\n background: var(--office-selected);\n}\n.navigation-toggle svg {\n width: 1.3em;\n height: 1.3em;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n}\n.brand {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n font-size: var(--office-font-size-lg);\n font-weight: var(--office-font-weight-bold);\n}\n.logo {\n display: inline-grid;\n place-items: center;\n width: 1.8em;\n height: 1.8em;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: 0.85em;\n}\n.search {\n position: relative;\n justify-self: center;\n width: min(34em, 100%);\n}\n.search input {\n width: 100%;\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n}\n.results {\n position: absolute;\n inset-inline: 0;\n inset-block-start: calc(100% + var(--office-space-1));\n z-index: 5;\n max-height: 22em;\n margin: 0;\n padding: var(--office-space-1);\n overflow: auto;\n list-style: none;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.results button {\n display: grid;\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n text-align: start;\n}\n.results button:hover {\n background: var(--office-selected);\n}\n.results .in,\n.results .none {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.results .none {\n padding: var(--office-space-3);\n}\n.snippet {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.me {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n}\n.me office-ui-menu-button::part(caret) {\n display: none;\n}\n.channel-head office-ui-menu-button::part(caret) {\n display: none;\n}\n.channel-head svg {\n width: 1em;\n height: 1em;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n/* ---- body ------------------------------------------------------------------------------- */\n.body {\n display: grid;\n grid-template-columns: auto 19em minmax(0, 1fr);\n min-height: 0;\n grid-template-rows: minmax(0, 1fr);\n}\n.body > office-ui-app-rail {\n grid-column: 1;\n grid-row: 1;\n}\n.body > .side {\n grid-column: 2;\n grid-row: 1;\n}\n.body > .main {\n grid-column: 3;\n grid-row: 1;\n}\n.body > teams-settings {\n grid-column: 2 / -1;\n grid-row: 1;\n min-width: 0;\n min-height: 0;\n z-index: 1;\n}\n.body > teams-settings:not([open]) {\n display: none;\n}\n.body > [inert] {\n visibility: hidden;\n}\n.side {\n min-height: 0;\n overflow: auto;\n background: var(--office-teams-panel);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.side h2 {\n margin: 0;\n padding: var(--office-space-3) var(--office-space-4);\n font-size: var(--office-font-size-lg);\n}\n.main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n}\n.popout-view {\n display: flex;\n height: 100%;\n min-height: 0;\n}\n.popout-view teams-content-preview {\n flex: 1;\n}\n.main office-ui-chat-list,\n.main office-ui-prejoin {\n flex: 1;\n}\n\nul.plain {\n margin: 0;\n padding: 0 var(--office-space-3);\n list-style: none;\n}\nul.plain li {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n padding: var(--office-space-2) var(--office-space-2);\n}\nul.plain li > :first-child:not(office-ui-avatar) {\n flex: 1;\n}\n.hint {\n padding: 0 var(--office-space-4);\n color: var(--office-teams-text-subtle);\n}\n.join {\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n.join.live {\n border-color: transparent;\n background: var(--office-call-live);\n color: var(--office-call-foreground);\n}\n\n/* ---- conversation ----------------------------------------------------------------------- */\n.channel-head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-4);\n padding: var(--office-space-2) var(--office-space-5);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n}\n.channel-head > button:not(.meet),\n.thread-head button {\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n.channel-head > button:hover:not(:disabled),\n.thread-head button:hover {\n background: var(--office-selected);\n}\n.channel-head > button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.channel-head h1 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n white-space: nowrap;\n}\n.hash {\n color: var(--office-teams-text-subtle);\n}\n.tabs {\n display: flex;\n gap: var(--office-space-1);\n}\n.tabs button {\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-bottom: var(--office-border-width-thick) solid transparent;\n background: transparent;\n}\n.tabs button[aria-selected='true'] {\n border-bottom-color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.topic {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n color: var(--office-teams-text-subtle);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.people {\n display: flex;\n align-items: center;\n gap: var(--office-space-0);\n}\n.more {\n margin-inline-start: var(--office-space-1);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.meet {\n padding: var(--office-space-2) var(--office-space-4);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.meet:hover:not(:disabled) {\n background: var(--office-teams-brand-hover);\n}\n.meet:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.callbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-2) var(--office-space-5);\n background: var(--office-call-live);\n color: var(--office-call-foreground);\n}\n.callbar button {\n padding: 0 var(--office-space-3);\n border: var(--office-border-width) solid currentColor;\n border-radius: var(--office-radius);\n background: transparent;\n}\n\n/* ---- files ------------------------------------------------------------------------------ */\n/* ---- meeting ---------------------------------------------------------------------------- */\n.meeting {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr);\n height: 100%;\n background: var(--office-call-background);\n}\n.stage {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n min-height: 0;\n}\n.panel {\n display: flex;\n flex-direction: column;\n width: 22em;\n min-height: 0;\n background: var(--office-background);\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n}\n.panel office-ui-chat-list {\n flex: 1;\n padding-inline: var(--office-space-3);\n}\n.panel office-ui-chat-composer {\n padding-inline: var(--office-space-3);\n}\n.people-panel h2 {\n margin: 0;\n padding: var(--office-space-3) var(--office-space-4);\n font-size: var(--office-font-size-lg);\n}\n\n/* ---- welcome and toast ------------------------------------------------------------------ */\n.welcome {\n display: grid;\n place-items: center;\n height: 100%;\n background: var(--office-teams-panel);\n}\n.welcome form {\n display: grid;\n justify-items: center;\n gap: var(--office-space-3);\n width: min(24em, 90%);\n padding: var(--office-space-6);\n border-radius: var(--office-radius-lg);\n background: var(--office-background);\n box-shadow: var(--office-shadow-teams);\n}\n.welcome h1 {\n margin: 0;\n font-size: var(--office-font-size-xl);\n}\n.welcome p {\n margin: 0;\n color: var(--office-teams-text-subtle);\n}\n.welcome input {\n width: 100%;\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n color: inherit;\n font: inherit;\n}\n.welcome button {\n width: 100%;\n padding: var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.toast {\n position: fixed;\n inset-block-end: var(--office-space-4);\n inset-inline-start: 50%;\n z-index: 10;\n transform: translateX(-50%);\n padding: var(--office-space-2) var(--office-space-4);\n border-radius: var(--office-radius-md);\n background: var(--office-call-background);\n color: var(--office-call-foreground);\n}\n\n.conversation-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n flex: 1;\n min-height: 0;\n}\n.conversation-grid[data-thread='true'] {\n grid-template-columns: minmax(0, 1fr) minmax(18em, 0.8fr);\n}\n.conversation-main,\n.thread-pane {\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n}\n.tab-conversation teams-channel-tab {\n flex: 1;\n min-height: 0;\n}\n.thread-pane {\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n}\n.thread-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-3);\n}\n.thread-head h2 {\n margin: 0;\n font-size: 1em;\n}\n.followed-threads {\n overflow: auto;\n padding: var(--office-space-4);\n}\n.message-transfer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-3);\n background: var(--office-teams-panel);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.message-transfer span {\n flex: 1;\n overflow-wrap: anywhere;\n}\n.message-transfer button {\n font: inherit;\n color: inherit;\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2) var(--office-space-3);\n cursor: pointer;\n}\n.followed-threads ul {\n list-style: none;\n padding: 0;\n}\n.followed-threads fieldset {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-4);\n padding: var(--office-space-3);\n margin-block: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n}\n.followed-threads li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-3);\n padding-block: var(--office-space-3);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.followed-threads button {\n font: inherit;\n color: inherit;\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2) var(--office-space-3);\n cursor: pointer;\n}\n.followed-threads button:hover {\n background: var(--office-selected);\n}\n.followed-threads li button:first-child {\n display: grid;\n gap: var(--office-space-2);\n flex: 1;\n text-align: start;\n min-width: 0;\n overflow-wrap: anywhere;\n border-color: transparent;\n}\n.followed-threads li[data-unread='true'] button:first-child span {\n font-weight: var(--office-font-weight-bold);\n}\n@media (max-width: 56em) {\n .conversation-grid[data-thread='true'] {\n grid-template-columns: minmax(0, 1fr);\n }\n .conversation-grid[data-thread='true'] .conversation-main {\n display: none;\n }\n}\n.tabs {\n overflow-x: auto;\n min-width: 0;\n flex: 1 1 16em;\n}\n\n@media (max-width: 56.25em) {\n .navigation-toggle {\n display: inline-flex;\n }\n .brand > .logo,\n .brand > span:last-child {\n display: none;\n }\n .channel-head .topic,\n .channel-head .people {\n display: none;\n }\n .channel-head .tabs {\n flex-basis: 100%;\n }\n .body {\n grid-template-columns: auto minmax(0, 1fr);\n }\n .body > .main {\n grid-column: 2;\n }\n .side {\n display: none;\n }\n .topbar {\n grid-template-columns: auto minmax(0, 1fr) auto;\n }\n}\n\n/* An application host can own the single suite-level account control. */\n:host([host-profile]) teams-profile-menu {\n display: none;\n}\n";
2443
- function threadPane(state, client, openFile, close) {
3375
+ var teams_app_default = "/* <teams-app>: the shell. Colours, spacing and radii come from the ooxml-ui tokens, so a host\n retheme or dark mode reaches the whole app. The elements inside bring their own styles. */\n:host {\n display: block;\n height: 100%;\n min-height: 28em;\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n background: var(--office-background);\n}\n:host([data-office-theme='dark']) {\n --office-background: var(--office-teams-panel);\n --office-surface: var(--office-teams-bubble);\n --office-popover: var(--office-teams-panel);\n}\n:host([data-chat-density='compact']) {\n --office-teams-message-gap: var(--office-space-1);\n --office-teams-message-padding: var(--office-space-1) var(--office-space-2);\n}\n@media (prefers-color-scheme: dark) {\n :host(:not([data-office-theme='light'])) {\n --office-background: var(--office-teams-panel);\n --office-surface: var(--office-teams-bubble);\n --office-popover: var(--office-teams-panel);\n }\n}\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\nbutton {\n font: inherit;\n color: inherit;\n cursor: pointer;\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n\n.shell {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr);\n height: 100%;\n}\n\n/* ---- top bar ---------------------------------------------------------------------------- */\n.topbar {\n display: grid;\n grid-template-columns: 16em minmax(0, 1fr) auto;\n align-items: center;\n gap: var(--office-space-4);\n padding: var(--office-space-2) var(--office-space-4);\n background: var(--office-teams-rail);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n}\n.brand {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n font-size: var(--office-font-size-lg);\n font-weight: var(--office-font-weight-bold);\n}\n.logo {\n display: inline-grid;\n place-items: center;\n width: 1.8em;\n height: 1.8em;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-size: 0.85em;\n}\n.search {\n position: relative;\n justify-self: center;\n width: min(34em, 100%);\n}\n.search input {\n width: 100%;\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-full);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n}\n.results {\n position: absolute;\n inset-inline: 0;\n inset-block-start: calc(100% + var(--office-space-1));\n z-index: 5;\n max-height: 22em;\n margin: 0;\n padding: var(--office-space-1);\n overflow: auto;\n list-style: none;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\n.results button {\n display: grid;\n width: 100%;\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n text-align: start;\n}\n.results button:hover {\n background: var(--office-selected);\n}\n.results .in,\n.results .none {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.results .none {\n padding: var(--office-space-3);\n}\n.snippet {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.me {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n}\n.me office-ui-menu-button::part(caret) {\n display: none;\n}\n.channel-head office-ui-menu-button::part(caret) {\n display: none;\n}\n.channel-head svg {\n width: 1em;\n height: 1em;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n/* ---- body ------------------------------------------------------------------------------- */\n.body {\n display: grid;\n grid-template-columns: auto 19em minmax(0, 1fr);\n min-height: 0;\n grid-template-rows: minmax(0, 1fr);\n}\n.body > office-ui-app-rail {\n grid-column: 1;\n grid-row: 1;\n}\n.body > .side {\n grid-column: 2;\n grid-row: 1;\n}\n.body > .main {\n grid-column: 3;\n grid-row: 1;\n}\n.body > teams-settings {\n grid-column: 2 / -1;\n grid-row: 1;\n min-width: 0;\n min-height: 0;\n z-index: 1;\n}\n.body > teams-settings:not([open]) {\n display: none;\n}\n.body > [inert] {\n visibility: hidden;\n}\n.side {\n min-height: 0;\n overflow: auto;\n background: var(--office-teams-panel);\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.side h2 {\n margin: 0;\n padding: var(--office-space-3) var(--office-space-4);\n font-size: var(--office-font-size-lg);\n}\n.main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n}\n.popout-view {\n display: flex;\n height: 100%;\n min-height: 0;\n}\n.popout-view teams-content-preview {\n flex: 1;\n}\n.main office-ui-chat-list,\n.main office-ui-prejoin {\n flex: 1;\n}\n\nul.plain {\n margin: 0;\n padding: 0 var(--office-space-3);\n list-style: none;\n}\nul.plain li {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n padding: var(--office-space-2) var(--office-space-2);\n}\nul.plain li > :first-child:not(office-ui-avatar) {\n flex: 1;\n}\n.hint {\n padding: 0 var(--office-space-4);\n color: var(--office-teams-text-subtle);\n}\n.join {\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n.join.live {\n border-color: transparent;\n background: var(--office-call-live);\n color: var(--office-call-foreground);\n}\n\n/* ---- conversation ----------------------------------------------------------------------- */\n.channel-head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-4);\n padding: var(--office-space-2) var(--office-space-5);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n}\n.channel-head > button:not(.meet),\n.thread-head button {\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n.channel-head > button:hover:not(:disabled),\n.thread-head button:hover {\n background: var(--office-selected);\n}\n.channel-head > button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.channel-head h1 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n white-space: nowrap;\n}\n.hash {\n color: var(--office-teams-text-subtle);\n}\n.tabs {\n display: flex;\n gap: var(--office-space-1);\n}\n.tabs button {\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-bottom: var(--office-border-width-thick) solid transparent;\n background: transparent;\n}\n.tabs button[aria-selected='true'] {\n border-bottom-color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\n.topic {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n color: var(--office-teams-text-subtle);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.people {\n display: flex;\n align-items: center;\n gap: var(--office-space-0);\n}\n.more {\n margin-inline-start: var(--office-space-1);\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n}\n.meet {\n padding: var(--office-space-2) var(--office-space-4);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.meet:hover:not(:disabled) {\n background: var(--office-teams-brand-hover);\n}\n.meet:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.callbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--office-space-2) var(--office-space-5);\n background: var(--office-call-live);\n color: var(--office-call-foreground);\n}\n.callbar button {\n padding: 0 var(--office-space-3);\n border: var(--office-border-width) solid currentColor;\n border-radius: var(--office-radius);\n background: transparent;\n}\n\n/* ---- files ------------------------------------------------------------------------------ */\n/* ---- meeting ---------------------------------------------------------------------------- */\n.meeting {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr);\n height: 100%;\n background: var(--office-call-background);\n}\n.stage {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n min-height: 0;\n}\n.panel {\n display: flex;\n flex-direction: column;\n width: 22em;\n min-height: 0;\n background: var(--office-background);\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n}\n.panel office-ui-chat-list {\n flex: 1;\n padding-inline: var(--office-space-3);\n}\n.panel office-ui-chat-composer {\n padding-inline: var(--office-space-3);\n}\n.people-panel h2 {\n margin: 0;\n padding: var(--office-space-3) var(--office-space-4);\n font-size: var(--office-font-size-lg);\n}\n\n/* ---- welcome and toast ------------------------------------------------------------------ */\n.welcome {\n display: grid;\n place-items: center;\n height: 100%;\n background: var(--office-teams-panel);\n}\n.welcome form {\n display: grid;\n justify-items: center;\n gap: var(--office-space-3);\n width: min(24em, 90%);\n padding: var(--office-space-6);\n border-radius: var(--office-radius-lg);\n background: var(--office-background);\n box-shadow: var(--office-shadow-teams);\n}\n.welcome h1 {\n margin: 0;\n font-size: var(--office-font-size-xl);\n}\n.welcome p {\n margin: 0;\n color: var(--office-teams-text-subtle);\n}\n.welcome input {\n width: 100%;\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n color: inherit;\n font: inherit;\n}\n.welcome button {\n width: 100%;\n padding: var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font-weight: var(--office-font-weight-bold);\n}\n.toast {\n position: fixed;\n inset-block-end: var(--office-space-4);\n inset-inline-start: 50%;\n z-index: 10;\n transform: translateX(-50%);\n padding: var(--office-space-2) var(--office-space-4);\n border-radius: var(--office-radius-md);\n background: var(--office-call-background);\n color: var(--office-call-foreground);\n}\n\n.conversation-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n flex: 1;\n min-height: 0;\n}\n.conversation-grid[data-thread='true'] {\n grid-template-columns: minmax(0, 1fr) minmax(18em, 0.8fr);\n}\n.conversation-main,\n.thread-pane {\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n}\n.tab-conversation teams-channel-tab {\n flex: 1;\n min-height: 0;\n}\n.thread-pane {\n border-inline-start: var(--office-border-width) solid var(--office-teams-divider);\n}\n.thread-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-3);\n}\n.thread-head h2 {\n margin: 0;\n font-size: 1em;\n}\n.followed-threads {\n overflow: auto;\n padding: var(--office-space-4);\n}\n.message-transfer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-3);\n background: var(--office-teams-panel);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.message-transfer span {\n flex: 1;\n overflow-wrap: anywhere;\n}\n.message-transfer button {\n font: inherit;\n color: inherit;\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2) var(--office-space-3);\n cursor: pointer;\n}\n.followed-threads ul {\n list-style: none;\n padding: 0;\n}\n.followed-threads fieldset {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-4);\n padding: var(--office-space-3);\n margin-block: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n}\n.followed-threads li {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--office-space-3);\n padding-block: var(--office-space-3);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\n.followed-threads button {\n font: inherit;\n color: inherit;\n background: var(--office-background);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n padding: var(--office-space-2) var(--office-space-3);\n cursor: pointer;\n}\n.followed-threads button:hover {\n background: var(--office-selected);\n}\n.followed-threads li button:first-child {\n display: grid;\n gap: var(--office-space-2);\n flex: 1;\n text-align: start;\n min-width: 0;\n overflow-wrap: anywhere;\n border-color: transparent;\n}\n.followed-threads li[data-unread='true'] button:first-child span {\n font-weight: var(--office-font-weight-bold);\n}\n@media (max-width: 56em) {\n .conversation-grid[data-thread='true'] {\n grid-template-columns: minmax(0, 1fr);\n }\n .conversation-grid[data-thread='true'] .conversation-main {\n display: none;\n }\n}\n.tabs {\n overflow-x: auto;\n min-width: 0;\n flex: 1 1 16em;\n}\n\n.channel-head > .back {\n display: none;\n}\n\n/* ---- compact shell ---------------------------------------------------------------------- */\n/* Below 56em the app rail is a bottom bar, the channel list is its own full-screen view and the\n conversation (or any other content) fills the width with a back button to the list. */\n@media (max-width: 56em) {\n .brand {\n display: none;\n }\n .topbar {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: var(--office-space-2);\n }\n .search {\n width: 100%;\n }\n .channel-head {\n gap: var(--office-space-2);\n padding-inline: var(--office-space-3);\n }\n .channel-head > .back {\n display: inline-flex;\n border-color: transparent;\n }\n .channel-head .topic,\n .channel-head .people {\n display: none;\n }\n .channel-head .tabs {\n flex-basis: 100%;\n order: 1;\n }\n .body {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(0, 1fr) auto;\n }\n .body > office-ui-app-rail {\n grid-column: 1;\n grid-row: 2;\n }\n .body > .side,\n .body > .main,\n .body > teams-settings {\n grid-column: 1;\n grid-row: 1;\n }\n .side {\n border-inline-end: 0;\n }\n .shell[data-mobile-view='list'] .main,\n .shell[data-mobile-view='content'] .side {\n display: none;\n }\n}\n/* Phone landscape has little height: keep the chrome to one thin row each. */\n@media (max-width: 56em) and (max-height: 30em) {\n .topbar {\n padding-block: var(--office-space-1);\n }\n .channel-head {\n flex-wrap: nowrap;\n padding-block: var(--office-space-1);\n }\n .channel-head .tabs {\n flex: 1 1 0;\n order: 0;\n }\n .channel-head h1 {\n font-size: var(--office-font-size);\n }\n}\n/* Touch: every control in the shell reaches the minimum hit target. */\n@media (pointer: coarse) {\n .channel-head > button,\n .meet,\n .tabs button,\n .join,\n .thread-head button,\n .callbar button {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n\n/* An application host can own the single suite-level account control. */\n:host([host-profile]) teams-profile-menu {\n display: none;\n}\n\n/* ---- activity ---------------------------------------------------------------------------- */\n.side > office-ui-activity-feed {\n height: 100%;\n}\n/* Toasts for new activity stack at the top right, under the header, so they never cover the\n compose box; on phones they are full-width banners below the header. */\n.activity-toasts {\n position: fixed;\n inset-block-start: 8em;\n inset-inline-end: var(--office-space-4);\n z-index: 10;\n display: flex;\n flex-direction: column;\n gap: var(--office-space-2);\n width: min(28em, calc(100% - var(--office-space-5)));\n pointer-events: none;\n}\n.activity-toast {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-2) var(--office-space-3) var(--office-space-2) var(--office-space-4);\n border-radius: var(--office-radius-md);\n background: var(--office-call-background);\n color: var(--office-call-foreground);\n box-shadow: var(--office-shadow-teams);\n pointer-events: auto;\n}\n@media (max-width: 40em) {\n .activity-toasts {\n inset-block-start: 4.6em;\n inset-inline: var(--office-space-2);\n width: auto;\n }\n}\n.activity-toast .toast-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n}\n.activity-toast .toast-text > * {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.activity-toast .toast-preview {\n opacity: 0.8;\n font-size: var(--office-font-size-sm);\n}\n.activity-toast button {\n flex: none;\n padding: var(--office-space-1) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: inherit;\n font-weight: var(--office-font-weight-bold);\n}\n.activity-toast button:hover {\n background: var(--office-selected);\n}\n.activity-toast button svg {\n width: 1em;\n height: 1em;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n@media (pointer: coarse) {\n .activity-toast button {\n min-width: var(--office-touch-target, 44px);\n min-height: var(--office-touch-target, 44px);\n }\n}\n\n/* ---- chat and calendar ------------------------------------------------------------------ */\n.calendar-back svg,\n.show-calendar svg,\n.group-mark svg,\n.chat-head .chat-people svg {\n width: 1.25em;\n height: 1.25em;\n flex: none;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.chat-head office-ui-avatar {\n flex: none;\n}\n.group-mark {\n display: grid;\n flex: none;\n place-items: center;\n width: 2.3em;\n height: 2.3em;\n border-radius: var(--office-radius-full);\n background: var(--office-teams-bubble);\n color: var(--office-teams-brand);\n}\n.chat-head .chat-people {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-2);\n}\n.empty-main {\n display: grid;\n flex: 1;\n place-content: center;\n justify-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-6);\n color: var(--office-teams-text-subtle);\n text-align: center;\n}\n.empty-main button {\n padding: var(--office-space-2) var(--office-space-4);\n border: 0;\n border-radius: var(--office-radius-md);\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n font: inherit;\n}\n.main > office-ui-calendar-view {\n flex: 1;\n min-height: 0;\n}\n.calendar-back {\n display: none;\n}\n@media (max-width: 56em) {\n .calendar-back {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n padding: var(--office-space-2) var(--office-space-3);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n }\n .calendar-back button {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-2);\n min-height: var(--office-touch-target, 44px);\n padding: var(--office-space-2) var(--office-space-3);\n border: 0;\n border-radius: var(--office-radius-md);\n background: transparent;\n }\n .chat-head .chat-people {\n min-width: var(--office-touch-target, 44px);\n }\n}\n.show-calendar {\n display: none;\n}\n@media (max-width: 56em) {\n .show-calendar {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-2);\n width: calc(100% - 2 * var(--office-space-4));\n min-height: var(--office-touch-target, 44px);\n margin: var(--office-space-3) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n }\n}\n";
3376
+ function threadPane(state, client, openFile, close, highlightId = "") {
2444
3377
  const thread = state.thread;
2445
3378
  if (!thread) return nothing;
2446
3379
  return html`<aside class="thread-pane" aria-label="Thread">
@@ -2456,7 +3389,11 @@ function threadPane(state, client, openFile, close) {
2456
3389
  </header>
2457
3390
  <office-ui-chat-list
2458
3391
  .messages=${[thread.root, ...thread.replies]}
3392
+ .pinnedIds=${state.pinned.map((p) => p.message.id)}
2459
3393
  self-id=${state.user.id}
3394
+ .highlightId=${highlightId}
3395
+ @office-chat-pin=${(e) => client.pinMessage(e.detail.messageId)}
3396
+ @office-chat-unpin=${(e) => client.unpinMessage(e.detail.messageId)}
2460
3397
  @office-chat-react=${(e) => client.toggleReaction(e.detail.messageId, e.detail.emoji)}
2461
3398
  @office-chat-reply=${(e) => client.startReply(e.detail.messageId)}
2462
3399
  @office-chat-edit=${(e) => client.startEdit(e.detail.messageId)}
@@ -2470,6 +3407,7 @@ function threadPane(state, client, openFile, close) {
2470
3407
  .value=${state.draft.text}
2471
3408
  .files=${state.draft.files}
2472
3409
  .missingFiles=${state.draft.missingFiles}
3410
+ .mentionTargets=${state.mentionTargets}
2473
3411
  @office-chat-draft=${(e) => client.setDraft(e.detail)}
2474
3412
  placeholder="Reply in thread"
2475
3413
  @office-chat-typing=${() => client.notifyTyping()}
@@ -2486,7 +3424,10 @@ function followedThreads(state, client, open, unreadOnly, filter) {
2486
3424
  const threads = state.followedThreads.filter((thread) => !unreadOnly || thread.unread > 0);
2487
3425
  return html`<section class="followed-threads" aria-label="Followed threads">
2488
3426
  <h1>Followed threads</h1>
2489
- <p>Threads you follow on this device. Notifications are not enabled.</p>
3427
+ <p>
3428
+ Threads you follow on this device. Their replies appear in Activity and notify you according
3429
+ to your notification settings.
3430
+ </p>
2490
3431
  <fieldset>
2491
3432
  <legend>Automatically follow</legend>
2492
3433
  <label
@@ -2622,17 +3563,37 @@ function messageTransfers(state, client) {
2622
3563
  }
2623
3564
 
2624
3565
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/profile-menu.css?raw
2625
- var profile_menu_default = ":host {\n display: inline-flex;\n font: inherit;\n color: inherit;\n}\n.profile {\n display: inline-flex;\n align-items: center;\n padding: var(--office-space-1);\n border: 0;\n border-radius: var(--office-radius-full);\n background: transparent;\n cursor: pointer;\n}\n.profile:hover {\n background: var(--office-selected);\n}\n[popover] {\n min-width: 20em;\n max-width: 90vw;\n padding: var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n color: var(--office-foreground);\n box-shadow: var(--office-shadow-teams);\n}\n.identity {\n display: flex;\n gap: var(--office-space-3);\n align-items: center;\n margin-bottom: var(--office-space-4);\n}\n.identity strong {\n overflow-wrap: anywhere;\n}\nlabel {\n display: grid;\n gap: var(--office-space-2);\n}\nselect {\n font: inherit;\n padding: var(--office-space-2);\n color: inherit;\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n}\n.connection {\n color: var(--office-teams-text-subtle);\n margin-bottom: 0;\n font-size: var(--office-font-size-sm);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n";
3566
+ var profile_menu_default = ":host {\n display: inline-flex;\n font: inherit;\n color: inherit;\n}\n.profile {\n display: inline-flex;\n align-items: center;\n padding: var(--office-space-1);\n border: 0;\n border-radius: var(--office-radius-full);\n background: transparent;\n cursor: pointer;\n}\n.profile:hover {\n background: var(--office-selected);\n}\n[popover] {\n min-width: 20em;\n max-width: 90vw;\n max-height: min(80vh, 40em);\n overflow: auto;\n padding: var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n color: var(--office-foreground);\n box-shadow: var(--office-shadow-teams);\n}\n.identity {\n display: flex;\n gap: var(--office-space-3);\n align-items: center;\n margin-bottom: var(--office-space-4);\n}\n.identity strong {\n overflow-wrap: anywhere;\n}\nlabel {\n display: grid;\n gap: var(--office-space-2);\n margin-bottom: var(--office-space-3);\n}\n.who {\n display: grid;\n min-width: 0;\n}\n.status-line,\n.message,\n.hint {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-sm);\n overflow-wrap: anywhere;\n}\n.hint:empty {\n display: none;\n}\n.actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-2);\n margin-bottom: var(--office-space-3);\n}\n.actions button {\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.actions button:hover:not(:disabled) {\n background: var(--office-selected);\n}\n.actions button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n@media (pointer: coarse) {\n .actions button,\n select,\n input {\n min-height: var(--office-touch-target, 44px);\n }\n}\ninput,\nselect {\n font: inherit;\n padding: var(--office-space-2);\n color: inherit;\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n}\nselect:disabled {\n opacity: 0.6;\n}\n.connection {\n color: var(--office-teams-text-subtle);\n margin-bottom: 0;\n font-size: var(--office-font-size-sm);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n";
2626
3567
 
2627
3568
  // src/teams/app/profile-menu.ts
2628
- var AVAILABILITY = ["available", "busy", "away"];
3569
+ var idleLabel = (minutes) => minutes === 0 ? "Never" : `${minutes} minutes`;
3570
+ var untilLabel = (at) => {
3571
+ if (at === null) return "";
3572
+ const d = new Date(at);
3573
+ const today = (/* @__PURE__ */ new Date()).toDateString();
3574
+ if (d.getHours() === 0 && d.getMinutes() === 0) {
3575
+ const lastDay = new Date(at - 1);
3576
+ return lastDay.toDateString() === today ? "Until end of today" : `Until end of ${lastDay.toLocaleDateString([], { weekday: "long" })}`;
3577
+ }
3578
+ const time = d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
3579
+ return d.toDateString() === today ? `Until ${time}` : `Until ${d.toLocaleDateString([], { weekday: "long" })} ${time}`;
3580
+ };
2629
3581
  var TeamsProfileMenu = class extends LitElement {
2630
3582
  static styles = unsafeCSS(profile_menu_default);
2631
- static properties = { state: { attribute: false }, open: { state: true } };
3583
+ static properties = {
3584
+ state: { attribute: false },
3585
+ open: { state: true },
3586
+ draft: { state: true },
3587
+ resetAfter: { state: true },
3588
+ clearAfter: { state: true }
3589
+ };
2632
3590
  constructor() {
2633
3591
  super();
2634
3592
  this.state = null;
2635
3593
  this.open = false;
3594
+ this.draft = null;
3595
+ this.resetAfter = "never";
3596
+ this.clearAfter = "never";
2636
3597
  }
2637
3598
  willUpdate(changed) {
2638
3599
  const old = changed.get("state");
@@ -2644,9 +3605,27 @@ var TeamsProfileMenu = class extends LitElement {
2644
3605
  if (toggleAnchoredPopover(panel, this.renderRoot.querySelector("button")))
2645
3606
  panel.querySelector("select")?.focus();
2646
3607
  }
3608
+ fire(name, detail) {
3609
+ this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
3610
+ }
3611
+ saveMessage() {
3612
+ this.fire("teams-profile-message", {
3613
+ message: this.draft ?? this.state?.presence.message ?? "",
3614
+ duration: this.clearAfter
3615
+ });
3616
+ this.draft = null;
3617
+ }
3618
+ durationOptions(selected) {
3619
+ return STATUS_DURATIONS.map(
3620
+ (value) => html`<option value=${value} ?selected=${selected === value}>
3621
+ ${STATUS_DURATION_LABELS[value]}
3622
+ </option>`
3623
+ );
3624
+ }
2647
3625
  render() {
2648
3626
  const state = this.state;
2649
3627
  if (!state) return nothing;
3628
+ const { presence } = state;
2650
3629
  return html`<button
2651
3630
  type="button"
2652
3631
  class="profile"
@@ -2686,25 +3665,91 @@ var TeamsProfileMenu = class extends LitElement {
2686
3665
  name=${state.user.name}
2687
3666
  seed=${state.user.id}
2688
3667
  presence=${state.availability}
2689
- ></office-ui-avatar
2690
- ><strong>${state.user.name}</strong>
3668
+ ></office-ui-avatar>
3669
+ <div class="who">
3670
+ <strong>${state.user.name}</strong>
3671
+ <span class="status-line"
3672
+ >${PRESENCE_LABELS[state.availability]}${state.presence.idle && state.presence.status === "available" ? " (idle)" : ""}</span
3673
+ >
3674
+ ${presence.message ? html`<span class="message">${presence.message}</span>` : nothing}
3675
+ </div>
2691
3676
  </div>
2692
3677
  <label
2693
3678
  >Availability<select
2694
- .value=${state.availability}
3679
+ .value=${presence.status}
2695
3680
  @change=${(event) => {
2696
3681
  const availability = event.target.value;
2697
- if (AVAILABILITY.some((value) => value === availability))
2698
- this.dispatchEvent(
2699
- new CustomEvent("teams-profile-status", {
2700
- detail: { availability },
2701
- bubbles: true,
2702
- composed: true
2703
- })
2704
- );
3682
+ if (!isPresenceStatus(availability)) return;
3683
+ this.fire("teams-profile-status", { availability, duration: this.resetAfter });
2705
3684
  }}
2706
3685
  >
2707
- ${AVAILABILITY.map((value) => html`<option value=${value} ?selected=${state.availability === value}>${value[0].toUpperCase() + value.slice(1)}</option>`)}
3686
+ ${PRESENCE_STATUSES.map((value) => html`<option value=${value} ?selected=${presence.status === value}>${PRESENCE_LABELS[value]}</option>`)}
3687
+ </select></label
3688
+ >
3689
+ <label
3690
+ >Reset status after<select
3691
+ name="status-duration"
3692
+ @change=${(event) => {
3693
+ const duration = event.target.value;
3694
+ if (!isStatusDuration(duration)) return;
3695
+ this.resetAfter = duration;
3696
+ this.fire("teams-profile-status", { availability: presence.status, duration });
3697
+ }}
3698
+ >
3699
+ ${this.durationOptions(this.resetAfter)}
3700
+ </select>
3701
+ <span class="hint">${untilLabel(presence.statusExpiresAt)}</span></label
3702
+ >
3703
+ <label
3704
+ >Status message<input
3705
+ type="text"
3706
+ name="message"
3707
+ maxlength=${MAX_STATUS_MESSAGE_CHARS}
3708
+ placeholder="What's your status message?"
3709
+ .value=${this.draft ?? presence.message}
3710
+ @input=${(event) => this.draft = event.target.value}
3711
+ @keydown=${(event) => {
3712
+ if (event.key !== "Enter") return;
3713
+ event.preventDefault();
3714
+ this.saveMessage();
3715
+ }}
3716
+ />
3717
+ <span class="hint">${untilLabel(presence.messageExpiresAt)}</span></label
3718
+ >
3719
+ <label
3720
+ >Clear status message after<select
3721
+ name="message-duration"
3722
+ @change=${(event) => {
3723
+ const duration = event.target.value;
3724
+ if (isStatusDuration(duration)) this.clearAfter = duration;
3725
+ }}
3726
+ >
3727
+ ${this.durationOptions(this.clearAfter)}
3728
+ </select></label
3729
+ >
3730
+ <div class="actions">
3731
+ <button type="button" @click=${this.saveMessage}>Save message</button>
3732
+ <button
3733
+ type="button"
3734
+ ?disabled=${!presence.message && !this.draft}
3735
+ @click=${() => {
3736
+ this.draft = "";
3737
+ this.saveMessage();
3738
+ }}
3739
+ >
3740
+ Clear message
3741
+ </button>
3742
+ </div>
3743
+ <label
3744
+ >Show me as away after<select
3745
+ name="idle"
3746
+ @change=${(event) => {
3747
+ const minutes = Number(event.target.value);
3748
+ if (IDLE_MINUTE_CHOICES.includes(minutes))
3749
+ this.fire("teams-profile-idle", { minutes });
3750
+ }}
3751
+ >
3752
+ ${IDLE_MINUTE_CHOICES.map((minutes) => html`<option value=${minutes} ?selected=${presence.idleMinutes === minutes}>${idleLabel(minutes)}</option>`)}
2708
3753
  </select></label
2709
3754
  >
2710
3755
  <p class="connection">
@@ -2718,6 +3763,157 @@ function defineTeamsProfileMenu() {
2718
3763
  customElements.define("teams-profile-menu", TeamsProfileMenu);
2719
3764
  }
2720
3765
 
3766
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/pinned-bar.css?raw
3767
+ var pinned_bar_default = ":host {\n display: block;\n flex: none;\n font: inherit;\n color: inherit;\n}\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\nsection {\n background: var(--office-teams-panel);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nbutton {\n font: inherit;\n color: inherit;\n cursor: pointer;\n border: 0;\n background: transparent;\n}\n.summary {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n padding: var(--office-space-1) var(--office-space-3);\n}\n.latest {\n display: flex;\n flex: 1;\n align-items: center;\n gap: var(--office-space-2);\n min-width: 0;\n min-height: 2.2em;\n padding: var(--office-space-1) var(--office-space-2);\n border-radius: var(--office-radius);\n text-align: start;\n}\n.latest:hover,\n.count:hover,\n.jump:hover,\n.unpin:hover {\n background: var(--office-selected);\n}\n.mark {\n display: inline-grid;\n flex: none;\n color: var(--office-teams-brand);\n}\nsvg {\n width: 1.1em;\n height: 1.1em;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.text {\n min-width: 0;\n overflow: hidden;\n color: var(--office-teams-text-subtle);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.count {\n flex: none;\n min-height: 2.2em;\n padding: var(--office-space-1) var(--office-space-3);\n border-radius: var(--office-radius);\n color: var(--office-teams-brand);\n font-weight: var(--office-font-weight-bold);\n}\nul {\n display: grid;\n gap: var(--office-space-1);\n max-height: 14em;\n margin: 0;\n padding: 0 var(--office-space-3) var(--office-space-2);\n overflow: auto;\n list-style: none;\n}\nli {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n.jump {\n display: grid;\n flex: 1;\n gap: var(--office-space-0);\n min-width: 0;\n padding: var(--office-space-2) var(--office-space-3);\n border-radius: var(--office-radius-md);\n text-align: start;\n}\n.by {\n color: var(--office-teams-text-subtle);\n font-size: var(--office-font-size-xs);\n}\n.unpin {\n flex: none;\n margin-inline-end: var(--office-space-2);\n padding: var(--office-space-1) var(--office-space-3);\n border-radius: var(--office-radius);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (pointer: coarse) {\n .latest,\n .count,\n .unpin {\n min-height: var(--office-touch-target, 44px);\n }\n .unpin {\n min-width: var(--office-touch-target, 44px);\n }\n}\n";
3768
+
3769
+ // src/teams/app/pinned-bar.ts
3770
+ var SNIPPET = 140;
3771
+ var snippet = (m) => {
3772
+ const text = m.text.replace(/\s+/gu, " ").trim();
3773
+ if (text) return text.length > SNIPPET ? `${text.slice(0, SNIPPET)}...` : text;
3774
+ return m.attachments[0]?.name ?? "Attachment";
3775
+ };
3776
+ var TeamsPinnedBar = class extends LitElement {
3777
+ static styles = unsafeCSS(pinned_bar_default);
3778
+ static properties = { pinned: { attribute: false }, expanded: { state: true } };
3779
+ constructor() {
3780
+ super();
3781
+ this.pinned = [];
3782
+ this.expanded = false;
3783
+ }
3784
+ emit(name, messageId) {
3785
+ this.dispatchEvent(
3786
+ new CustomEvent(name, { detail: { messageId }, bubbles: true, composed: true })
3787
+ );
3788
+ }
3789
+ render() {
3790
+ const [latest] = this.pinned;
3791
+ if (!latest) return nothing;
3792
+ return html`
3793
+ <section aria-label="Pinned messages">
3794
+ <div class="summary">
3795
+ <button
3796
+ type="button"
3797
+ class="latest"
3798
+ @click=${() => this.emit("teams-pinned-open", latest.message.id)}
3799
+ >
3800
+ <span class="mark">${icon("pin")}</span>
3801
+ <strong>${latest.message.authorName}</strong>
3802
+ <span class="text">${snippet(latest.message)}</span>
3803
+ </button>
3804
+ <button
3805
+ type="button"
3806
+ class="count"
3807
+ aria-expanded=${String(this.expanded)}
3808
+ aria-controls="pins"
3809
+ @click=${() => this.expanded = !this.expanded}
3810
+ >
3811
+ ${this.pinned.length} pinned
3812
+ </button>
3813
+ </div>
3814
+ ${this.expanded ? html`<ul id="pins">
3815
+ ${this.pinned.map(
3816
+ (pin) => html`<li>
3817
+ <button
3818
+ type="button"
3819
+ class="jump"
3820
+ @click=${() => this.emit("teams-pinned-open", pin.message.id)}
3821
+ >
3822
+ <strong>${pin.message.authorName}</strong>
3823
+ <span class="text">${snippet(pin.message)}</span>
3824
+ <span class="by">Pinned by ${pin.pinnedByName}</span>
3825
+ </button>
3826
+ <button
3827
+ type="button"
3828
+ class="unpin"
3829
+ aria-label=${`Unpin message from ${pin.message.authorName}`}
3830
+ title="Unpin"
3831
+ @click=${() => this.emit("teams-pinned-unpin", pin.message.id)}
3832
+ >
3833
+ Unpin
3834
+ </button>
3835
+ </li>`
3836
+ )}
3837
+ </ul>` : nothing}
3838
+ </section>
3839
+ `;
3840
+ }
3841
+ };
3842
+ function defineTeamsPinnedBar() {
3843
+ if (!customElements.get("teams-pinned-bar"))
3844
+ customElements.define("teams-pinned-bar", TeamsPinnedBar);
3845
+ }
3846
+
3847
+ // src/teams/app/activity.ts
3848
+ var ACTIVITY_EVENTS = ["pointerdown", "pointermove", "keydown", "wheel", "touchstart"];
3849
+ function watchActivity(target, onActivity, throttleMs = 1e3, now = Date.now) {
3850
+ let last = Number.NEGATIVE_INFINITY;
3851
+ const handler = () => {
3852
+ const t = now();
3853
+ if (t - last < throttleMs) return;
3854
+ last = t;
3855
+ onActivity();
3856
+ };
3857
+ for (const name of ACTIVITY_EVENTS) target.addEventListener(name, handler, { passive: true });
3858
+ return () => {
3859
+ for (const name of ACTIVITY_EVENTS) target.removeEventListener(name, handler);
3860
+ };
3861
+ }
3862
+ function deliverNotification(item, settings, env, actions) {
3863
+ let result = "none";
3864
+ if (!env.hidden()) {
3865
+ actions.toast(item);
3866
+ result = "toast";
3867
+ } else if (env.permission() === "granted") {
3868
+ env.show(
3869
+ describeActivity(item),
3870
+ { body: item.preview, tag: item.id },
3871
+ () => actions.open(item)
3872
+ );
3873
+ result = "desktop";
3874
+ }
3875
+ if (result !== "none" && settings.sound) env.beep();
3876
+ return result;
3877
+ }
3878
+ function chime() {
3879
+ try {
3880
+ const Context = globalThis.AudioContext ?? globalThis.webkitAudioContext;
3881
+ if (!Context) return;
3882
+ const audio = new Context();
3883
+ const gain = audio.createGain();
3884
+ gain.gain.setValueAtTime(0.06, audio.currentTime);
3885
+ gain.gain.exponentialRampToValueAtTime(1e-4, audio.currentTime + 0.35);
3886
+ gain.connect(audio.destination);
3887
+ [660, 880].forEach((frequency, index) => {
3888
+ const tone = audio.createOscillator();
3889
+ tone.frequency.value = frequency;
3890
+ tone.connect(gain);
3891
+ tone.start(audio.currentTime + index * 0.12);
3892
+ tone.stop(audio.currentTime + index * 0.12 + 0.12);
3893
+ });
3894
+ globalThis.setTimeout(() => void audio.close(), 700);
3895
+ } catch {
3896
+ }
3897
+ }
3898
+ function browserDeliveryEnv() {
3899
+ return {
3900
+ hidden: () => globalThis.document?.visibilityState === "hidden",
3901
+ permission: () => globalThis.Notification?.permission ?? "unsupported",
3902
+ show(title, options, onClick) {
3903
+ try {
3904
+ const notification = new globalThis.Notification(title, options);
3905
+ notification.onclick = () => {
3906
+ globalThis.focus();
3907
+ notification.close();
3908
+ onClick();
3909
+ };
3910
+ } catch {
3911
+ }
3912
+ },
3913
+ beep: chime
3914
+ };
3915
+ }
3916
+
2721
3917
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/add-tab-dialog.css?raw
2722
3918
  var add_tab_dialog_default = ":host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n width: min(54em, 94vw);\n max-height: 90vh;\n padding: 0;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-lg);\n color: inherit;\n background: var(--office-popover);\n box-shadow: var(--office-shadow-teams);\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\nheader,\n.app-heading,\nfooter {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n}\nheader {\n justify-content: space-between;\n padding: var(--office-space-4) var(--office-space-5);\n border-bottom: var(--office-border-width) solid var(--office-teams-divider);\n}\nh1,\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nsection,\nform {\n padding: var(--office-space-5);\n}\nform {\n display: grid;\n gap: var(--office-space-3);\n}\n.apps {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));\n gap: var(--office-space-3);\n}\nbutton,\ninput {\n font: inherit;\n color: inherit;\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius);\n background: var(--office-field-background);\n padding: var(--office-space-2) var(--office-space-3);\n}\nbutton {\n cursor: pointer;\n}\nbutton:hover:not(:disabled) {\n background: var(--office-selected);\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.app {\n display: grid;\n justify-items: start;\n text-align: start;\n gap: var(--office-space-2);\n padding: var(--office-space-4);\n}\n.app-heading h2 {\n flex: 1;\n}\n.badge {\n display: grid;\n place-items: center;\n width: 2.6em;\n height: 2.6em;\n border-radius: var(--office-radius-md);\n color: var(--office-call-foreground);\n background: var(--office-file-other);\n font-weight: var(--office-font-weight-bold);\n}\n.badge[data-kind='docx'] {\n background: var(--office-file-docx);\n}\n.badge[data-kind='xlsx'] {\n background: var(--office-file-xlsx);\n}\n.badge[data-kind='pptx'] {\n background: var(--office-file-pptx);\n}\n.badge[data-kind='vsdx'] {\n background: var(--office-file-vsdx);\n}\nlabel {\n display: grid;\n gap: var(--office-space-2);\n}\ninput {\n min-width: 0;\n}\nsmall,\n.hint,\nsection > p {\n color: var(--office-teams-text-subtle);\n line-height: 1.5;\n}\nsmall {\n display: block;\n}\n.files {\n margin: 0;\n padding: var(--office-space-2);\n max-height: 18em;\n overflow: auto;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius);\n}\n.file {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-3);\n border-radius: var(--office-radius);\n cursor: pointer;\n}\n.file:has(input:checked) {\n background: var(--office-teams-brand-subtle);\n}\n.file strong {\n overflow-wrap: anywhere;\n}\n[role='alert'] {\n color: var(--office-danger);\n}\nfooter {\n justify-content: flex-end;\n}\n.post-choice {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n}\nbutton.primary {\n color: var(--office-teams-brand-foreground);\n border-color: var(--office-teams-brand);\n background: var(--office-teams-brand);\n}\nbutton.primary:hover:not(:disabled) {\n background: var(--office-teams-brand-hover);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n";
2723
3919
 
@@ -2964,129 +4160,6 @@ function defineTeamsAddTabDialog() {
2964
4160
  customElements.define("teams-add-tab-dialog", TeamsAddTabDialog);
2965
4161
  }
2966
4162
 
2967
- // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/navigation-drawer.css?raw
2968
- var navigation_drawer_default = ":host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n inset: 0 auto 0 0;\n margin: 0;\n width: min(24em, 92vw);\n max-width: 92vw;\n height: 100dvh;\n max-height: 100dvh;\n padding: 0;\n border: 0;\n border-inline-end: var(--office-border-width) solid var(--office-teams-divider);\n background: var(--office-teams-panel);\n color: inherit;\n box-shadow: var(--office-shadow-teams);\n}\ndialog[open] {\n display: flex;\n flex-direction: column;\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-3) var(--office-space-4);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n font: inherit;\n background: var(--office-background);\n color: inherit;\n cursor: pointer;\n}\nbutton:hover {\n background: var(--office-selected);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\nnav {\n flex: 1;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n}\n";
2969
-
2970
- // src/dialog/focus.ts
2971
- var MODAL_FOCUSABLE_SELECTOR = [
2972
- "a[href]",
2973
- "button:not([disabled])",
2974
- 'input:not([disabled]):not([type="hidden"])',
2975
- "select:not([disabled])",
2976
- "textarea:not([disabled])",
2977
- '[contenteditable="true"]',
2978
- '[tabindex]:not([tabindex="-1"])'
2979
- ].join(",");
2980
- function isAvailable(element) {
2981
- if (element.hidden || element.getAttribute("aria-hidden") === "true") {
2982
- return false;
2983
- }
2984
- const style = element.ownerDocument.defaultView?.getComputedStyle(element);
2985
- return style?.display !== "none" && style?.visibility !== "hidden";
2986
- }
2987
- function focusableElements(root, found = []) {
2988
- for (const child of Array.from(root.children)) {
2989
- if (child instanceof HTMLElement) {
2990
- if (!isAvailable(child)) continue;
2991
- if (child.matches(MODAL_FOCUSABLE_SELECTOR) && isAvailable(child)) {
2992
- found.push(child);
2993
- }
2994
- if (child.shadowRoot && isAvailable(child)) {
2995
- focusableElements(child.shadowRoot, found);
2996
- }
2997
- }
2998
- focusableElements(child, found);
2999
- }
3000
- return found;
3001
- }
3002
- function deepActiveElement(doc) {
3003
- let active = doc.activeElement;
3004
- while (active?.shadowRoot?.activeElement) {
3005
- active = active.shadowRoot.activeElement;
3006
- }
3007
- return active;
3008
- }
3009
- function modalFocusableElements(panel) {
3010
- return focusableElements(panel);
3011
- }
3012
- function modalActiveElement(doc) {
3013
- return deepActiveElement(doc);
3014
- }
3015
-
3016
- // src/teams/app/navigation-drawer.ts
3017
- var TeamsNavigationDrawer = class extends LitElement {
3018
- static styles = unsafeCSS(navigation_drawer_default);
3019
- static properties = {
3020
- open: { type: Boolean, reflect: true },
3021
- heading: { type: String }
3022
- };
3023
- compact;
3024
- resized = () => {
3025
- if (this.open && !this.compact?.matches) this.close();
3026
- };
3027
- constructor() {
3028
- super();
3029
- this.open = false;
3030
- this.heading = "Teams and channels";
3031
- this.addEventListener("keydown", (event) => this.trapTab(event));
3032
- }
3033
- connectedCallback() {
3034
- super.connectedCallback();
3035
- this.compact = globalThis.matchMedia?.("(max-width: 56.25em)");
3036
- this.compact?.addEventListener("change", this.resized);
3037
- }
3038
- disconnectedCallback() {
3039
- this.compact?.removeEventListener("change", this.resized);
3040
- super.disconnectedCallback();
3041
- }
3042
- updated() {
3043
- const dialog = this.renderRoot.querySelector("dialog");
3044
- if (this.open && !dialog.open) dialog.showModal();
3045
- else if (!this.open && dialog.open) dialog.close();
3046
- }
3047
- trapTab(event) {
3048
- if (!this.open || event.key !== "Tab") return;
3049
- const close = this.renderRoot.querySelector("button");
3050
- const stops = [close, ...modalFocusableElements(this)];
3051
- const current = modalActiveElement(this.ownerDocument);
3052
- const index = stops.findIndex((element) => element === current);
3053
- if (index < 0 || (event.shiftKey ? index === 0 : index === stops.length - 1)) {
3054
- event.preventDefault();
3055
- stops[event.shiftKey ? stops.length - 1 : 0]?.focus();
3056
- }
3057
- }
3058
- close() {
3059
- this.dispatchEvent(
3060
- new CustomEvent("teams-navigation-close", { bubbles: true, composed: true })
3061
- );
3062
- }
3063
- render() {
3064
- return html`<dialog
3065
- aria-label="Workspace navigation"
3066
- @click=${(event) => {
3067
- const dialog = event.currentTarget;
3068
- const rect = dialog.getBoundingClientRect();
3069
- if (event.target === dialog && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom))
3070
- this.close();
3071
- }}
3072
- @cancel=${(event) => {
3073
- event.preventDefault();
3074
- this.close();
3075
- }}
3076
- >
3077
- <header>
3078
- <h2>${this.heading}</h2>
3079
- <button type="button" aria-label="Close navigation" @click=${this.close}>×</button>
3080
- </header>
3081
- <nav aria-label="Workspace channels and calls"><slot></slot></nav>
3082
- </dialog>`;
3083
- }
3084
- };
3085
- function defineTeamsNavigationDrawer(registry = globalThis.customElements) {
3086
- if (registry && !registry.get("teams-navigation-drawer"))
3087
- registry.define("teams-navigation-drawer", TeamsNavigationDrawer);
3088
- }
3089
-
3090
4163
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/create-channel-dialog.css?raw
3091
4164
  var create_channel_dialog_default = ":host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n width: min(40em, 94vw);\n max-height: 90dvh;\n padding: 0;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: inherit;\n box-shadow: var(--office-shadow-teams);\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-4) var(--office-space-5);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nform {\n display: grid;\n gap: var(--office-space-3);\n padding: var(--office-space-5);\n}\nlabel {\n display: grid;\n gap: var(--office-space-2);\n font-weight: var(--office-font-weight-bold);\n}\ninput,\ntextarea {\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n padding: var(--office-space-3);\n font: inherit;\n font-weight: normal;\n color: inherit;\n background: var(--office-field-background);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius-md);\n}\ntextarea {\n min-height: 6em;\n resize: vertical;\n}\np,\nsmall {\n margin: 0;\n color: var(--office-teams-text-subtle);\n line-height: 1.5;\n}\n.issue {\n color: var(--office-danger);\n}\nfooter {\n display: flex;\n justify-content: flex-end;\n gap: var(--office-space-2);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\nbutton:hover {\n background: var(--office-selected);\n}\nbutton.primary {\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n border-color: transparent;\n}\nbutton.primary:hover {\n background: var(--office-teams-brand-hover);\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n";
3092
4165
 
@@ -3191,17 +4264,724 @@ function defineTeamsCreateChannelDialog(registry = globalThis.customElements) {
3191
4264
  registry.define("teams-create-channel-dialog", TeamsCreateChannelDialog);
3192
4265
  }
3193
4266
 
4267
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/form-dialog.css?raw
4268
+ var form_dialog_default = "/* The frame shared by the Chat and Calendar dialogs: a modal <dialog> with a header, a form and a\n footer, in the same look as the create-channel dialog. */\n:host {\n font: var(--office-font-size) var(--office-font);\n color: var(--office-foreground);\n}\ndialog {\n box-sizing: border-box;\n width: min(40em, 94vw);\n max-height: 90dvh;\n padding: 0;\n overflow: auto;\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: inherit;\n box-shadow: var(--office-shadow-teams);\n}\ndialog::backdrop {\n background: var(--office-overlay);\n}\ndialog > header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-4) var(--office-space-5);\n border-block-end: var(--office-border-width) solid var(--office-teams-divider);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n}\nform,\n.body {\n display: grid;\n gap: var(--office-space-3);\n padding: var(--office-space-5);\n}\nlabel,\n.field {\n display: grid;\n gap: var(--office-space-2);\n font-weight: var(--office-font-weight-bold);\n}\ninput:not([type='checkbox']),\ntextarea,\nselect {\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n padding: var(--office-space-3);\n font: inherit;\n font-weight: normal;\n color: inherit;\n background: var(--office-field-background);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-radius-md);\n}\ntextarea {\n min-height: 6em;\n resize: vertical;\n}\np,\nsmall {\n margin: 0;\n color: var(--office-teams-text-subtle);\n line-height: 1.5;\n}\n.issue {\n color: var(--office-danger);\n}\nfooter {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: var(--office-space-2);\n}\nbutton {\n padding: var(--office-space-2) var(--office-space-4);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\nbutton:hover {\n background: var(--office-selected);\n}\nbutton.primary {\n background: var(--office-teams-brand);\n color: var(--office-teams-brand-foreground);\n border-color: transparent;\n}\nbutton.primary:hover {\n background: var(--office-teams-brand-hover);\n}\nbutton.danger {\n color: var(--office-danger);\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: default;\n}\n:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (max-width: 40em) {\n /* Full screen on a phone: the dialog is a page, not a floating card. */\n dialog {\n width: 100vw;\n max-width: 100vw;\n height: 100dvh;\n max-height: 100dvh;\n border-radius: 0;\n }\n button,\n input:not([type='checkbox']),\n select {\n min-height: 2.75em;\n }\n}\n";
4269
+
4270
+ // src/teams/app/dialog-base.ts
4271
+ var dialogStyles = (own = "") => withStyles(`${form_dialog_default}
4272
+ ${own}`);
4273
+ var TeamsDialog = class extends TeamsElement {
4274
+ static properties = {
4275
+ open: { type: Boolean, reflect: true }
4276
+ };
4277
+ constructor() {
4278
+ super();
4279
+ this.open = false;
4280
+ }
4281
+ updated() {
4282
+ const dialog = this.renderRoot.querySelector("dialog");
4283
+ if (!dialog) return;
4284
+ if (this.open && !dialog.open) dialog.showModal();
4285
+ else if (!this.open && dialog.open) dialog.close();
4286
+ }
4287
+ requestClose() {
4288
+ this.fire("teams-dialog-close", {});
4289
+ }
4290
+ /** The dialog frame with a title and a close button around `body`. */
4291
+ frame(title, body, closeLabel = `Close ${title.toLowerCase()}`) {
4292
+ return html`<dialog
4293
+ aria-labelledby="title"
4294
+ @cancel=${(event) => {
4295
+ event.preventDefault();
4296
+ this.requestClose();
4297
+ }}
4298
+ >
4299
+ <header>
4300
+ <h2 id="title">${title}</h2>
4301
+ <button type="button" aria-label=${closeLabel} @click=${this.requestClose}>×</button>
4302
+ </header>
4303
+ ${body}
4304
+ </dialog>`;
4305
+ }
4306
+ };
4307
+
4308
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/new-chat-dialog.css?raw
4309
+ var new_chat_dialog_default = "office-ui-people-picker {\n font-weight: normal;\n}\n";
4310
+
4311
+ // src/teams/app/new-chat-dialog.ts
4312
+ var TeamsNewChatDialog = class extends TeamsDialog {
4313
+ static styles = dialogStyles(new_chat_dialog_default);
4314
+ static properties = {
4315
+ ...TeamsDialog.properties,
4316
+ contacts: { attribute: false },
4317
+ create: { attribute: false },
4318
+ selected: { state: true },
4319
+ name: { state: true },
4320
+ issue: { state: true }
4321
+ };
4322
+ constructor() {
4323
+ super();
4324
+ this.contacts = [];
4325
+ this.create = void 0;
4326
+ this.selected = [];
4327
+ this.name = "";
4328
+ this.issue = "";
4329
+ }
4330
+ willUpdate(changed) {
4331
+ if (changed.has("open") && this.open) {
4332
+ this.selected = [];
4333
+ this.name = "";
4334
+ this.issue = "";
4335
+ }
4336
+ }
4337
+ submit(event) {
4338
+ event.preventDefault();
4339
+ if (this.selected.length === 0) {
4340
+ this.issue = "Pick at least one person.";
4341
+ return;
4342
+ }
4343
+ if (!this.create?.(this.selected, this.selected.length > 1 ? this.name : ""))
4344
+ this.issue = "The chat could not be started.";
4345
+ }
4346
+ render() {
4347
+ const group = this.selected.length > 1;
4348
+ return this.frame(
4349
+ "New chat",
4350
+ html`<form @submit=${this.submit}>
4351
+ <div class="field">
4352
+ To
4353
+ <office-ui-people-picker
4354
+ label="People to chat with"
4355
+ .contacts=${this.contacts}
4356
+ .selected=${this.selected}
4357
+ @office-people-change=${(e) => {
4358
+ this.selected = e.detail.selected.slice(0, MAX_CHAT_MEMBERS - 1);
4359
+ this.issue = "";
4360
+ }}
4361
+ ></office-ui-people-picker>
4362
+ </div>
4363
+ ${group ? html`<label
4364
+ >Group name (optional)<input
4365
+ maxlength=${MAX_CHANNEL_NAME_CHARS}
4366
+ .value=${this.name}
4367
+ placeholder="Name this group chat"
4368
+ @input=${(e) => this.name = e.target.value}
4369
+ /></label>` : nothing}
4370
+ <small
4371
+ >${group ? "Everyone you pick joins one group chat. Only its members can read it." : "Pick one person for a 1:1 chat, or several for a group chat."}</small
4372
+ >
4373
+ ${this.issue ? html`<p class="issue" role="alert">${this.issue}</p>` : nothing}
4374
+ <footer>
4375
+ <button type="button" @click=${this.requestClose}>Cancel</button>
4376
+ <button type="submit" class="primary" ?disabled=${this.selected.length === 0}>
4377
+ ${group ? "Start group chat" : "Start chat"}
4378
+ </button>
4379
+ </footer>
4380
+ </form>`
4381
+ );
4382
+ }
4383
+ };
4384
+ function defineTeamsNewChatDialog(registry = globalThis.customElements) {
4385
+ if (registry && !registry.get("teams-new-chat-dialog"))
4386
+ registry.define("teams-new-chat-dialog", TeamsNewChatDialog);
4387
+ }
4388
+
4389
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/chat-people-dialog.css?raw
4390
+ var chat_people_dialog_default = "h3 {\n margin: var(--office-space-2) 0 0;\n font-size: var(--office-font-size);\n}\n.rename {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: end;\n gap: var(--office-space-2);\n padding: 0;\n}\n.members {\n display: grid;\n gap: var(--office-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.members li {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-1) 0;\n}\n.name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\noffice-ui-people-picker {\n font-weight: normal;\n}\n";
4391
+
4392
+ // src/teams/app/chat-people-dialog.ts
4393
+ var TeamsChatPeopleDialog = class extends TeamsDialog {
4394
+ static styles = dialogStyles(chat_people_dialog_default);
4395
+ static properties = {
4396
+ ...TeamsDialog.properties,
4397
+ chat: { attribute: false },
4398
+ contacts: { attribute: false },
4399
+ name: { state: true },
4400
+ adding: { state: true }
4401
+ };
4402
+ constructor() {
4403
+ super();
4404
+ this.chat = null;
4405
+ this.contacts = [];
4406
+ this.name = "";
4407
+ this.adding = [];
4408
+ }
4409
+ willUpdate(changed) {
4410
+ if (changed.has("open") && this.open) {
4411
+ this.name = this.chat?.name ?? "";
4412
+ this.adding = [];
4413
+ }
4414
+ }
4415
+ render() {
4416
+ const chat = this.chat;
4417
+ if (!chat) return this.frame("People", html``);
4418
+ const group = chat.kind === "group";
4419
+ return this.frame(
4420
+ "People",
4421
+ html`<div class="body">
4422
+ ${group ? html`<form
4423
+ class="rename"
4424
+ @submit=${(e) => {
4425
+ e.preventDefault();
4426
+ this.fire("teams-chat-rename", { id: chat.id, name: this.name });
4427
+ }}
4428
+ >
4429
+ <label
4430
+ >Group name<input
4431
+ maxlength=${MAX_CHANNEL_NAME_CHARS}
4432
+ .value=${this.name}
4433
+ placeholder="Name this group chat"
4434
+ @input=${(e) => this.name = e.target.value}
4435
+ /></label>
4436
+ <button type="submit">Rename</button>
4437
+ </form>` : nothing}
4438
+ <h3>In this chat (${chat.members.length})</h3>
4439
+ <ul class="members">
4440
+ ${chat.members.map(
4441
+ (m) => html`<li>
4442
+ <office-ui-avatar
4443
+ size="sm"
4444
+ name=${m.name}
4445
+ seed=${m.id}
4446
+ presence=${m.availability}
4447
+ ></office-ui-avatar>
4448
+ <span class="name">${m.name}${m.self ? " (you)" : ""}</span>
4449
+ ${group && !m.self && chat.members.length > 1 ? html`<button
4450
+ type="button"
4451
+ aria-label=${`Remove ${m.name}`}
4452
+ @click=${() => this.fire("teams-chat-remove", { id: chat.id, userId: m.id })}
4453
+ >
4454
+ Remove
4455
+ </button>` : nothing}
4456
+ </li>`
4457
+ )}
4458
+ </ul>
4459
+ ${group ? html`<h3>Add people</h3>
4460
+ <office-ui-people-picker
4461
+ label="People to add"
4462
+ .contacts=${this.contacts}
4463
+ .exclude=${chat.members.map((m) => m.id)}
4464
+ .selected=${this.adding}
4465
+ @office-people-change=${(e) => this.adding = e.detail.selected}
4466
+ ></office-ui-people-picker>
4467
+ <footer>
4468
+ <button
4469
+ type="button"
4470
+ class="primary"
4471
+ ?disabled=${this.adding.length === 0}
4472
+ @click=${() => {
4473
+ this.fire("teams-chat-add", { id: chat.id, userIds: this.adding });
4474
+ this.adding = [];
4475
+ }}
4476
+ >
4477
+ Add to chat
4478
+ </button>
4479
+ </footer>` : html`<p>
4480
+ People cannot be added to or removed from a 1:1 chat. Start a group chat instead.
4481
+ </p>`}
4482
+ <footer>
4483
+ ${chat.canLeave ? html`<button
4484
+ type="button"
4485
+ class="danger"
4486
+ @click=${() => this.fire("teams-chat-leave", { id: chat.id })}
4487
+ >
4488
+ Leave chat
4489
+ </button>` : nothing}
4490
+ <button type="button" @click=${this.requestClose}>Done</button>
4491
+ </footer>
4492
+ </div>`
4493
+ );
4494
+ }
4495
+ };
4496
+ function defineTeamsChatPeopleDialog(registry = globalThis.customElements) {
4497
+ if (registry && !registry.get("teams-chat-people-dialog"))
4498
+ registry.define("teams-chat-people-dialog", TeamsChatPeopleDialog);
4499
+ }
4500
+
4501
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/meeting-dialog.css?raw
4502
+ var meeting_dialog_default = ".times {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--office-space-3);\n}\noffice-ui-people-picker {\n font-weight: normal;\n}\n@media (max-width: 40em) {\n .times {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n";
4503
+
4504
+ // src/teams/app/meeting-dialog.ts
4505
+ var SLOT = 30 * MINUTE_MS;
4506
+ var NOT_SAVED = "The meeting was not saved. Your details are kept here.";
4507
+ function toLocalInput(ms) {
4508
+ const d = new Date(ms);
4509
+ const p = (n) => String(n).padStart(2, "0");
4510
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
4511
+ }
4512
+ var TeamsMeetingDialog = class extends TeamsDialog {
4513
+ static styles = dialogStyles(meeting_dialog_default);
4514
+ static properties = {
4515
+ ...TeamsDialog.properties,
4516
+ meeting: { attribute: false },
4517
+ start: { type: Number },
4518
+ occurrence: { type: Number },
4519
+ contacts: { attribute: false },
4520
+ channels: { attribute: false },
4521
+ save: { attribute: false },
4522
+ meetingTitle: { state: true },
4523
+ description: { state: true },
4524
+ startText: { state: true },
4525
+ endText: { state: true },
4526
+ repeat: { state: true },
4527
+ endMode: { state: true },
4528
+ untilText: { state: true },
4529
+ countText: { state: true },
4530
+ channelId: { state: true },
4531
+ attendees: { state: true },
4532
+ issue: { state: true }
4533
+ };
4534
+ constructor() {
4535
+ super();
4536
+ this.meeting = null;
4537
+ this.start = 0;
4538
+ this.occurrence = 0;
4539
+ this.contacts = [];
4540
+ this.channels = [];
4541
+ this.save = void 0;
4542
+ this.meetingTitle = "";
4543
+ this.description = "";
4544
+ this.startText = "";
4545
+ this.endText = "";
4546
+ this.repeat = "none";
4547
+ this.endMode = "never";
4548
+ this.untilText = "";
4549
+ this.countText = "10";
4550
+ this.channelId = "";
4551
+ this.attendees = [];
4552
+ this.issue = "";
4553
+ }
4554
+ willUpdate(changed) {
4555
+ if (!changed.has("open") || !this.open) return;
4556
+ const m = this.meeting;
4557
+ const one = m && this.occurrence ? occurrenceAt(m, this.start || m.start) : null;
4558
+ const begin = one?.start ?? m?.start ?? (this.start || Math.ceil(Date.now() / SLOT) * SLOT);
4559
+ this.meetingTitle = one?.title ?? m?.title ?? "";
4560
+ this.description = one?.description ?? m?.description ?? "";
4561
+ this.startText = toLocalInput(begin);
4562
+ this.endText = toLocalInput(one?.end ?? m?.end ?? begin + SLOT);
4563
+ this.repeat = m?.repeat ?? "none";
4564
+ this.endMode = m?.count !== void 0 ? "count" : m?.until !== void 0 ? "date" : "never";
4565
+ this.countText = String(m?.count ?? 10);
4566
+ this.untilText = toLocalInput(m?.until ?? begin + 28 * 864e5).slice(0, 10);
4567
+ this.channelId = m?.channelId ?? "";
4568
+ this.attendees = m?.attendees.map((a) => a.id) ?? [];
4569
+ this.issue = "";
4570
+ }
4571
+ moveStart(value) {
4572
+ const before = new Date(this.startText).getTime();
4573
+ const after = new Date(value).getTime();
4574
+ const end = new Date(this.endText).getTime();
4575
+ this.startText = value;
4576
+ if (Number.isFinite(before) && Number.isFinite(after) && Number.isFinite(end))
4577
+ this.endText = toLocalInput(end + (after - before));
4578
+ this.issue = "";
4579
+ }
4580
+ validCount() {
4581
+ const n = Number(this.countText);
4582
+ return Number.isInteger(n) && n >= 1 && n <= MAX_RECURRENCE_COUNT;
4583
+ }
4584
+ untilTime() {
4585
+ return (/* @__PURE__ */ new Date(`${this.untilText}T23:59:59.999`)).getTime();
4586
+ }
4587
+ validUntil(start) {
4588
+ const until = this.untilTime();
4589
+ return Number.isFinite(until) && until >= start;
4590
+ }
4591
+ /** The end of the repeat to store: a count, the end of a day, or none (null clears an old end). */
4592
+ recurrenceEnd() {
4593
+ if (this.repeat === "none" || this.endMode === "never") return { count: null, until: null };
4594
+ return this.endMode === "count" ? { count: Number(this.countText), until: null } : { count: null, until: this.untilTime() };
4595
+ }
4596
+ submit(event) {
4597
+ event.preventDefault();
4598
+ const start = new Date(this.startText).getTime();
4599
+ const end = new Date(this.endText).getTime();
4600
+ if (!this.meetingTitle.trim()) this.issue = "Add a title.";
4601
+ else if (!Number.isFinite(start) || !Number.isFinite(end)) this.issue = "Pick a start and end.";
4602
+ else if (end <= start) this.issue = "The meeting must end after it starts.";
4603
+ else if (end - start > 24 * 60 * MINUTE_MS) this.issue = "A meeting can last at most 24 hours.";
4604
+ else if (this.occurrence) {
4605
+ const one = { title: this.meetingTitle, description: this.description, start, end };
4606
+ if (!this.save?.(one, this.meeting?.id, this.occurrence)) this.issue = NOT_SAVED;
4607
+ } else if (this.repeat !== "none" && this.endMode === "count" && !this.validCount())
4608
+ this.issue = `Enter a number of events from 1 to ${MAX_RECURRENCE_COUNT}.`;
4609
+ else if (this.repeat !== "none" && this.endMode === "date" && !this.validUntil(start))
4610
+ this.issue = "The series must end on or after its first event.";
4611
+ else if (!this.save?.(
4612
+ {
4613
+ title: this.meetingTitle,
4614
+ description: this.description,
4615
+ start,
4616
+ end,
4617
+ repeat: this.repeat,
4618
+ ...this.recurrenceEnd(),
4619
+ attendeeIds: this.channelId ? [] : this.attendees,
4620
+ ...this.channelId ? { channelId: this.channelId } : {}
4621
+ },
4622
+ this.meeting?.id
4623
+ ))
4624
+ this.issue = NOT_SAVED;
4625
+ }
4626
+ render() {
4627
+ const editing = this.meeting !== null;
4628
+ const single = this.occurrence !== 0;
4629
+ return this.frame(
4630
+ single ? "Edit this event" : editing ? "Edit meeting" : "New meeting",
4631
+ html`<form @submit=${this.submit}>
4632
+ <label
4633
+ >Title<input
4634
+ maxlength=${MAX_MEETING_TITLE}
4635
+ .value=${this.meetingTitle}
4636
+ placeholder="Add a title"
4637
+ @input=${(e) => {
4638
+ this.meetingTitle = e.target.value;
4639
+ this.issue = "";
4640
+ }}
4641
+ /></label>
4642
+ <div class="times">
4643
+ <label
4644
+ >Start<input
4645
+ type="datetime-local"
4646
+ .value=${this.startText}
4647
+ @change=${(e) => this.moveStart(e.target.value)}
4648
+ /></label>
4649
+ <label
4650
+ >End<input
4651
+ type="datetime-local"
4652
+ .value=${this.endText}
4653
+ @change=${(e) => {
4654
+ this.endText = e.target.value;
4655
+ this.issue = "";
4656
+ }}
4657
+ /></label>
4658
+ </div>
4659
+ ${single ? html`<small>Only this event changes. The rest of the series stays as it is.</small>` : html`
4660
+ <label
4661
+ >Repeat<select
4662
+ .value=${this.repeat}
4663
+ @change=${(e) => this.repeat = e.target.value}
4664
+ >
4665
+ <option value="none">Does not repeat</option>
4666
+ <option value="daily">Every day</option>
4667
+ <option value="weekly">Every week</option>
4668
+ </select></label
4669
+ >
4670
+ ${this.repeat === "none" ? nothing : html`<label
4671
+ >Ends<select
4672
+ .value=${this.endMode}
4673
+ @change=${(e) => this.endMode = e.target.value}
4674
+ >
4675
+ <option value="never">Never</option>
4676
+ <option value="date">On a date</option>
4677
+ <option value="count">After a number of events</option>
4678
+ </select></label
4679
+ >
4680
+ ${this.endMode === "date" ? html`<label
4681
+ >End date<input
4682
+ type="date"
4683
+ .value=${this.untilText}
4684
+ @change=${(e) => {
4685
+ this.untilText = e.target.value;
4686
+ this.issue = "";
4687
+ }}
4688
+ /></label>` : nothing}
4689
+ ${this.endMode === "count" ? html`<label
4690
+ >Number of events<input
4691
+ type="number"
4692
+ min="1"
4693
+ max=${MAX_RECURRENCE_COUNT}
4694
+ .value=${this.countText}
4695
+ @input=${(e) => {
4696
+ this.countText = e.target.value;
4697
+ this.issue = "";
4698
+ }}
4699
+ /></label>` : nothing}`}
4700
+ <label
4701
+ >Channel (optional)<select
4702
+ ?disabled=${editing}
4703
+ .value=${this.channelId}
4704
+ @change=${(e) => this.channelId = e.target.value}
4705
+ >
4706
+ <option value="">No channel: invite people</option>
4707
+ ${this.channels.map((c) => html`<option value=${c.id}># ${c.name}</option>`)}
4708
+ </select></label
4709
+ >
4710
+ ${this.channelId ? html`<small
4711
+ >A channel meeting posts a meeting card in the channel and shows in
4712
+ everyone's calendar.</small
4713
+ >` : html`<div class="field">
4714
+ Attendees
4715
+ <office-ui-people-picker
4716
+ label="Attendees"
4717
+ .contacts=${this.contacts}
4718
+ .selected=${this.attendees}
4719
+ @office-people-change=${(e) => this.attendees = e.detail.selected}
4720
+ ></office-ui-people-picker>
4721
+ </div>`}
4722
+ `}
4723
+ <label
4724
+ >Details (optional)<textarea
4725
+ maxlength=${MAX_MEETING_DESCRIPTION}
4726
+ .value=${this.description}
4727
+ placeholder="Add an agenda or notes"
4728
+ @input=${(e) => this.description = e.target.value}
4729
+ ></textarea>
4730
+ </label>
4731
+ <small
4732
+ >Times are shown in your time zone. The organizer's zone keeps repeats on schedule.</small
4733
+ >
4734
+ ${this.issue ? html`<p class="issue" role="alert">${this.issue}</p>` : nothing}
4735
+ <footer>
4736
+ <button type="button" @click=${this.requestClose}>Cancel</button>
4737
+ <button type="submit" class="primary">${editing ? "Save" : "Send"}</button>
4738
+ </footer>
4739
+ </form>`
4740
+ );
4741
+ }
4742
+ };
4743
+ function defineTeamsMeetingDialog(registry = globalThis.customElements) {
4744
+ if (registry && !registry.get("teams-meeting-dialog"))
4745
+ registry.define("teams-meeting-dialog", TeamsMeetingDialog);
4746
+ }
4747
+
4748
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/teams/app/meeting-details.css?raw
4749
+ var meeting_details_default = ".when {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n color: var(--office-foreground);\n font-weight: var(--office-font-weight-bold);\n}\nh3 {\n margin: 0 0 var(--office-space-1);\n font-size: var(--office-font-size);\n}\n.people ul {\n margin: 0;\n padding-inline-start: var(--office-space-5);\n}\n.description {\n color: var(--office-foreground);\n white-space: pre-wrap;\n}\n.scope {\n display: grid;\n gap: var(--office-space-3);\n}\n.scope fieldset {\n display: grid;\n gap: var(--office-space-2);\n margin: 0;\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-teams-divider);\n border-radius: var(--office-radius-md);\n}\n.scope label {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n}\nfooter[hidden] {\n display: none;\n}\n";
4750
+
4751
+ // src/teams/app/meeting-details.ts
4752
+ var REPEAT_TEXT = { none: "", daily: "Repeats every day", weekly: "Repeats every week" };
4753
+ var TeamsMeetingDetails = class extends TeamsDialog {
4754
+ static styles = dialogStyles(meeting_details_default);
4755
+ static properties = {
4756
+ ...TeamsDialog.properties,
4757
+ meeting: { attribute: false },
4758
+ occurrenceStart: { type: Number },
4759
+ zone: { type: String },
4760
+ channelName: { type: String },
4761
+ pending: { state: true },
4762
+ scope: { state: true }
4763
+ };
4764
+ constructor() {
4765
+ super();
4766
+ this.meeting = null;
4767
+ this.occurrenceStart = 0;
4768
+ this.zone = "";
4769
+ this.channelName = "";
4770
+ this.pending = null;
4771
+ this.scope = "occurrence";
4772
+ }
4773
+ willUpdate(changed) {
4774
+ if (changed.has("open") || changed.has("meeting") || changed.has("occurrenceStart"))
4775
+ this.pending = null;
4776
+ }
4777
+ emit(name) {
4778
+ if (this.meeting) this.fire(name, { meetingId: this.meeting.id });
4779
+ }
4780
+ recurrenceId() {
4781
+ const m = this.meeting;
4782
+ return m ? occurrenceAt(m, this.occurrenceStart || m.start)?.recurrenceId ?? m.start : 0;
4783
+ }
4784
+ /** Edit or cancel: a repeating meeting asks which events first. */
4785
+ change(action) {
4786
+ if (this.meeting?.repeat === "none") this.finish(action, "series");
4787
+ else {
4788
+ this.pending = action;
4789
+ this.scope = "occurrence";
4790
+ }
4791
+ }
4792
+ finish(action, scope) {
4793
+ if (!this.meeting) return;
4794
+ this.fire(`teams-meeting-${action}`, {
4795
+ meetingId: this.meeting.id,
4796
+ scope,
4797
+ recurrenceId: this.recurrenceId()
4798
+ });
4799
+ this.pending = null;
4800
+ }
4801
+ endText(m) {
4802
+ const date = (ms) => new Intl.DateTimeFormat([], {
4803
+ dateStyle: "medium",
4804
+ ...this.zone ? { timeZone: this.zone } : {}
4805
+ }).format(ms);
4806
+ if (m.count !== void 0) return m.count === 1 ? ", 1 time" : `, ${m.count} times`;
4807
+ return m.until !== void 0 ? `, until ${date(m.until)}` : "";
4808
+ }
4809
+ scopeChoice(action) {
4810
+ const options = [
4811
+ ["occurrence", action === "edit" ? "This event" : "This event only"],
4812
+ ["series", action === "edit" ? "All events in the series" : "All events in the series"]
4813
+ ];
4814
+ return html`<form
4815
+ class="scope"
4816
+ @submit=${(e) => {
4817
+ e.preventDefault();
4818
+ this.finish(action, this.scope);
4819
+ }}
4820
+ >
4821
+ <fieldset>
4822
+ <legend>
4823
+ ${action === "edit" ? "Edit recurring meeting" : "Cancel recurring meeting"}
4824
+ </legend>
4825
+ ${options.map(
4826
+ ([value, label]) => html`<label
4827
+ ><input
4828
+ type="radio"
4829
+ name="scope"
4830
+ .value=${value}
4831
+ .checked=${this.scope === value}
4832
+ @change=${() => this.scope = value}
4833
+ />
4834
+ ${label}</label
4835
+ >`
4836
+ )}
4837
+ </fieldset>
4838
+ <footer>
4839
+ <button type="button" @click=${() => this.pending = null}>Back</button>
4840
+ <button type="submit" class=${action === "cancel" ? "danger" : "primary"}>
4841
+ ${action === "edit" ? "Continue" : "Cancel meeting"}
4842
+ </button>
4843
+ </footer>
4844
+ </form>`;
4845
+ }
4846
+ render() {
4847
+ const m = this.meeting;
4848
+ if (!m) return this.frame("Meeting", html``);
4849
+ const occurrence = occurrenceAt(m, this.occurrenceStart || m.start);
4850
+ const start = occurrence?.start ?? (this.occurrenceStart || m.start);
4851
+ const end = occurrence?.end ?? start + (m.end - m.start);
4852
+ const description = occurrence?.description ?? m.description;
4853
+ const hasChat = Boolean(m.chatId ?? m.channelId);
4854
+ return this.frame(
4855
+ occurrence?.title ?? m.title,
4856
+ html`<div class="body">
4857
+ <p class="when">${icon("calendar")} ${formatWhen(start, end)}</p>
4858
+ ${m.repeat !== "none" ? html`<p>${REPEAT_TEXT[m.repeat]}${this.endText(m)}</p>` : nothing}
4859
+ ${m.zone !== this.zone ? html`<p>Scheduled in the organizer's time zone, ${m.zone.replaceAll("_", " ")}.</p>` : nothing}
4860
+ <p>Organized by ${m.mine ? "you" : m.organizerName}</p>
4861
+ ${m.channelId ? html`<p>
4862
+ Channel meeting in # ${this.channelName || "a channel"}. Everyone in the channel can
4863
+ join.
4864
+ </p>` : html`<div class="people">
4865
+ <h3>Attendees (${m.attendees.length + 1})</h3>
4866
+ <ul>
4867
+ <li>${m.organizerName} (organizer)</li>
4868
+ ${m.attendees.map((a) => html`<li>${a.name}</li>`)}
4869
+ </ul>
4870
+ </div>`}
4871
+ ${description ? html`<p class="description">${description}</p>` : nothing}
4872
+ ${this.pending ? this.scopeChoice(this.pending) : nothing}
4873
+ <footer ?hidden=${this.pending !== null}>
4874
+ <button type="button" class="primary" @click=${() => this.emit("teams-meeting-join")}>
4875
+ ${m.live ? "Join now" : "Join"}
4876
+ </button>
4877
+ ${hasChat ? html`<button type="button" @click=${() => this.emit("teams-meeting-chat")}>
4878
+ Meeting chat
4879
+ </button>` : nothing}
4880
+ <button type="button" @click=${() => this.emit("teams-meeting-ics")}>Export .ics</button>
4881
+ ${m.mine ? html`<button type="button" @click=${() => this.change("edit")}>Edit</button>
4882
+ <button type="button" class="danger" @click=${() => this.change("cancel")}>
4883
+ Cancel meeting
4884
+ </button>` : nothing}
4885
+ </footer>
4886
+ </div>`,
4887
+ "Close meeting details"
4888
+ );
4889
+ }
4890
+ };
4891
+ function defineTeamsMeetingDetails(registry = globalThis.customElements) {
4892
+ if (registry && !registry.get("teams-meeting-details"))
4893
+ registry.define("teams-meeting-details", TeamsMeetingDetails);
4894
+ }
4895
+ function chatHeader(chat, on) {
4896
+ const other = chat.kind === "direct" ? chat.members.find((m) => !m.self) : void 0;
4897
+ const online = chat.members.filter((m) => !m.self && m.availability !== "offline").length;
4898
+ return html`<header class="channel-head chat-head">
4899
+ <button
4900
+ type="button"
4901
+ class="back"
4902
+ aria-label="Back to chats"
4903
+ title="Back to chats"
4904
+ @click=${on.back}
4905
+ >
4906
+ ${icon("back")}
4907
+ </button>
4908
+ ${other ? html`<office-ui-avatar
4909
+ name=${other.name}
4910
+ seed=${other.id}
4911
+ presence=${other.availability}
4912
+ ></office-ui-avatar>` : html`<span class="group-mark" aria-hidden="true"
4913
+ >${icon(chat.meetingId ? "calendar" : "users")}</span
4914
+ >`}
4915
+ <h1>${chat.title}</h1>
4916
+ <span class="topic"
4917
+ >${chat.kind === "direct" ? other?.availability ?? "" : `${chat.members.length} people${online ? `, ${online} online` : ""}`}</span
4918
+ >
4919
+ <div class="people" aria-label="People in this chat">
4920
+ ${chat.members.slice(0, 5).map(
4921
+ (m) => html`<office-ui-avatar
4922
+ size="sm"
4923
+ name=${m.name}
4924
+ seed=${m.id}
4925
+ presence=${m.availability}
4926
+ ></office-ui-avatar>`
4927
+ )}
4928
+ ${chat.members.length > 5 ? html`<span class="more">+${chat.members.length - 5}</span>` : nothing}
4929
+ </div>
4930
+ <button type="button" class="chat-people" @click=${on.people}>${icon("users")} People</button>
4931
+ <button type="button" class="meet" @click=${on.call}>
4932
+ ${chat.live ? "Join call" : "Call"}
4933
+ </button>
4934
+ </header>`;
4935
+ }
4936
+ function meetingCards(meetings, now) {
4937
+ const out = {};
4938
+ for (const m of meetings) {
4939
+ const next = occurrencesBetween(m, now, now + 400 * 864e5)[0];
4940
+ out[m.id] = {
4941
+ title: m.title,
4942
+ start: next?.start ?? m.start,
4943
+ end: next?.end ?? m.end,
4944
+ live: m.live
4945
+ };
4946
+ }
4947
+ return out;
4948
+ }
4949
+
4950
+ // src/teams/app/calendar-app.ts
4951
+ function downloadText(name, text, type = "text/calendar") {
4952
+ const doc = globalThis.document;
4953
+ if (!doc || typeof URL.createObjectURL !== "function") return false;
4954
+ const url = URL.createObjectURL(new Blob([text], { type: `${type};charset=utf-8` }));
4955
+ const link = doc.createElement("a");
4956
+ link.href = url;
4957
+ link.download = name;
4958
+ doc.body.append(link);
4959
+ link.click();
4960
+ link.remove();
4961
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
4962
+ return true;
4963
+ }
4964
+
3194
4965
  // src/teams/app/teams-app.ts
3195
4966
  var LOCAL_THEME = THEME_CSS.replaceAll(
3196
4967
  ':root[data-office-theme="dark"]',
3197
4968
  ':host([data-office-theme="dark"])'
3198
4969
  ).replaceAll(':root:not([data-office-theme="light"])', ':host([data-office-theme="system"])').replaceAll(":root", ":host([data-office-theme])");
3199
4970
  var RAIL = [
4971
+ { id: "activity", label: "Activity", icon: "bell" },
4972
+ { id: "chat", label: "Chat", icon: "message" },
3200
4973
  { id: "teams", label: "Teams", icon: "users" },
3201
- { id: "calls", label: "Calls", icon: "phone" },
3202
- { id: "files", label: "Files", icon: "folder" },
3203
- { id: "followed", label: "Followed threads", icon: "chat" },
3204
- { id: "drafts", label: "Drafts", icon: "chat" }
4974
+ { id: "calendar", label: "Calendar", icon: "calendar" },
4975
+ { id: "calls", label: "Calls", icon: "phone", secondary: true },
4976
+ { id: "files", label: "Files", icon: "folder", secondary: true },
4977
+ {
4978
+ id: "followed",
4979
+ label: "Followed threads",
4980
+ shortLabel: "Threads",
4981
+ icon: "chat",
4982
+ secondary: true
4983
+ },
4984
+ { id: "drafts", label: "Drafts", icon: "chat", secondary: true }
3205
4985
  ];
3206
4986
  var TeamsApp = class extends LitElement {
3207
4987
  static styles = [unsafeCSS(LOCAL_THEME), unsafeCSS(teams_app_default)];
@@ -3217,13 +4997,20 @@ var TeamsApp = class extends LitElement {
3217
4997
  preview: { state: true },
3218
4998
  addingTab: { state: true },
3219
4999
  creatingChannel: { state: true },
5000
+ newChat: { state: true },
5001
+ chatPeople: { state: true },
5002
+ meetingForm: { state: true },
5003
+ meetingDetails: { state: true },
3220
5004
  rail: { state: true },
3221
5005
  tab: { state: true },
3222
5006
  panel: { state: true },
3223
5007
  meeting: { state: true },
3224
5008
  settingsOpen: { state: true },
3225
- navigationOpen: { state: true },
5009
+ mobileView: { state: true },
3226
5010
  toast: { state: true },
5011
+ activityToasts: { state: true },
5012
+ activityFilter: { state: true },
5013
+ highlightId: { state: true },
3227
5014
  identity: { state: true },
3228
5015
  followedUnreadOnly: { state: true },
3229
5016
  theme: { state: true },
@@ -3231,9 +5018,18 @@ var TeamsApp = class extends LitElement {
3231
5018
  chatDensity: { state: true },
3232
5019
  showAppNames: { state: true }
3233
5020
  };
3234
- teams = new TeamsController(this, (text) => this.notify(text));
5021
+ teams = new TeamsController(
5022
+ this,
5023
+ (text) => this.notify(text),
5024
+ (item) => this.announce(item)
5025
+ );
3235
5026
  toastTimer;
5027
+ activityToastTimers = /* @__PURE__ */ new Map();
5028
+ highlightTimer;
5029
+ stopActivity;
3236
5030
  openRequest = 0;
5031
+ /** The channel last open in the Teams app, restored when you come back from Chat. */
5032
+ lastChannelId = "";
3237
5033
  retainedTab;
3238
5034
  themeStorageKey = "";
3239
5035
  popoutRequested = new URL(globalThis.location?.href ?? "https://workspace.invalid").searchParams.get(
@@ -3256,13 +5052,20 @@ var TeamsApp = class extends LitElement {
3256
5052
  this.retainedTab = void 0;
3257
5053
  this.addingTab = false;
3258
5054
  this.creatingChannel = false;
5055
+ this.newChat = false;
5056
+ this.chatPeople = false;
5057
+ this.meetingForm = null;
5058
+ this.meetingDetails = null;
3259
5059
  this.rail = "teams";
3260
5060
  this.tab = "posts";
3261
5061
  this.panel = "";
3262
5062
  this.meeting = false;
3263
5063
  this.settingsOpen = false;
3264
- this.navigationOpen = false;
5064
+ this.mobileView = "conversation";
3265
5065
  this.toast = "";
5066
+ this.activityToasts = [];
5067
+ this.activityFilter = "all";
5068
+ this.highlightId = "";
3266
5069
  this.identity = null;
3267
5070
  this.followedUnreadOnly = false;
3268
5071
  this.theme = "system";
@@ -3284,9 +5087,14 @@ var TeamsApp = class extends LitElement {
3284
5087
  defineTeamsChannelTab();
3285
5088
  defineTeamsFilesPanel();
3286
5089
  defineTeamsProfileMenu();
5090
+ defineTeamsPinnedBar();
3287
5091
  defineTeamsAddTabDialog();
3288
- defineTeamsNavigationDrawer();
3289
5092
  defineTeamsCreateChannelDialog();
5093
+ defineTeamsNewChatDialog();
5094
+ defineTeamsChatPeopleDialog();
5095
+ defineTeamsMeetingDialog();
5096
+ defineTeamsMeetingDetails();
5097
+ this.stopActivity = watchActivity(this.ownerDocument, () => this.teams.client?.noteActivity());
3290
5098
  super.connectedCallback();
3291
5099
  }
3292
5100
  // ---- lifecycle --------------------------------------------------------------------------
@@ -3315,7 +5123,8 @@ var TeamsApp = class extends LitElement {
3315
5123
  this.tab = "posts";
3316
5124
  this.addingTab = false;
3317
5125
  this.creatingChannel = false;
3318
- this.navigationOpen = false;
5126
+ this.newChat = this.chatPeople = false;
5127
+ this.meetingForm = this.meetingDetails = null;
3319
5128
  this.settingsOpen = false;
3320
5129
  this.openRequest++;
3321
5130
  const named = this.userName ? { id: this.userId || loadIdentity()?.id || crypto.randomUUID(), name: this.userName } : this.identity ?? loadIdentity();
@@ -3344,6 +5153,8 @@ var TeamsApp = class extends LitElement {
3344
5153
  updated() {
3345
5154
  this.setAttribute("data-chat-density", this.chatDensity);
3346
5155
  const state = this.teams.state;
5156
+ if (state?.channels.some((c) => c.id === state.selectedChannelId))
5157
+ this.lastChannelId = state.selectedChannelId;
3347
5158
  if (state?.thread && document.visibilityState === "visible" && this.renderRoot.querySelector(".thread-pane"))
3348
5159
  this.teams.client?.markThreadRead(state.selectedChannelId, state.thread.root.id);
3349
5160
  }
@@ -3352,6 +5163,25 @@ var TeamsApp = class extends LitElement {
3352
5163
  clearTimeout(this.toastTimer);
3353
5164
  this.toastTimer = setTimeout(() => this.toast = "", 4e3);
3354
5165
  }
5166
+ /** Deliver a notification the core allowed: a toast in a visible tab, else a desktop one. */
5167
+ announce(item) {
5168
+ const settings = this.teams.client?.getState().notificationSettings;
5169
+ if (!settings) return;
5170
+ deliverNotification(item, settings, browserDeliveryEnv(), {
5171
+ toast: (entry) => {
5172
+ this.activityToasts = [
5173
+ entry,
5174
+ ...this.activityToasts.filter((t) => t.id !== entry.id)
5175
+ ].slice(0, 3);
5176
+ clearTimeout(this.activityToastTimers.get(entry.id));
5177
+ this.activityToastTimers.set(
5178
+ entry.id,
5179
+ setTimeout(() => this.dismissToast(entry.id), 6e3)
5180
+ );
5181
+ },
5182
+ open: (entry) => entry.kind === "meeting" ? this.openMeeting(entry.messageId) : void this.openActivity(entry.id)
5183
+ });
5184
+ }
3355
5185
  // ---- intent -> client actions -----------------------------------------------------------
3356
5186
  async openFile(a, mode) {
3357
5187
  if (!this.canLeaveContent()) return;
@@ -3423,6 +5253,11 @@ var TeamsApp = class extends LitElement {
3423
5253
  this.openRequest++;
3424
5254
  this.preview = null;
3425
5255
  clearTimeout(this.toastTimer);
5256
+ for (const timer of this.activityToastTimers.values()) clearTimeout(timer);
5257
+ this.activityToastTimers.clear();
5258
+ clearTimeout(this.highlightTimer);
5259
+ this.stopActivity?.();
5260
+ this.stopActivity = void 0;
3426
5261
  super.disconnectedCallback();
3427
5262
  }
3428
5263
  createChannel() {
@@ -3436,7 +5271,7 @@ var TeamsApp = class extends LitElement {
3436
5271
  this.tab = "posts";
3437
5272
  this.rail = "teams";
3438
5273
  this.meeting = false;
3439
- void this.closeNavigation(true);
5274
+ this.mobileView = "conversation";
3440
5275
  void this.focusCreatedChannel();
3441
5276
  return true;
3442
5277
  }
@@ -3465,9 +5300,10 @@ var TeamsApp = class extends LitElement {
3465
5300
  startMeeting(channelId) {
3466
5301
  if (!this.closePreview()) return;
3467
5302
  void this.teams.client?.openCall(channelId);
3468
- void this.closeNavigation(true);
5303
+ this.mobileView = "conversation";
3469
5304
  this.meeting = true;
3470
- this.rail = "teams";
5305
+ const target = channelId ?? this.teams.state?.selectedChannelId;
5306
+ this.rail = this.teams.state?.chats.some((c) => c.id === target) ? "chat" : "teams";
3471
5307
  }
3472
5308
  selectChannel(id) {
3473
5309
  if (!this.closePreview()) return false;
@@ -3478,27 +5314,11 @@ var TeamsApp = class extends LitElement {
3478
5314
  this.teams.client?.search("");
3479
5315
  this.meeting = false;
3480
5316
  this.tab = "posts";
3481
- this.rail = "teams";
3482
- void this.closeNavigation(true);
5317
+ this.rail = this.teams.state?.chats.some((c) => c.id === id) ? "chat" : "teams";
5318
+ this.mobileView = "conversation";
3483
5319
  return true;
3484
5320
  }
3485
- async closeNavigation(navigated = false) {
3486
- if (!this.navigationOpen) return;
3487
- this.navigationOpen = false;
3488
- await this.updateComplete;
3489
- const drawer = this.renderRoot.querySelector("teams-navigation-drawer");
3490
- await drawer?.updateComplete;
3491
- if (this.navigationOpen || !this.isConnected) return;
3492
- const trigger = this.renderRoot.querySelector("[data-open-navigation]");
3493
- const heading = this.renderRoot.querySelector("main h1") ?? this.renderRoot.querySelector("main");
3494
- if (!navigated && trigger?.getClientRects().length) trigger.focus();
3495
- else if (heading) {
3496
- heading.tabIndex = -1;
3497
- heading.focus();
3498
- }
3499
- }
3500
5321
  askSettings() {
3501
- this.navigationOpen = false;
3502
5322
  this.settingsOpen = true;
3503
5323
  }
3504
5324
  async closeSettings() {
@@ -3511,6 +5331,11 @@ var TeamsApp = class extends LitElement {
3511
5331
  if (this.theme === "system") this.removeAttribute("data-office-theme");
3512
5332
  else this.setAttribute("data-office-theme", this.theme);
3513
5333
  }
5334
+ /** Which screen the compact shell shows: the channel or call list, or the content itself. */
5335
+ mobileScreen(s) {
5336
+ const listed = ["teams", "calls", "activity", "chat", "calendar"].includes(this.rail);
5337
+ return listed && this.mobileView === "list" && !(this.meeting && s.call) ? "list" : "content";
5338
+ }
3514
5339
  // ---- template ---------------------------------------------------------------------------
3515
5340
  render() {
3516
5341
  if (this.popoutRequested)
@@ -3525,6 +5350,7 @@ var TeamsApp = class extends LitElement {
3525
5350
  return html`
3526
5351
  <div
3527
5352
  class="shell"
5353
+ data-mobile-view=${this.mobileScreen(s)}
3528
5354
  @office-chat-open-tab=${(event) => {
3529
5355
  if (this.teams.state?.tabs.some((entry) => entry.id === event.detail.tabId))
3530
5356
  this.selectTab(event.detail.tabId);
@@ -3535,13 +5361,15 @@ var TeamsApp = class extends LitElement {
3535
5361
  <div class="body">
3536
5362
  <office-ui-app-rail
3537
5363
  .hideLabels=${!this.showAppNames}
3538
- .items=${RAIL.map((i) => ({ ...i, ...i.id === "calls" && s.channels.some((c) => c.live) ? { badge: s.channels.filter((c) => c.live).length } : {} }))}
5364
+ .items=${RAIL.map((i) => ({ ...i, ...i.id === "calls" && s.channels.some((c) => c.live) ? { badge: s.channels.filter((c) => c.live).length } : {}, ...i.id === "activity" && s.activityUnread ? { badge: s.activityUnread } : {}, ...i.id === "chat" && s.chats.some((c) => c.unread) ? { badge: s.chats.reduce((n, c) => n + c.unread, 0) } : {} }))}
3539
5365
  selected=${this.rail}
3540
5366
  @office-rail-select=${(e) => {
3541
5367
  if (!this.closePreview()) return;
3542
5368
  this.settingsOpen = false;
5369
+ if (e.detail.id === "teams") this.leaveChatSelection();
3543
5370
  this.rail = e.detail.id;
3544
5371
  this.meeting = false;
5372
+ this.mobileView = "list";
3545
5373
  }}
3546
5374
  ></office-ui-app-rail>
3547
5375
  <aside class="side" ?inert=${this.settingsOpen}>${this.sidebar(s)}</aside>
@@ -3579,6 +5407,8 @@ var TeamsApp = class extends LitElement {
3579
5407
  );
3580
5408
  }}
3581
5409
  .followSettings=${s.threadFollowSettings}
5410
+ .notificationSettings=${s.notificationSettings}
5411
+ @teams-settings-notifications=${(event) => this.teams.client?.setNotificationSettings(event.detail)}
3582
5412
  .userName=${s.user.name}
3583
5413
  @teams-settings-theme=${(event) => {
3584
5414
  if (!["system", "light", "dark"].includes(event.detail.theme)) return;
@@ -3608,15 +5438,9 @@ var TeamsApp = class extends LitElement {
3608
5438
  .create=${(name, description) => this.commitChannel(name, description, this.client)}
3609
5439
  @teams-create-channel-close=${() => this.creatingChannel = false}
3610
5440
  ></teams-create-channel-dialog>
3611
- <teams-navigation-drawer
3612
- .heading=${this.rail === "calls" ? "Calls" : "Teams and channels"}
3613
- ?open=${this.navigationOpen}
3614
- @teams-navigation-close=${() => void this.closeNavigation()}
3615
- >
3616
- ${this.navigationOpen ? this.sidebar(s) : nothing}
3617
- </teams-navigation-drawer>
3618
5441
  ${this.toast ? html`<div class="toast" role="status">${this.toast}</div>` : nothing}
3619
-
5442
+ ${this.activityToasts.length ? html`<div class="activity-toasts">${this.activityToasts.map((t) => this.activityToastView(t))}</div>` : nothing}
5443
+ ${this.chatDialogs(s)} ${this.calendarDialogs(s)}
3620
5444
  <teams-add-tab-dialog
3621
5445
  .open=${this.addingTab}
3622
5446
  .client=${this.teams.client}
@@ -3673,21 +5497,7 @@ var TeamsApp = class extends LitElement {
3673
5497
  topbar(s) {
3674
5498
  return html`
3675
5499
  <header class="topbar">
3676
- <div class="brand">
3677
- <button
3678
- type="button"
3679
- class="navigation-toggle"
3680
- data-open-navigation
3681
- aria-label="Open navigation"
3682
- title="Open navigation"
3683
- aria-haspopup="dialog"
3684
- aria-expanded=${String(this.navigationOpen)}
3685
- @click=${() => this.navigationOpen = true}
3686
- >
3687
- ${icon("navigation")}
3688
- </button>
3689
- <span class="logo">T</span><span>Teams</span>
3690
- </div>
5500
+ <div class="brand"><span class="logo">T</span><span>Teams</span></div>
3691
5501
  <div class="search">
3692
5502
  <input
3693
5503
  type="search"
@@ -3709,7 +5519,10 @@ var TeamsApp = class extends LitElement {
3709
5519
  }}
3710
5520
  >
3711
5521
  <strong>${h.message.authorName}</strong>
3712
- <span class="in">in # ${h.channelName}</span>
5522
+ <span class="in"
5523
+ >in
5524
+ ${s.chats.some((c) => c.id === h.channelId) ? "" : "# "}${h.channelName}</span
5525
+ >
3713
5526
  <span class="snippet">${h.message.text.slice(0, 100)}</span>
3714
5527
  </button>
3715
5528
  </li>`
@@ -3730,15 +5543,81 @@ var TeamsApp = class extends LitElement {
3730
5543
  <teams-profile-menu
3731
5544
  .state=${s}
3732
5545
  @teams-profile-status=${(event) => {
3733
- if (["available", "busy", "away"].includes(event.detail.availability))
3734
- this.teams.client?.setAvailability(event.detail.availability);
5546
+ const { availability, duration } = event.detail;
5547
+ if (isPresenceStatus(availability))
5548
+ this.teams.client?.setAvailability(
5549
+ availability,
5550
+ isStatusDuration(duration) ? duration : "never"
5551
+ );
3735
5552
  }}
5553
+ @teams-profile-message=${(event) => this.teams.client?.setStatusMessage(
5554
+ String(event.detail.message ?? ""),
5555
+ isStatusDuration(event.detail.duration) ? event.detail.duration : "never"
5556
+ )}
5557
+ @teams-profile-idle=${(event) => this.teams.client?.setIdleMinutes(Number(event.detail.minutes))}
3736
5558
  ></teams-profile-menu>
3737
5559
  </div>
3738
5560
  </header>
3739
5561
  `;
3740
5562
  }
5563
+ dismissToast(id) {
5564
+ clearTimeout(this.activityToastTimers.get(id));
5565
+ this.activityToastTimers.delete(id);
5566
+ this.activityToasts = this.activityToasts.filter((t) => t.id !== id);
5567
+ }
5568
+ activityToastView(item) {
5569
+ return html`<div class="activity-toast" role="status" data-kind=${item.kind}>
5570
+ <span class="toast-text"
5571
+ ><strong>${describeActivity(item)}</strong
5572
+ ><span class="toast-preview">${item.preview}</span></span
5573
+ >
5574
+ <button
5575
+ type="button"
5576
+ @click=${() => {
5577
+ this.dismissToast(item.id);
5578
+ if (item.kind === "meeting") this.openMeeting(item.messageId);
5579
+ else void this.openActivity(item.id);
5580
+ }}
5581
+ >
5582
+ View
5583
+ </button>
5584
+ <button type="button" aria-label="Dismiss" @click=${() => this.dismissToast(item.id)}>
5585
+ ${icon("close")}
5586
+ </button>
5587
+ </div>`;
5588
+ }
3741
5589
  sidebar(s) {
5590
+ if (this.rail === "chat")
5591
+ return html`<office-ui-chat-sidebar
5592
+ .chats=${s.chats}
5593
+ selected-id=${s.selectedChannelId}
5594
+ @office-chat-select=${(e) => this.selectChannel(e.detail.id)}
5595
+ @office-chat-new=${() => this.newChat = true}
5596
+ ></office-ui-chat-sidebar>`;
5597
+ if (this.rail === "calendar")
5598
+ return html`<office-ui-calendar-agenda
5599
+ .meetings=${s.meetings}
5600
+ .zone=${s.viewerZone}
5601
+ @office-agenda-open=${(e) => this.meetingDetails = { meetingId: e.detail.meetingId, start: e.detail.start }}
5602
+ @office-agenda-join=${(e) => void this.joinMeeting(e.detail.meetingId)}
5603
+ @office-agenda-new=${() => this.meetingForm = { meetingId: "", start: 0 }}
5604
+ ></office-ui-calendar-agenda>
5605
+ <button
5606
+ type="button"
5607
+ class="show-calendar"
5608
+ @click=${() => this.mobileView = "conversation"}
5609
+ >
5610
+ ${icon("calendar")} Open calendar
5611
+ </button>`;
5612
+ if (this.rail === "activity")
5613
+ return html`<office-ui-activity-feed
5614
+ .items=${s.activity}
5615
+ .filter=${this.activityFilter}
5616
+ @office-activity-filter=${(e) => this.activityFilter = e.detail.filter}
5617
+ @office-activity-open=${(e) => void this.openActivity(e.detail.id)}
5618
+ @office-activity-read=${(e) => this.teams.client?.markActivityRead(e.detail.id, e.detail.read)}
5619
+ @office-activity-read-all=${() => this.teams.client?.markAllActivityRead()}
5620
+ ></office-ui-activity-feed>`;
3742
5621
  if (this.rail === "calls") {
3743
5622
  const live2 = s.channels.filter((c) => c.live);
3744
5623
  return html`
@@ -3794,13 +5673,18 @@ var TeamsApp = class extends LitElement {
3794
5673
  }
3795
5674
  );
3796
5675
  if (s.call && this.meeting) return this.meetingView(s);
5676
+ if (this.rail === "calendar") return this.calendarMain(s);
5677
+ if (this.rail === "chat" && !s.chats.some((c) => c.id === s.selectedChannelId))
5678
+ return this.chatEmpty();
3797
5679
  return html`
3798
5680
  ${s.call && !this.meeting ? html`<div class="callbar">
3799
- You are in a call in # ${s.call.channelName}
5681
+ You are in a call in ${this.callHeading(s.call)}
3800
5682
  <button
3801
5683
  type="button"
3802
5684
  @click=${() => {
3803
- if (this.selectChannel(s.call.channelId)) this.meeting = true;
5685
+ if (s.call.kind === "meeting") {
5686
+ if (this.closePreview()) [this.rail, this.meeting] = ["calendar", true];
5687
+ } else if (this.selectChannel(s.call.channelId)) this.meeting = true;
3804
5688
  }}
3805
5689
  >
3806
5690
  Return to call
@@ -3811,43 +5695,57 @@ var TeamsApp = class extends LitElement {
3811
5695
  }
3812
5696
  conversation(s, compact = false) {
3813
5697
  const channel = s.channel;
5698
+ const chat = s.chats.find((x) => x.id === s.selectedChannelId);
3814
5699
  const c = this.teams.client;
3815
5700
  const compose = s.editing ?? s.replyingTo;
3816
5701
  const sharedTab = s.tabs.find((t) => t.id === this.tab);
3817
5702
  if (sharedTab) this.retainedTab = sharedTab;
3818
5703
  const activeTab = sharedTab ?? (this.retainedTab?.id === this.tab && this.retainedTab.channelId === s.selectedChannelId ? this.retainedTab : void 0);
3819
5704
  return html`
3820
- ${compact ? nothing : html`<header class="channel-head">
3821
- <h1><span class="hash">#</span> ${channel?.name ?? "No channel yet"}</h1>
3822
- <nav class="tabs" role="tablist">
3823
- ${["posts", "files"].map(
5705
+ ${compact ? nothing : chat ? chatHeader(chat, {
5706
+ back: () => this.mobileView = "list",
5707
+ call: () => this.startMeeting(),
5708
+ people: () => this.chatPeople = true
5709
+ }) : html`<header class="channel-head">
5710
+ <button
5711
+ type="button"
5712
+ class="back"
5713
+ aria-label="Back to channels"
5714
+ title="Back to channels"
5715
+ @click=${() => this.mobileView = "list"}
5716
+ >
5717
+ ${icon("back")}
5718
+ </button>
5719
+ <h1><span class="hash">#</span> ${channel?.name ?? "No channel yet"}</h1>
5720
+ <nav class="tabs" role="tablist">
5721
+ ${["posts", "files"].map(
3824
5722
  (t) => html`<button
3825
- type="button"
3826
- role="tab"
3827
- aria-selected=${String(this.tab === t)}
3828
- @click=${() => this.selectTab(t)}
3829
- >
3830
- ${t === "posts" ? "Posts" : "Shared"}
3831
- </button>`
5723
+ type="button"
5724
+ role="tab"
5725
+ aria-selected=${String(this.tab === t)}
5726
+ @click=${() => this.selectTab(t)}
5727
+ >
5728
+ ${t === "posts" ? "Posts" : "Shared"}
5729
+ </button>`
3832
5730
  )}
3833
- ${s.tabs.map((t) => html`<button type="button" role="tab" aria-selected=${String(this.tab === t.id)} @click=${() => this.selectTab(t.id)}>${t.name}</button>`)}
3834
- </nav>
3835
- <button
3836
- type="button"
3837
- aria-label="Add tab"
3838
- title="Add tab"
3839
- ?disabled=${!channel}
3840
- @click=${() => this.addingTab = !this.addingTab}
3841
- >
3842
- +
3843
- </button>
3844
- ${sharedTab ? html`<button
3845
- type="button"
3846
- data-tab-conversation
3847
- aria-label="Show tab conversation"
3848
- title="Show tab conversation"
3849
- aria-expanded=${String(s.thread?.root.tabId === sharedTab.id)}
3850
- @click=${async () => {
5731
+ ${s.tabs.map((t) => html`<button type="button" role="tab" aria-selected=${String(this.tab === t.id)} @click=${() => this.selectTab(t.id)}>${t.name}</button>`)}
5732
+ </nav>
5733
+ <button
5734
+ type="button"
5735
+ aria-label="Add tab"
5736
+ title="Add tab"
5737
+ ?disabled=${!channel}
5738
+ @click=${() => this.addingTab = !this.addingTab}
5739
+ >
5740
+ +
5741
+ </button>
5742
+ ${sharedTab ? html`<button
5743
+ type="button"
5744
+ data-tab-conversation
5745
+ aria-label="Show tab conversation"
5746
+ title="Show tab conversation"
5747
+ aria-expanded=${String(s.thread?.root.tabId === sharedTab.id)}
5748
+ @click=${async () => {
3851
5749
  if (s.thread?.root.tabId === sharedTab.id) {
3852
5750
  await this.closeThread();
3853
5751
  return;
@@ -3860,49 +5758,51 @@ var TeamsApp = class extends LitElement {
3860
5758
  await this.updateComplete;
3861
5759
  this.shadowRoot?.querySelector("[data-thread-heading]")?.focus();
3862
5760
  }}
3863
- >
3864
- ${icon("chat")}
3865
- </button>` : nothing}
3866
- ${sharedTab?.createdBy === s.user.id ? html`<office-ui-menu-button
3867
- label="Tab options"
3868
- icon="more"
3869
- icon-only
3870
- @office-command=${(event) => {
5761
+ >
5762
+ ${icon("chat")}
5763
+ </button>` : nothing}
5764
+ ${sharedTab?.createdBy === s.user.id ? html`<office-ui-menu-button
5765
+ label="Tab options"
5766
+ icon="more"
5767
+ icon-only
5768
+ @office-command=${(event) => {
3871
5769
  if (event.detail.command === "rename-tab") {
3872
5770
  const name = globalThis.prompt?.("Tab name", sharedTab.name)?.trim();
3873
5771
  if (name) c?.renameTab(sharedTab.id, name);
3874
5772
  } else if (event.detail.command === "remove-tab") {
3875
5773
  if (!this.canLeaveContent()) return;
3876
- if (globalThis.confirm?.(`Remove ${sharedTab.name} from this channel?`) === true && c?.removeTab(sharedTab.id)) {
5774
+ if (globalThis.confirm?.(
5775
+ `Remove ${sharedTab.name} from this channel?`
5776
+ ) === true && c?.removeTab(sharedTab.id)) {
3877
5777
  this.openRequest++;
3878
5778
  this.preview = null;
3879
5779
  this.tab = "posts";
3880
5780
  }
3881
5781
  }
3882
5782
  }}
3883
- ><office-ui-menu-item
3884
- label="Rename tab"
3885
- command="rename-tab"
3886
- ></office-ui-menu-item
3887
- ><office-ui-menu-item
3888
- label="Remove tab"
3889
- command="remove-tab"
3890
- ></office-ui-menu-item
3891
- ></office-ui-menu-button>` : nothing}
3892
- <span class="topic">${channel?.topic ?? ""}</span>
3893
- <div class="people" aria-label="People online">
3894
- ${s.people.slice(0, 5).map((p) => html`<office-ui-avatar size="sm" name=${p.name} seed=${p.id} color=${p.color} presence=${p.availability}></office-ui-avatar>`)}
3895
- ${s.people.length > 5 ? html`<span class="more">+${s.people.length - 5}</span>` : nothing}
3896
- </div>
3897
- <button
3898
- type="button"
3899
- class="meet"
3900
- ?disabled=${!channel}
3901
- @click=${() => this.startMeeting()}
3902
- >
3903
- ${s.channels.find((x) => x.id === s.selectedChannelId)?.live ? "Join call" : "Meet now"}
3904
- </button>
3905
- </header>`}
5783
+ ><office-ui-menu-item
5784
+ label="Rename tab"
5785
+ command="rename-tab"
5786
+ ></office-ui-menu-item
5787
+ ><office-ui-menu-item
5788
+ label="Remove tab"
5789
+ command="remove-tab"
5790
+ ></office-ui-menu-item
5791
+ ></office-ui-menu-button>` : nothing}
5792
+ <span class="topic">${channel?.topic ?? ""}</span>
5793
+ <div class="people" aria-label="People online">
5794
+ ${s.people.slice(0, 5).map((p) => html`<office-ui-avatar size="sm" name=${p.name} seed=${p.id} color=${p.color} presence=${p.availability}></office-ui-avatar>`)}
5795
+ ${s.people.length > 5 ? html`<span class="more">+${s.people.length - 5}</span>` : nothing}
5796
+ </div>
5797
+ <button
5798
+ type="button"
5799
+ class="meet"
5800
+ ?disabled=${!channel}
5801
+ @click=${() => this.startMeeting()}
5802
+ >
5803
+ ${s.channels.find((x) => x.id === s.selectedChannelId)?.live ? "Join call" : "Meet now"}
5804
+ </button>
5805
+ </header>`}
3906
5806
  ${c ? messageTransfers(s, c) : nothing}
3907
5807
  ${activeTab && !sharedTab && !compact ? html`<p role="status">This tab was removed from the channel. Your open copy remains here until you close it.</p>` : nothing}
3908
5808
  ${activeTab && !compact ? html`<div
@@ -3922,14 +5822,26 @@ var TeamsApp = class extends LitElement {
3922
5822
  s,
3923
5823
  c,
3924
5824
  (attachment) => void this.openFile(attachment),
3925
- () => void this.closeThread()
5825
+ () => void this.closeThread(),
5826
+ this.highlightId
3926
5827
  ) : nothing}
3927
5828
  </div>` : this.tab === "files" && !compact ? this.fileList(s.files, s) : html`<div class="conversation-grid" data-thread=${String(!!s.thread && !compact)}>
3928
5829
  <div class="conversation-main">
5830
+ ${compact || s.pinned.length === 0 ? nothing : html`<teams-pinned-bar
5831
+ .pinned=${s.pinned}
5832
+ @teams-pinned-open=${(e) => void this.revealMessage(e.detail.messageId)}
5833
+ @teams-pinned-unpin=${(e) => c?.unpinMessage(e.detail.messageId)}
5834
+ ></teams-pinned-bar>`}
3929
5835
  <office-ui-chat-list
3930
5836
  .messages=${compact ? s.messages : s.posts}
3931
5837
  .replyCounts=${compact ? {} : s.replyCounts}
5838
+ .pinnedIds=${s.pinned.map((p) => p.message.id)}
3932
5839
  self-id=${s.user.id}
5840
+ .highlightId=${this.highlightId}
5841
+ .meetings=${meetingCards(s.meetings, Date.now())}
5842
+ @office-chat-meeting=${(e) => e.detail.action === "join" ? void this.joinMeeting(e.detail.meetingId) : this.openMeeting(e.detail.meetingId)}
5843
+ @office-chat-pin=${(e) => c?.pinMessage(e.detail.messageId)}
5844
+ @office-chat-unpin=${(e) => c?.unpinMessage(e.detail.messageId)}
3933
5845
  @office-chat-react=${(e) => c?.toggleReaction(e.detail.messageId, e.detail.emoji)}
3934
5846
  @office-chat-thread=${(e) => void this.openThread(e.detail.messageId)}
3935
5847
  @office-chat-reply=${(e) => compact ? c?.startReply(e.detail.messageId) : void this.openThread(e.detail.messageId, "reply")}
@@ -3944,9 +5856,10 @@ var TeamsApp = class extends LitElement {
3944
5856
  .value=${s.draft.text}
3945
5857
  .files=${s.draft.files}
3946
5858
  .missingFiles=${s.draft.missingFiles}
5859
+ .mentionTargets=${s.mentionTargets}
3947
5860
  @office-chat-draft=${(e) => c?.setDraft(e.detail)}
3948
5861
  ?disabled=${!channel}
3949
- placeholder=${channel ? `Message # ${channel.name}` : "Create a channel first"}
5862
+ placeholder=${chat ? `Message ${chat.title}` : channel ? `Message # ${channel.name}` : "Create a channel first"}
3950
5863
  @office-chat-typing=${() => c?.notifyTyping()}
3951
5864
  @office-chat-cancel=${() => c?.cancelCompose()}
3952
5865
  @office-chat-send=${(e) => void c?.send(e.detail)}
@@ -3957,7 +5870,8 @@ var TeamsApp = class extends LitElement {
3957
5870
  s,
3958
5871
  c,
3959
5872
  (attachment) => void this.openFile(attachment),
3960
- () => void this.closeThread()
5873
+ () => void this.closeThread(),
5874
+ this.highlightId
3961
5875
  ) : nothing}
3962
5876
  </div>`}
3963
5877
  `;
@@ -3969,6 +5883,42 @@ var TeamsApp = class extends LitElement {
3969
5883
  this.addingTab = false;
3970
5884
  this.creatingChannel = false;
3971
5885
  }
5886
+ /** Scroll a message into view and highlight it, opening its thread when it is a reply. */
5887
+ async revealMessage(id) {
5888
+ const message = this.teams.state?.messages.find((m) => m.id === id);
5889
+ if (!message) return;
5890
+ if (message.replyTo) await this.openThread(id);
5891
+ await this.updateComplete;
5892
+ const lists = [...this.renderRoot.querySelectorAll("office-ui-chat-list")];
5893
+ await Promise.all(lists.map((list) => list.updateComplete));
5894
+ const row = lists.flatMap((list) => [
5895
+ ...list.shadowRoot?.querySelectorAll("[data-message-id]") ?? []
5896
+ ]).find((element) => element.dataset.messageId === id);
5897
+ row?.scrollIntoView({ block: "center" });
5898
+ row?.focus();
5899
+ this.highlightId = id;
5900
+ clearTimeout(this.highlightTimer);
5901
+ this.highlightTimer = setTimeout(() => this.highlightId = "", 2500);
5902
+ }
5903
+ /** Open an Activity item: its channel or thread, scrolled to the message, and mark it read. */
5904
+ async openActivity(id) {
5905
+ const c = this.teams.client;
5906
+ const item = c?.getState().activity.find((entry) => entry.id === id);
5907
+ if (!c || !item || !this.closePreview()) return;
5908
+ c.markActivityRead(id);
5909
+ this.settingsOpen = false;
5910
+ this.addingTab = false;
5911
+ this.creatingChannel = false;
5912
+ this.meeting = false;
5913
+ this.tab = "posts";
5914
+ this.mobileView = "conversation";
5915
+ c.select(item.channelId);
5916
+ c.search("");
5917
+ if (item.rootId === item.messageId) c.closeThread();
5918
+ await new Promise((resolve) => setTimeout(resolve, 0));
5919
+ await this.updateComplete;
5920
+ await this.revealMessage(item.messageId);
5921
+ }
3972
5922
  async openThread(id, action) {
3973
5923
  const client = this.teams.client;
3974
5924
  client?.openThread(id);
@@ -3978,6 +5928,166 @@ var TeamsApp = class extends LitElement {
3978
5928
  await this.updateComplete;
3979
5929
  this.shadowRoot?.querySelector("[data-thread-heading]")?.focus();
3980
5930
  }
5931
+ callHeading(call) {
5932
+ return call.kind === "channel" ? `# ${call.channelName}` : call.channelName;
5933
+ }
5934
+ /** Teams rail: if a chat is selected, go back to the channel you were in. */
5935
+ leaveChatSelection() {
5936
+ const st = this.teams.state;
5937
+ const c = this.teams.client;
5938
+ if (!st || !c || !st.chats.some((x) => x.id === st.selectedChannelId)) return;
5939
+ const target = st.channels.some((x) => x.id === this.lastChannelId) ? this.lastChannelId : st.channels[0]?.id;
5940
+ if (target) c.select(target);
5941
+ }
5942
+ chatEmpty() {
5943
+ return html`<div class="empty-main">
5944
+ <h1>Chat</h1>
5945
+ <p>Pick a chat from the list, or start a new one.</p>
5946
+ <button type="button" @click=${() => this.newChat = true}>New chat</button>
5947
+ </div>`;
5948
+ }
5949
+ commitChat(ids, name) {
5950
+ const c = this.client;
5951
+ if (!c || !this.closePreview()) return false;
5952
+ const id = c.createGroupChat(ids, name);
5953
+ if (!id) return false;
5954
+ this.newChat = false;
5955
+ this.rail = "chat";
5956
+ this.meeting = false;
5957
+ this.tab = "posts";
5958
+ this.mobileView = "conversation";
5959
+ return true;
5960
+ }
5961
+ chatDialogs(s) {
5962
+ const c = this.teams.client;
5963
+ const chat = s.chats.find((x) => x.id === s.selectedChannelId) ?? null;
5964
+ return html`
5965
+ <teams-new-chat-dialog
5966
+ ?open=${this.newChat}
5967
+ .contacts=${s.contacts}
5968
+ .create=${(ids, name) => this.commitChat(ids, name)}
5969
+ @teams-dialog-close=${() => this.newChat = false}
5970
+ ></teams-new-chat-dialog>
5971
+ <teams-chat-people-dialog
5972
+ ?open=${this.chatPeople && chat !== null}
5973
+ .chat=${chat}
5974
+ .contacts=${s.contacts}
5975
+ @teams-chat-rename=${(e) => c?.renameChat(e.detail.id, e.detail.name)}
5976
+ @teams-chat-add=${(e) => c?.addToChat(e.detail.id, e.detail.userIds)}
5977
+ @teams-chat-remove=${(e) => c?.removeFromChat(e.detail.id, e.detail.userId)}
5978
+ @teams-chat-leave=${(e) => {
5979
+ if (globalThis.confirm?.("Leave this chat? You will stop receiving its messages.") === false)
5980
+ return;
5981
+ if (c?.leaveChat(e.detail.id)) {
5982
+ this.chatPeople = false;
5983
+ this.mobileView = "list";
5984
+ }
5985
+ }}
5986
+ @teams-dialog-close=${() => this.chatPeople = false}
5987
+ ></teams-chat-people-dialog>
5988
+ `;
5989
+ }
5990
+ // ---- calendar ---------------------------------------------------------------------------
5991
+ calendarMain(s) {
5992
+ return html`
5993
+ <div class="calendar-back">
5994
+ <button type="button" @click=${() => this.mobileView = "list"}>
5995
+ ${icon("back")} Upcoming meetings
5996
+ </button>
5997
+ </div>
5998
+ <office-ui-calendar-view
5999
+ .meetings=${s.meetings}
6000
+ .zone=${s.viewerZone}
6001
+ @office-calendar-open=${(e) => this.meetingDetails = { meetingId: e.detail.meetingId, start: e.detail.start }}
6002
+ @office-calendar-new=${(e) => this.meetingForm = { meetingId: "", start: e.detail.start }}
6003
+ ></office-ui-calendar-view>
6004
+ `;
6005
+ }
6006
+ /** Open the calendar on a meeting's details, for example from its reminder. */
6007
+ openMeeting(meetingId, start = 0) {
6008
+ if (!this.closePreview()) return;
6009
+ this.settingsOpen = false;
6010
+ this.meeting = false;
6011
+ this.rail = "calendar";
6012
+ this.mobileView = "conversation";
6013
+ this.meetingDetails = { meetingId, start };
6014
+ }
6015
+ async joinMeeting(meetingId) {
6016
+ if (!this.closePreview()) return;
6017
+ this.meetingDetails = null;
6018
+ this.settingsOpen = false;
6019
+ if (await this.teams.client?.joinMeeting(meetingId)) {
6020
+ this.meeting = true;
6021
+ this.rail = "calendar";
6022
+ this.mobileView = "conversation";
6023
+ }
6024
+ }
6025
+ saveMeeting(form, id, occurrence) {
6026
+ const c = this.client;
6027
+ if (!c) return false;
6028
+ const saved = id && occurrence ? c.editOccurrence(id, occurrence, {
6029
+ start: form.start,
6030
+ end: form.end,
6031
+ title: form.title,
6032
+ ...form.description !== void 0 ? { description: form.description } : {}
6033
+ }) : id ? c.updateMeeting(id, form) : c.createMeeting(form);
6034
+ if (!saved) return false;
6035
+ this.meetingForm = null;
6036
+ return true;
6037
+ }
6038
+ calendarDialogs(s) {
6039
+ const c = this.teams.client;
6040
+ const editing = this.meetingForm?.meetingId ? s.meetings.find((m) => m.id === this.meetingForm?.meetingId) ?? null : null;
6041
+ const details = this.meetingDetails ? s.meetings.find((m) => m.id === this.meetingDetails?.meetingId) ?? null : null;
6042
+ return html`
6043
+ <teams-meeting-dialog
6044
+ ?open=${this.meetingForm !== null}
6045
+ .meeting=${editing}
6046
+ .start=${this.meetingForm?.start ?? 0}
6047
+ .occurrence=${this.meetingForm?.occurrence ?? 0}
6048
+ .contacts=${s.contacts}
6049
+ .channels=${s.channels.map((ch) => ({ id: ch.id, name: ch.name }))}
6050
+ .save=${(form, id, occurrence) => this.saveMeeting(form, id, occurrence)}
6051
+ @teams-dialog-close=${() => this.meetingForm = null}
6052
+ ></teams-meeting-dialog>
6053
+ <teams-meeting-details
6054
+ ?open=${details !== null}
6055
+ .meeting=${details}
6056
+ .occurrenceStart=${this.meetingDetails?.start ?? 0}
6057
+ .zone=${s.viewerZone}
6058
+ .channelName=${s.channels.find((ch) => ch.id === details?.channelId)?.name ?? ""}
6059
+ @teams-meeting-join=${(e) => void this.joinMeeting(e.detail.meetingId)}
6060
+ @teams-meeting-chat=${(e) => {
6061
+ const target = s.meetings.find((m) => m.id === e.detail.meetingId);
6062
+ const conversation = target?.chatId ?? target?.channelId;
6063
+ this.meetingDetails = null;
6064
+ if (conversation) this.selectChannel(conversation);
6065
+ }}
6066
+ @teams-meeting-edit=${(e) => {
6067
+ const { meetingId, scope, recurrenceId } = e.detail;
6068
+ const shown = this.meetingDetails?.start ?? 0;
6069
+ this.meetingDetails = null;
6070
+ this.meetingForm = scope === "occurrence" ? { meetingId, start: shown, occurrence: recurrenceId } : { meetingId, start: 0 };
6071
+ }}
6072
+ @teams-meeting-cancel=${(e) => {
6073
+ const { meetingId, scope, recurrenceId } = e.detail;
6074
+ if (scope === "occurrence") {
6075
+ if (c?.cancelOccurrence(meetingId, recurrenceId)) this.meetingDetails = null;
6076
+ return;
6077
+ }
6078
+ const ask = details?.repeat === "none" ? "Cancel this meeting for everyone?" : "Cancel every event in this series?";
6079
+ if (globalThis.confirm?.(ask) === false) return;
6080
+ if (c?.cancelMeeting(meetingId)) this.meetingDetails = null;
6081
+ }}
6082
+ @teams-meeting-ics=${(e) => {
6083
+ const file = c?.meetingIcs(e.detail.meetingId);
6084
+ if (file && !downloadText(file.name, file.text))
6085
+ this.notify("Could not start the download");
6086
+ }}
6087
+ @teams-dialog-close=${() => this.meetingDetails = null}
6088
+ ></teams-meeting-details>
6089
+ `;
6090
+ }
3981
6091
  async resumeDraft(context) {
3982
6092
  if (!this.closePreview() || !this.teams.client?.openDraft(context)) return;
3983
6093
  this.rail = "teams";
@@ -4046,7 +6156,7 @@ var TeamsApp = class extends LitElement {
4046
6156
  if (call.phase === "prejoin")
4047
6157
  return html`
4048
6158
  <office-ui-prejoin
4049
- heading=${`# ${call.channelName}`}
6159
+ heading=${this.callHeading(call)}
4050
6160
  name=${s.user.name}
4051
6161
  .preview=${call.preview}
4052
6162
  ?audio=${call.prejoin.audio}
@@ -4060,7 +6170,7 @@ var TeamsApp = class extends LitElement {
4060
6170
  return html`
4061
6171
  <section class="meeting">
4062
6172
  <office-ui-call-controls
4063
- heading=${`# ${call.channelName}`}
6173
+ heading=${this.callHeading(call)}
4064
6174
  started-at=${call.startedAt ?? 0}
4065
6175
  ?mic=${self?.state.audio}
4066
6176
  ?camera=${self?.state.video}
@@ -4145,22 +6255,32 @@ function createTeams(options) {
4145
6255
  var TEAMS_TAGS = [
4146
6256
  "office-ui-avatar",
4147
6257
  "office-ui-app-rail",
6258
+ "office-ui-activity-feed",
4148
6259
  "office-ui-channel-list",
4149
6260
  "office-ui-chat-list",
4150
6261
  "office-ui-chat-composer",
4151
6262
  "office-ui-prejoin",
4152
6263
  "office-ui-call-grid",
4153
- "office-ui-call-controls"
6264
+ "office-ui-call-controls",
6265
+ "office-ui-chat-sidebar",
6266
+ "office-ui-people-picker",
6267
+ "office-ui-calendar-view",
6268
+ "office-ui-calendar-agenda"
4154
6269
  ];
4155
6270
  function registerTeams() {
4156
6271
  defineAvatar();
4157
6272
  defineAppRail();
6273
+ defineActivityFeed();
4158
6274
  defineChannelList();
4159
6275
  defineChatList();
4160
6276
  defineChatComposer();
4161
6277
  definePrejoin();
4162
6278
  defineCallGrid();
4163
6279
  defineCallControls();
6280
+ defineChatSidebar();
6281
+ definePeoplePicker();
6282
+ defineCalendarView();
6283
+ defineCalendarAgenda();
4164
6284
  }
4165
6285
 
4166
- export { EMOJI, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsApp, TeamsChannelTab, TeamsContentPreview, TeamsController, TeamsElement, TeamsFilesPanel, TeamsSettings, XLSX_EDITOR_EVENTS, announceFileCommand, applyTeamsProps, blobFor, colorFor, createTeams, dayLabel, defineAppRail, defineAvatar, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, definePrejoin, defineTeamsApp, defineTeamsChannelTab, defineTeamsContentPreview, defineTeamsFilesPanel, defineTeamsSettings, downloadBytes, emit, emitEvent, formatElapsed, formatSize, formatTime, icon, listenTeamsEvents, loadConfig, loadIdentity, parseIceLines, pickFile, registerTeams, safeStorage, saveConfig, saveExtension, saveIdentity, withExtension, withStyles };
6286
+ export { EMOJI, OfficeUiActivityFeed, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCalendarAgenda, OfficeUiCalendarView, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiChatSidebar, OfficeUiPeoplePicker, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsApp, TeamsChannelTab, TeamsContentPreview, TeamsController, TeamsElement, TeamsFilesPanel, TeamsSettings, XLSX_EDITOR_EVENTS, activityTime, announceFileCommand, applyTeamsProps, blobFor, colorFor, createTeams, dayLabel, defineActivityFeed, defineAppRail, defineAvatar, defineCalendarAgenda, defineCalendarView, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, defineChatSidebar, definePeoplePicker, definePrejoin, defineTeamsApp, defineTeamsChannelTab, defineTeamsContentPreview, defineTeamsFilesPanel, defineTeamsSettings, downloadBytes, emit, emitEvent, formatElapsed, formatSize, formatTime, formatWhen, icon, listenTeamsEvents, loadConfig, loadIdentity, parseIceLines, pickFile, registerTeams, safeStorage, saveConfig, saveExtension, saveIdentity, withExtension, withStyles };