@mieweb/ui 0.11.0-dev.36 → 0.11.0-dev.38

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 (103) hide show
  1. package/agent/README.md +1 -1
  2. package/agent/agents/mieweb-ui-design.agent.md +12 -7
  3. package/agent/agents/mieweb-ui-migration.agent.md +144 -0
  4. package/agent/skills/mieweb-ui-catalog/SKILL.md +8 -6
  5. package/agent/skills/mieweb-ui-catalog/selection-guidance.md +9 -5
  6. package/dist/{chunk-TUYWCUKH.js → chunk-4LKAZW2W.js} +7 -3
  7. package/dist/chunk-4LKAZW2W.js.map +1 -0
  8. package/dist/{chunk-MNXIN4MS.js → chunk-5WA6IYZD.js} +60 -2
  9. package/dist/chunk-5WA6IYZD.js.map +1 -0
  10. package/dist/{chunk-DT3X3RBM.js → chunk-7U7SONM5.js} +3 -3
  11. package/dist/{chunk-DT3X3RBM.js.map → chunk-7U7SONM5.js.map} +1 -1
  12. package/dist/{chunk-OGFSFUIX.cjs → chunk-AHYDJN4H.cjs} +501 -120
  13. package/dist/chunk-AHYDJN4H.cjs.map +1 -0
  14. package/dist/{chunk-C4GEPUE7.js → chunk-CKECOCYJ.js} +2 -2
  15. package/dist/{chunk-C4GEPUE7.js.map → chunk-CKECOCYJ.js.map} +1 -1
  16. package/dist/{chunk-V5VPECJI.js → chunk-CRR7AE5D.js} +11 -7
  17. package/dist/chunk-CRR7AE5D.js.map +1 -0
  18. package/dist/chunk-D3FNZMOB.cjs +4 -0
  19. package/dist/{chunk-GLXTVCHN.cjs.map → chunk-D3FNZMOB.cjs.map} +1 -1
  20. package/dist/{chunk-ZK2R6UGF.cjs → chunk-DCV34MEF.cjs} +4 -4
  21. package/dist/{chunk-ZK2R6UGF.cjs.map → chunk-DCV34MEF.cjs.map} +1 -1
  22. package/dist/chunk-G4VQEQGW.js +3 -0
  23. package/dist/{chunk-RISZWWEQ.js.map → chunk-G4VQEQGW.js.map} +1 -1
  24. package/dist/{chunk-S3LTUSO5.cjs → chunk-GG3IK5TL.cjs} +7 -7
  25. package/dist/{chunk-S3LTUSO5.cjs.map → chunk-GG3IK5TL.cjs.map} +1 -1
  26. package/dist/{chunk-QHCAWDMG.cjs → chunk-HDZQLTBP.cjs} +7 -3
  27. package/dist/chunk-HDZQLTBP.cjs.map +1 -0
  28. package/dist/chunk-JCIQIR5V.js +546 -0
  29. package/dist/chunk-JCIQIR5V.js.map +1 -0
  30. package/dist/{chunk-AIO2GCTL.js → chunk-JUY2JLFL.js} +501 -120
  31. package/dist/chunk-JUY2JLFL.js.map +1 -0
  32. package/dist/{chunk-2S76TRBJ.cjs → chunk-JUYBJLPI.cjs} +4 -4
  33. package/dist/{chunk-2S76TRBJ.cjs.map → chunk-JUYBJLPI.cjs.map} +1 -1
  34. package/dist/{chunk-UDUYZDTA.js → chunk-JWMMWNTT.js} +3 -3
  35. package/dist/{chunk-UDUYZDTA.js.map → chunk-JWMMWNTT.js.map} +1 -1
  36. package/dist/{chunk-RM4CBS7Y.js → chunk-LQ22CY7X.js} +4 -4
  37. package/dist/{chunk-RM4CBS7Y.js.map → chunk-LQ22CY7X.js.map} +1 -1
  38. package/dist/{chunk-AKMTPCLC.cjs → chunk-LTCHD3K5.cjs} +2 -2
  39. package/dist/{chunk-AKMTPCLC.cjs.map → chunk-LTCHD3K5.cjs.map} +1 -1
  40. package/dist/{chunk-GDKOYVX3.cjs → chunk-NZUQL42A.cjs} +11 -7
  41. package/dist/chunk-NZUQL42A.cjs.map +1 -0
  42. package/dist/chunk-QAEAC7HO.cjs +4 -0
  43. package/dist/chunk-QAEAC7HO.cjs.map +1 -0
  44. package/dist/chunk-SCGXD334.js +3 -0
  45. package/dist/chunk-SCGXD334.js.map +1 -0
  46. package/dist/chunk-TZKBW4GA.cjs +578 -0
  47. package/dist/chunk-TZKBW4GA.cjs.map +1 -0
  48. package/dist/{chunk-47AOS5EC.cjs → chunk-XX6PNPF2.cjs} +60 -2
  49. package/dist/chunk-XX6PNPF2.cjs.map +1 -0
  50. package/dist/components/AudioPlayer/index.cjs +7 -6
  51. package/dist/components/AudioPlayer/index.js +2 -1
  52. package/dist/components/MediaEditor/index.cjs +10 -9
  53. package/dist/components/MediaEditor/index.js +5 -4
  54. package/dist/components/MediaFeed/index.cjs +8 -5
  55. package/dist/components/MediaFeed/index.d.cts +33 -0
  56. package/dist/components/MediaFeed/index.d.ts +33 -0
  57. package/dist/components/MediaFeed/index.js +7 -4
  58. package/dist/components/MediaPlayer/index.cjs +13 -6
  59. package/dist/components/MediaPlayer/index.d.cts +103 -5
  60. package/dist/components/MediaPlayer/index.d.ts +103 -5
  61. package/dist/components/MediaPlayer/index.js +6 -3
  62. package/dist/components/Slider/index.cjs +5 -5
  63. package/dist/components/Slider/index.d.cts +6 -0
  64. package/dist/components/Slider/index.d.ts +6 -0
  65. package/dist/components/Slider/index.js +1 -1
  66. package/dist/components/SliderCalculator/index.cjs +4 -4
  67. package/dist/components/SliderCalculator/index.js +2 -2
  68. package/dist/components/SuperChat/index.cjs +16 -14
  69. package/dist/components/SuperChat/index.cjs.map +1 -1
  70. package/dist/components/SuperChat/index.d.cts +2 -2
  71. package/dist/components/SuperChat/index.d.ts +2 -2
  72. package/dist/components/SuperChat/index.js +8 -6
  73. package/dist/components/SuperChat/index.js.map +1 -1
  74. package/dist/components/SuperChat/plugins/index.d.cts +2 -2
  75. package/dist/components/SuperChat/plugins/index.d.ts +2 -2
  76. package/dist/hooks/index.cjs +2 -2
  77. package/dist/hooks/index.js +1 -1
  78. package/dist/index.cjs +73 -68
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.d.cts +187 -90
  81. package/dist/index.d.ts +187 -90
  82. package/dist/index.js +14 -13
  83. package/dist/index.js.map +1 -1
  84. package/dist/styles.css +1 -1
  85. package/dist/tailwind-preset.cjs +4 -4
  86. package/dist/tailwind-preset.js +1 -1
  87. package/dist/{types-DSWbrVkM.d.cts → types-BfVolads.d.cts} +33 -0
  88. package/dist/{types-DSWbrVkM.d.ts → types-BfVolads.d.ts} +33 -0
  89. package/package.json +1 -1
  90. package/dist/chunk-47AOS5EC.cjs.map +0 -1
  91. package/dist/chunk-AIO2GCTL.js.map +0 -1
  92. package/dist/chunk-ECKIFQSD.cjs +0 -183
  93. package/dist/chunk-ECKIFQSD.cjs.map +0 -1
  94. package/dist/chunk-GDKOYVX3.cjs.map +0 -1
  95. package/dist/chunk-GLXTVCHN.cjs +0 -4
  96. package/dist/chunk-MNXIN4MS.js.map +0 -1
  97. package/dist/chunk-OGFSFUIX.cjs.map +0 -1
  98. package/dist/chunk-QHCAWDMG.cjs.map +0 -1
  99. package/dist/chunk-R5P7NAJQ.js +0 -159
  100. package/dist/chunk-R5P7NAJQ.js.map +0 -1
  101. package/dist/chunk-RISZWWEQ.js +0 -3
  102. package/dist/chunk-TUYWCUKH.js.map +0 -1
  103. package/dist/chunk-V5VPECJI.js.map +0 -1
package/agent/README.md CHANGED
@@ -22,7 +22,7 @@ governing spec: later tickets cite it instead of re-deciding.
22
22
  | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
23
  | [mieweb-ui.instructions.md](mieweb-ui.instructions.md) | The rules template. Copied verbatim to the consumer's `.github/instructions/` (VS Code Copilot auto-applies it via the `applyTo` frontmatter) and embedded, minus frontmatter, in a marked block in the consumer's `AGENTS.md` (the cross-tool convention). |
24
24
  | [init-agent.mjs](init-agent.mjs) | The `mieweb-ui` bin. Idempotent — rerun after upgrading `@mieweb/ui` to refresh all targets. `--experimental-agents` additionally installs the agent team below. |
25
- | [agents/](agents/) | Agent templates: `mieweb-ui-design` (orchestrator) + `mieweb-ui-styling` + `mieweb-ui-install` + `mieweb-ui-a11y` + `mieweb-ui-i18n`. Installed to the consumer's `.github/agents/` behind `--experimental-agents`. |
25
+ | [agents/](agents/) | Agent templates: `mieweb-ui-design` (orchestrator) + `mieweb-ui-styling` + `mieweb-ui-install` + `mieweb-ui-a11y` + `mieweb-ui-i18n` + `mieweb-ui-migration`. Installed to the consumer's `.github/agents/` behind `--experimental-agents`. |
26
26
  | [skills/](skills/) | Skill templates: `mieweb-ui-catalog`, `mieweb-ui-tokens`, `mieweb-ui-icons` (generated — `pnpm agent:skills`, never hand-edit) + `mieweb-ui-brands`, `mieweb-ui-tailwind4`, `mieweb-ui-a11y-checklist`, `mieweb-ui-i18n-checklist` (hand-written). Installed to `.github/skills/` behind `--experimental-agents`. |
27
27
  | `evals/` _(Phase 2+)_ | Golden-question eval sets per specialist; the regression harness for agent knowledge. |
28
28
 
@@ -19,6 +19,10 @@ handoffs:
19
19
  agent: MIE UI i18n Agent
20
20
  prompt: Audit the code discussed above for internationalization and RTL compliance (string externalization, locale-bundle completeness, Intl formatting, logical properties) and report findings.
21
21
  send: false
22
+ - label: Migration/compliance audit
23
+ agent: MIE UI Migration Agent
24
+ prompt: Audit the code discussed above for @mieweb/ui compliance (raw HTML elements, custom modal/card/badge patterns, hand-rolled icons, CSS foundation) and report findings with a prioritized replacement plan.
25
+ send: false
22
26
  ---
23
27
 
24
28
  # MIE UI Design Agent (orchestrator)
@@ -48,14 +52,15 @@ comparison tables) and `mieweb-ui-icons` (icon aliases and conventions):
48
52
 
49
53
  ## Route to specialists
50
54
 
51
- | Question is about… | Delegate to |
52
- | --------------------------------------------------- | -------------------- |
53
- | install / peer deps / preset / safelist / `@source` | MIE UI Install Agent |
54
- | tokens, CSS vars, Tailwind classes, brands, dark | MIE UI Styling Agent |
55
- | ARIA, keyboard nav, screen readers, contrast | MIE UI A11y Agent |
56
- | translations, locale bundles, Intl formats, RTL | MIE UI i18n Agent |
55
+ | Question is about… | Delegate to |
56
+ | --------------------------------------------------- | ---------------------- |
57
+ | install / peer deps / preset / safelist / `@source` | MIE UI Install Agent |
58
+ | tokens, CSS vars, Tailwind classes, brands, dark | MIE UI Styling Agent |
59
+ | ARIA, keyboard nav, screen readers, contrast | MIE UI A11y Agent |
60
+ | translations, locale bundles, Intl formats, RTL | MIE UI i18n Agent |
61
+ | migrating code to @mieweb/ui, raw-HTML compliance | MIE UI Migration Agent |
57
62
 
58
- More specialists (Migration, Grids, DataVis, Views,
63
+ More specialists (Grids, DataVis, Views,
59
64
  SuperChat, Rich Text) land in later phases; until one ships, answer its domain
60
65
  from the skills and the component docs, or defer (below).
61
66
 
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: MIE UI Migration Agent
3
+ description: Migration and compliance auditor for bringing codebases onto @mieweb/ui — raw-HTML-to-component pattern enforcement (badge spans, card divs, raw buttons, tables to DataVis NITRO), hand-rolled SVG to lucide-react, CSS foundation checks, and legacy-framework migration planning (Meteor/Blaze to React). Invoke to audit a file, page, or project for @mieweb/ui compliance or to plan a migration.
4
+ tools: ['search', 'codebase', 'fetch']
5
+ handoffs:
6
+ - label: Apply Fixes
7
+ agent: agent
8
+ prompt: Apply the @mieweb/ui compliance fixes identified in the audit above — replace the flagged raw elements and patterns with the prescribed @mieweb/ui components, preserving existing functionality and event handlers.
9
+ send: false
10
+ ---
11
+
12
+ # MIE UI Migration Agent
13
+
14
+ You are the migration and compliance auditor for the **@mieweb/ui** design
15
+ system. You bring legacy code into compliance: every UI element must use a
16
+ `@mieweb/ui` component when one exists — no raw `<button>`, `<input>`,
17
+ `<select>`, `<table>`, `<textarea>`, or custom modal/card/badge markup. You
18
+ are **read-only**: you audit, plan, and prescribe — the **Apply Fixes**
19
+ handoff performs the edits.
20
+
21
+ ## Your sources of truth (never inline-duplicate them)
22
+
23
+ - **`mieweb-ui-catalog` skill** — the component inventory and selection
24
+ guidance; every replacement you prescribe must exist there. It is the
25
+ raw-element → component mapping (`<button>` → `Button`, card divs →
26
+ `Card`/`CardContent`, badge spans → `Badge`, modal divs → `Modal`, …).
27
+ - **`mieweb-ui-tailwind4` skill** — the CSS foundation a migrated project
28
+ needs (`@source` directive, `@custom-variant dark`, `@theme` fallbacks,
29
+ `@tailwindcss/postcss`).
30
+ - **`mieweb-ui-icons` skill** — the lucide-react aliases that replace
31
+ hand-rolled `<svg>` and icon fonts.
32
+ - The installed `mieweb-ui.instructions.md` — the policy you enforce
33
+ (NITRO-first for tables, tokens not hardcoded colors, lucide-react icons).
34
+
35
+ ## The prime directive: patterns, not just imports
36
+
37
+ Compliance is not "does the file import @mieweb/ui". Raw HTML that serves
38
+ the same purpose as a component is a violation even when the file already
39
+ imports the library:
40
+
41
+ - `<span className="rounded-full bg-blue-100 px-2 …">` is a `Badge`.
42
+ - `<div className="rounded-lg border p-4 shadow">` is a `Card`.
43
+ - `<div className="fixed inset-0 bg-black/50">…` is a `Modal`.
44
+ - A hand-rolled `<svg>` or icon-font glyph is a `lucide-react` icon.
45
+ - Any `<table>` the user will sort, filter, page, or export is
46
+ **DataVis NITRO** (`Table` only for a few static rows; `AGGrid` is
47
+ deprecated — never migrate _to_ it).
48
+
49
+ ## Audit workflow
50
+
51
+ 1. **Scope the audit.** A file, a page, or a whole project — confirm which,
52
+ then read the files before reporting. Exclude test files from violation
53
+ counts.
54
+ 2. **Grep for raw elements** — `<button`, `<input`, `<select`, `<textarea`,
55
+ `<table`/`<thead`/`<tbody`/`<tr`/`<td`/`<th`, `<form` controls. Also grep
56
+ the **dynamic forms**: literal `type="date"` misses
57
+ `type={cond ? 'date' : 'text'}` — search `type={` and other
58
+ prop-expression variants too.
59
+ 3. **Grep for patterns** — badge/pill/tag spans, card-like divs
60
+ (`rounded…border…shadow`), modal/overlay divs (`fixed inset-0`), inline
61
+ `<svg>` and icon-font classes, hand-rolled spinners/progress bars,
62
+ custom tab/breadcrumb/pagination markup.
63
+ 4. **Check the CSS foundation** (project-level audits): identify the
64
+ consumer's CSS mode first, then audit against that mode's checklist in
65
+ the `mieweb-ui-tailwind4` skill — Tailwind 4 (`@source` for
66
+ `@mieweb/ui/dist`, `@custom-variant dark`, `@theme` fallbacks,
67
+ `@tailwindcss/postcss`), Tailwind 3 (preset + safelist + `content`
68
+ coverage), or precompiled CSS (`styles.css`/`init.css` import — never
69
+ prescribe Tailwind tooling to it). Validate the dark-mode toggle
70
+ against the `@custom-variant` actually in effect (a single-hook toggle
71
+ against `init.css` is valid); setting both `.dark` and `data-theme` is
72
+ the recommendation for new code, not a violation when one hook matches.
73
+ 5. **Build the resolution plan**, prioritized: layout shell (`AppHeader`,
74
+ `Sidebar`, `SiteFooter`) → buttons → form elements → cards & modals →
75
+ tables (NITRO) → feedback (`Badge`, `Toast`, `Spinner`) → navigation
76
+ (`Tabs`, `Breadcrumb`, `Pagination`). Replacements preserve existing
77
+ functionality and event handlers.
78
+ 6. **Detect gaps.** A pattern with no `@mieweb/ui` equivalent is not a
79
+ violation — report it with the tier recommendation: compose from existing
80
+ primitives; build locally in library style (CVA variants, `forwardRef`,
81
+ theme CSS variables, dark mode, ARIA); headless-module contract if it
82
+ renders a caller-owned collection; contribute upstream once stable.
83
+ 7. **Report, then hand off.** Findings in the format below; offer
84
+ **Apply Fixes** for the edits.
85
+
86
+ ## Audit output format
87
+
88
+ ### 🧭 @mieweb/ui compliance audit for `<scope>`
89
+
90
+ | # | Category | File | Line | Violation | Replacement |
91
+ | --- | -------- | ---- | ---- | --------- | ----------- |
92
+
93
+ Order findings: raw elements, then patterns, then CSS foundation, then gaps.
94
+ After the table, the compliance summary:
95
+
96
+ | Category | Count | Status |
97
+ | ----------------------- | ----- | ------ |
98
+ | Raw `<button>` | X | ❌ |
99
+ | Raw form elements | X | ❌ |
100
+ | Raw `<table>` | X | ❌ |
101
+ | Custom modal/card/badge | X | ❌ |
102
+ | Hand-rolled icons | X | ❌ |
103
+ | CSS foundation | — | ✅/❌ |
104
+ | Gaps (no equivalent) | N | 📝 |
105
+
106
+ Close with what passes, any judgment calls, and the **Apply Fixes** handoff.
107
+
108
+ ## Migration planning (beyond single-file audits)
109
+
110
+ For whole-app migrations (e.g. Meteor 2 + Blaze → Meteor 3 + React +
111
+ @mieweb/ui), plan in this order: scaffold → install deps → the three
112
+ critical CSS/PostCSS/Tailwind config files → verify Tailwind renders →
113
+ shared hooks (`useTheme`, `useBrand`) → layout shell → features one at a
114
+ time, auth first → verify dark mode, brand switching, and `tsc --noEmit`.
115
+ Meteor 3 specifics: all methods async, collection ops use `*Async()`
116
+ variants. Verify each migrated feature before starting the next.
117
+
118
+ ## Redirects
119
+
120
+ - "Which component should I use?" for new (non-migration) work → **MIE UI
121
+ Design Agent** (your mapping covers replacements; open-ended selection is
122
+ theirs).
123
+ - Broken styling after install, unstyled components, Tailwind wiring →
124
+ **MIE UI Install Agent** (you flag the missing foundation; they debug it).
125
+ - Token/brand/dark-mode policy questions → **MIE UI Styling Agent**.
126
+ - ARIA, keyboard navigation, focus, contrast → **MIE UI A11y Agent** (your
127
+ replacements inherit library a11y; auditing what remains is theirs).
128
+ - String externalization, locale bundles, RTL → **MIE UI i18n Agent**.
129
+
130
+ ## Rules
131
+
132
+ 1. Evidence over checklists: cite file and line for every finding; read the
133
+ code before flagging it.
134
+ 2. **Patterns, not just imports** — a compliant import list proves nothing;
135
+ grep for the raw markup the components replace.
136
+ 3. Grep dynamic forms: prop expressions (`type={…}`, spread props,
137
+ conditional classes) hide violations that literal-string greps miss.
138
+ 4. Never prescribe a component or prop you cannot verify in the catalog
139
+ skill, the published component docs (https://ui.mieweb.org), or the
140
+ package's public types — the catalog is a selection inventory, not an
141
+ API reference; a gap honestly reported beats an invented replacement.
142
+ 5. Replacements must preserve behavior — event handlers, controlled state,
143
+ form semantics. Flag any replacement that would change behavior as a
144
+ judgment call instead of silently prescribing it.
@@ -7,7 +7,7 @@ description: Component inventory and selection guidance for the @mieweb/ui desig
7
7
 
8
8
  # @mieweb/ui component catalog
9
9
 
10
- 271 cataloged components. Full docs: https://ui.mieweb.org/ (Storybook; sidebar is Tier / Family / Component, every family has an Overview page).
10
+ 274 cataloged components. Full docs: https://ui.mieweb.org/ (Storybook; sidebar is Tier / Family / Component, every family has an Overview page).
11
11
 
12
12
  For per-component "Which one?" comparison tables, read [selection-guidance.md](./selection-guidance.md) in this skill and jump to the family's section.
13
13
 
@@ -43,7 +43,7 @@ For per-component "Which one?" comparison tables, read [selection-guidance.md](.
43
43
 
44
44
  ## Inventory
45
45
 
46
- Components by tier and family. Annotations: maturity when not stable, and the import entry when it is not the root (e.g. _(`/datavis`)_).
46
+ Components by tier and family. Annotations: maturity when not stable, the import entry when it is not the root (e.g. _(`/datavis`)_), and _(not exported yet)_ for components documented in Storybook but not yet importable — do not recommend those for use.
47
47
 
48
48
  ### Foundations
49
49
 
@@ -73,14 +73,14 @@ Components by tier and family. Annotations: maturity when not stable, and the im
73
73
 
74
74
  - **Chat:** AIChat, AIChat (Voice), AIMessage, ChatComposer _(beta)_, Composer Action Ribbon (POC) _(experimental)_, ComposerModelSelector, MCPToolCall, Messaging, OzwellChat, ReconciliationPanel
75
75
  - **Dashboards:** CustomizableDashboard, Dashboard (Demo) _(experimental)_, Dashboard Widgets (Demo) _(experimental)_, DashboardCustomizePanel, DashboardWidget, QuickLinksCard, ReportDashboard
76
- - **Editors:** EmailEditor _(experimental)_, MacroLibrary _(experimental)_, Markdown, Q _(`/q`)_, RichEditor _(`/kerebron`)_, RichTextEditor, TextAreaRich _(experimental)_
76
+ - **Editors:** EmailEditor _(experimental)_, MacroLibrary _(experimental)_, Markdown, Q _(`/q`)_, RichEditor _(`/kerebron`)_, RichTextEditor, RichTextEsheet _(experimental, `/esheet`)_, TextAreaRich (Kerebron) _(experimental, `/kerebron`)_
77
77
  - **Files:** DocumentScanner, DropzoneOverlay, FileManager
78
78
  - **Media:** AudioPlayer, AudioRecorder, DictationButton _(experimental)_, MediaEditor, MediaEditor Live Demo, MediaFeed _(experimental, `/components/MediaFeed`)_, MediaPlayer, RecordButton, TranscriptView, VideoCard _(experimental)_
79
79
  - **Presentations:** Deck _(experimental, `/deck`)_, PointMap _(experimental, `/maps`)_
80
80
  - **Records:** ActionPlan _(experimental)_, ActivityFeed _(experimental)_, AssociationList _(experimental)_, FieldHistory _(experimental)_, OrgChart _(experimental, `/org-chart`)_, RecordLayout _(experimental)_
81
81
  - **SuperChat:** Conversations (List) _(`/components/SuperChat`)_, Inbox _(`/components/SuperChat`)_, SuperChat (Panel) _(`/components/SuperChat`)_
82
82
  - **Views:** BoardView _(alpha)_, CalendarView _(alpha)_, GanttView _(alpha)_, ListView _(alpha)_, ViewSet _(alpha)_, ViewSwitcher _(alpha)_
83
- - **Voice:** Hands-Free Chat, Hey Ozwell Demo, Hey Ozwell Selector _(experimental)_, Speaker Verify _(experimental)_, Transcript Player _(experimental)_, Visit Scribe, Voice Manager, Voice Setup, Wake Word
83
+ - **Voice:** Hands-Free Chat, Hey Ozwell Demo, Hey Ozwell Selector _(experimental)_, Speaker Scribe _(experimental, not exported yet)_, Speaker Verify _(experimental)_, Transcript Player _(experimental)_, Visit Scribe, Voice Manager, Voice Setup, Wake Word
84
84
 
85
85
  ### Templates
86
86
 
@@ -107,7 +107,7 @@ Components by tier and family. Annotations: maturity when not stable, and the im
107
107
 
108
108
  ### Deprecated
109
109
 
110
- - AGGrid _(deprecated, `/ag-grid`)_, AGGrid Enhanced _(deprecated, `/ag-grid`)_
110
+ - AGGrid _(deprecated, `/ag-grid`)_, AGGrid Enhanced _(deprecated, `/ag-grid`)_, TextAreaRich (Legacy) _(deprecated)_
111
111
 
112
112
  ## Family policies
113
113
 
@@ -198,7 +198,9 @@ The one-line rule that governs each family. When a task touches a family, read i
198
198
 
199
199
  > **What is the stored format, and who edits it?** Markdown that people write (and maybe
200
200
  > co-write) → `RichEditor`. HTML for templated letters with merge fields and dictation →
201
- > `RichTextEditor`. HTML in a surface that should look like a plain textarea until asked →
201
+ > `RichTextEditor`. A **multi-section guided document** — referral letters, support tickets —
202
+ > where an eSheet definition supplies the sections and memory aids → `RichTextEsheet`.
203
+ > HTML in a surface that should look like a plain textarea until asked →
202
204
  > `TextAreaRich`. An **email** laid out from blocks and compiled to MJML → `EmailEditor`.
203
205
  > Markdown that is only **read** → `MarkdownRenderer`. A structured agent
204
206
  > configuration with a YAML/JSON pane → `Q`. Plain text storage with rich composing →
@@ -259,7 +259,9 @@ lists of options in [Choice inputs](https://ui.mieweb.org/?path=/docs/inputs-cho
259
259
 
260
260
  > **What is the stored format, and who edits it?** Markdown that people write (and maybe
261
261
  > co-write) → `RichEditor`. HTML for templated letters with merge fields and dictation →
262
- > `RichTextEditor`. HTML in a surface that should look like a plain textarea until asked →
262
+ > `RichTextEditor`. A **multi-section guided document** — referral letters, support tickets —
263
+ > where an eSheet definition supplies the sections and memory aids → `RichTextEsheet`.
264
+ > HTML in a surface that should look like a plain textarea until asked →
263
265
  > `TextAreaRich`. An **email** laid out from blocks and compiled to MJML → `EmailEditor`.
264
266
  > Markdown that is only **read** → `MarkdownRenderer`. A structured agent
265
267
  > configuration with a YAML/JSON pane → `Q`. Plain text storage with rich composing →
@@ -272,10 +274,12 @@ lists of options in [Choice inputs](https://ui.mieweb.org/?path=/docs/inputs-cho
272
274
  | --- | --- | --- |
273
275
  | [RichEditor](https://ui.mieweb.org/?path=/docs/editors-richeditor--docs) | The document is **Markdown**: headings, lists, tables, code; `getContent()` on submit; `collab.room` for Yjs co-editing; `CodeEditor` for source | The stored format is HTML or you need merge variables/dictation — `RichTextEditor`; the host cannot add the Kerebron peers and WASM assets |
274
276
  | [RichTextEditor](https://ui.mieweb.org/?path=/docs/editors-richtexteditor--docs) | The document is **HTML**: bold/italic/underline, lists, alignment, opt-in markdown block shortcuts (H1–H3, lists, quote), `variableGroups` inserting `{{tokens}}`, Web Speech dictation; no peers | You need Markdown, real undo history, links/tables/code or collaboration — `RichEditor`; plain text — `Textarea`; a textarea-like surface with the toolbar hidden — `TextAreaRich` |
275
- | [TextAreaRich](https://ui.mieweb.org/?path=/docs/editors-textarearich--docs) | A **textarea-like** HTML surface: starts one Input row tall and auto-grows, toolbar hidden until asked, markdown block shortcuts, a Notion-style `+` gutter menu, `.shortcut` macros | The value must be **plain text** (form posts, bindings) — `Textarea` (this surface with a plain-text contract); a full document with an always-on toolbar, variables or dictation — `RichTextEditor` |
277
+ | [TextAreaRich](https://ui.mieweb.org/?path=/docs/editors-textarearich-kerebron--docs) | A **textarea-like** HTML surface on the Kerebron engine (`@mieweb/ui/kerebron`): starts one Input row tall and auto-grows, toolbar hidden until asked, markdown block shortcuts, a Notion-style `+` gutter menu, `.shortcut` macros | The value must be **plain text** (form posts, bindings) — `Textarea` (this surface with a plain-text contract); a full document with an always-on toolbar, variables or dictation — `RichTextEditor` |
278
+ | [RichTextEsheet](https://ui.mieweb.org/?path=/docs/editors-richtextesheet--docs) | A **multi-section guided document** — referral letters, support tickets, incident write-ups — where an eSheet definition drives section prompts, memory-aid phrases and party macros; exports portable MDY | One free-form HTML letter with merge fields and dictation — `RichTextEditor`; a single rich input — `TextAreaRich`; arbitrary native `FormDefinition` forms — EsheetRenderer (Composite forms); coded clinical visits — EncounterVisit (Encounter & orders) |
276
279
  | [EmailEditor](https://ui.mieweb.org/?path=/docs/editors-emaileditor--docs) | The document is an **email**: hero, columns, buttons, images, social, footer blocks on a drag-to-reorder canvas; `renderEmailMjml` serialises it | A single formatted body — `RichTextEditor`; sending, recipients and templates are the host's |
277
280
  | [Markdown](https://ui.mieweb.org/?path=/docs/editors-markdown--docs) | **Displaying** Markdown: sanitised HTML, code highlighting, `mermaid` / `csv` / `survey` / `html` fences, `streaming` for token-by-token AI output | The user edits the text — `RichEditor` / `RichTextEditor`; the source is HTML |
278
281
  | [Q](https://ui.mieweb.org/?path=/docs/editors-q--docs) | Admins configure an **agent** (prompt, tools, telephony) through a schema-generated form and see the YAML/JSON; `onSubmit(yaml)`, `onConfigChange` | Free-form prose — `RichEditor`; general clinical/business forms — ESheet (Composite forms) |
282
+ | [TextAreaRich (Legacy)](https://ui.mieweb.org/?path=/docs/deprecated-textarearich--docs) _(deprecated)_ | Never for new work — the `document.execCommand` engine; use [TextAreaRich (Kerebron)](https://ui.mieweb.org/?path=/docs/editors-textarearich-kerebron--docs) | — |
279
283
 
280
284
  ---
281
285
 
@@ -432,8 +436,8 @@ Related, in other families: `Button isLoading` (inline spinner), `ReadingProgres
432
436
  | Component | Use it when | Not when |
433
437
  | --- | --- | --- |
434
438
  | [AudioPlayer](https://ui.mieweb.org/?path=/docs/media-audioplayer--docs) | A small, themed player for one audio clip: `inline` / `compact` / `waveform` variants, `AudioPlayerRef` in **seconds**, `preload={false}` for lists | The source may be video or you want native controls and captions — `MediaPlayer`; you are capturing — `AudioRecorder` / `RecordButton` |
435
- | [MediaPlayer](https://ui.mieweb.org/?path=/docs/media-mediaplayer--docs) | Native `<audio>`/`<video>` with `controls`, `kind` inferred from the extension, `MediaPlayerRef` in **milliseconds** to sync a transcript | You want a compact styled audio control or a waveform — `AudioPlayer`; the words are edited — `MediaEditor` |
436
- | [MediaFeed](https://ui.mieweb.org/?path=/docs/media-mediafeed--docs) | A host-owned collection of clips and images with vertical browsing, one active player and an immersive viewer; also the media view of [SuperChatInbox](https://ui.mieweb.org/?path=/docs/superchat-inbox--docs) | A single clip — `MediaPlayer`; a linked video thumbnail — `VideoCard`; text discussion — the normal SuperChat thread |
439
+ | [MediaPlayer](https://ui.mieweb.org/?path=/docs/media-mediaplayer--docs) | `<audio>`/`<video>` with the browser's controls or, for video, touch-first overlay controls (`controlsStyle`); `kind` inferred from the extension; `MediaPlayerRef` in **milliseconds** | You want a compact styled audio control or a waveform — `AudioPlayer`; the words are edited — `MediaEditor` |
440
+ | [MediaFeed](https://ui.mieweb.org/?path=/docs/media-mediafeed--docs) | A host-owned collection of clips and images with vertical browsing, one active player and an immersive viewer, as cards or edge to edge (`layout`); also the media view of [SuperChatInbox](https://ui.mieweb.org/?path=/docs/superchat-inbox--docs) | A single clip — `MediaPlayer`; a linked video thumbnail — `VideoCard`; text discussion — the normal SuperChat thread |
437
441
  | [TranscriptView](https://ui.mieweb.org/?path=/docs/media-transcriptview--docs) | Read-only word- or segment-level transcript that highlights `currentTimeMs` and reports `onSeek(ms)`; speaker rows via `speakerLabels` / `mergeSameSpeaker` | The user changes words or the cut — `MediaEditor`; you have no media position to follow |
438
442
  | [MediaEditor](https://ui.mieweb.org/?path=/docs/media-mediaeditor--docs) | Word-timestamped `Transcript` the user tightens: delete/cut/paste words, split silences, filler removal, speed markers; state persisted via `onEditorStateChange` | Read-only playback — `TranscriptView` + `MediaPlayer`; segment-only timestamps (no per-word times) |
439
443
  | [MediaEditor Live Demo](https://ui.mieweb.org/?path=/docs/media-mediaeditor-live-demo--docs) | Trying `MediaEditor` on your own file: on-device Whisper transcription, then the editor with the Script panel | Anything you ship — use `MediaEditor` with your own transcription source |
@@ -649,7 +653,7 @@ executive summary (numbers as lettermark icons) and outlook, and `LeadFormSectio
649
653
  | Component | Use it when | Not when |
650
654
  | --- | --- | --- |
651
655
  | [Input](https://ui.mieweb.org/?path=/docs/text-inputs-input--docs) | One line of free text or a native type (`email`, `password`, `number`) | Phone / URL / date / fixed options — see the specialists |
652
- | [Textarea](https://ui.mieweb.org/?path=/docs/text-inputs-textarea--docs) | A sentence or more stored as **plain text** — rich formatting while composing ([TextAreaRich](https://ui.mieweb.org/?path=/docs/editors-textarearich--docs) underneath), plain string through `onChange` | The host **persists** HTML — [TextAreaRich](https://ui.mieweb.org/?path=/docs/editors-textarearich--docs); native `<textarea>` behaviours (hard `maxLength`, validation, drag-resize, floating label) — `TextAreaPlain` (documented on the Textarea page); full documents — `RichEditor` / `RichTextEditor` |
656
+ | [Textarea](https://ui.mieweb.org/?path=/docs/text-inputs-textarea--docs) | A sentence or more stored as **plain text** — rich formatting while composing ([TextAreaRich](https://ui.mieweb.org/?path=/docs/editors-textarearich-kerebron--docs) underneath), plain string through `onChange` | The host **persists** HTML — [TextAreaRich (Kerebron)](https://ui.mieweb.org/?path=/docs/editors-textarearich-kerebron--docs); native `<textarea>` behaviours (hard `maxLength`, validation, drag-resize, floating label) — `TextAreaPlain` (documented on the Textarea page); full documents — `RichEditor` / `RichTextEditor` |
653
657
  | [Label](https://ui.mieweb.org/?path=/docs/text-inputs-label--docs) | Labelling a control with no `label` prop (raw `<select>`, custom) | The control already takes `label` |
654
658
  | [PhoneInput](https://ui.mieweb.org/?path=/docs/text-inputs-phoneinput--docs) | Exactly one US/Canada number stored as 10 digits | Several typed numbers — `PhoneInputGroup`; international |
655
659
  | [PhoneInputGroup](https://ui.mieweb.org/?path=/docs/text-inputs-phoneinputgroup--docs) | A record holds several numbers and the type (cell, work, fax) matters | One number; rows need extra fields |
@@ -72,6 +72,7 @@ function useMediaTransport(options = {}) {
72
72
  };
73
73
  const onDurationChange = () => setDurationMs(toMs(element.duration));
74
74
  const onTimeUpdateEvt = () => emitTime();
75
+ const onRateChange = () => setPlaybackRateState(element.playbackRate);
75
76
  const onPlay = () => setState("playing");
76
77
  const onPlaying = () => setState("playing");
77
78
  const onPause = () => setState("paused");
@@ -94,6 +95,7 @@ function useMediaTransport(options = {}) {
94
95
  element.addEventListener("loadedmetadata", onLoadedMetadata);
95
96
  element.addEventListener("durationchange", onDurationChange);
96
97
  element.addEventListener("timeupdate", onTimeUpdateEvt);
98
+ element.addEventListener("ratechange", onRateChange);
97
99
  element.addEventListener("play", onPlay);
98
100
  element.addEventListener("playing", onPlaying);
99
101
  element.addEventListener("pause", onPause);
@@ -108,6 +110,7 @@ function useMediaTransport(options = {}) {
108
110
  element.removeEventListener("loadedmetadata", onLoadedMetadata);
109
111
  element.removeEventListener("durationchange", onDurationChange);
110
112
  element.removeEventListener("timeupdate", onTimeUpdateEvt);
113
+ element.removeEventListener("ratechange", onRateChange);
111
114
  element.removeEventListener("play", onPlay);
112
115
  element.removeEventListener("playing", onPlaying);
113
116
  element.removeEventListener("pause", onPause);
@@ -127,7 +130,8 @@ function useMediaTransport(options = {}) {
127
130
  const togglePlay = useCallback(() => {
128
131
  const el = elementRef.current;
129
132
  if (!el) return;
130
- if (el.paused) void el.play();
133
+ if (el.paused) void el.play()?.catch(() => {
134
+ });
131
135
  else el.pause();
132
136
  }, []);
133
137
  const seekToMs = useCallback((timeMs) => {
@@ -171,5 +175,5 @@ function useMediaTransport(options = {}) {
171
175
  }
172
176
 
173
177
  export { useMediaTransport };
174
- //# sourceMappingURL=chunk-TUYWCUKH.js.map
175
- //# sourceMappingURL=chunk-TUYWCUKH.js.map
178
+ //# sourceMappingURL=chunk-4LKAZW2W.js.map
179
+ //# sourceMappingURL=chunk-4LKAZW2W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useMediaTransport.ts"],"names":[],"mappings":"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","file":"chunk-4LKAZW2W.js","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type MediaTransportState =\n | 'idle'\n | 'loading'\n | 'playing'\n | 'paused'\n | 'error';\n\nexport interface UseMediaTransportOptions {\n /** Fired on every native `timeupdate` and on metadata load, in milliseconds. */\n onTimeUpdate?: (currentTimeMs: number, durationMs: number) => void;\n /** Fired whenever the derived transport state changes. */\n onStateChange?: (state: MediaTransportState) => void;\n /** Fired when playback reaches the end. */\n onEnded?: () => void;\n /** Fired on a media error, with a descriptive Error. */\n onError?: (error: Error) => void;\n /** Initial playback rate (default `1`). Kept in sync with the element. */\n initialRate?: number;\n /** Transport state to enter when playback ends (default `'paused'`). */\n endedState?: Extract<MediaTransportState, 'idle' | 'paused'>;\n /** Reset the position to `0` when playback ends (default `false`). */\n resetTimeOnEnd?: boolean;\n /** Human-readable label used in the error message (default `'media'`). */\n errorLabel?: string;\n}\n\nexport interface UseMediaTransportReturn {\n /**\n * Callback ref for the `<audio>`/`<video>` element to control. Pass this to\n * the element's `ref` (or call it imperatively). Transport listeners attach\n * when an element mounts and detach when it unmounts.\n */\n setMediaElement: (element: HTMLMediaElement | null) => void;\n /** The currently attached media element, or `null`. */\n mediaElement: HTMLMediaElement | null;\n\n /** Derived transport state. */\n state: MediaTransportState;\n /** Current playback position in milliseconds. */\n currentTimeMs: number;\n /** Media duration in milliseconds (`0` until known / non-finite). */\n durationMs: number;\n /** Current playback rate. */\n playbackRate: number;\n /** `true` while `state === 'playing'`. */\n isPlaying: boolean;\n /** `true` while `state === 'loading'`. */\n isLoading: boolean;\n\n /** Start playback. */\n play: () => void;\n /** Pause playback. */\n pause: () => void;\n /** Toggle between play and pause. */\n togglePlay: () => void;\n /** Seek to a position in milliseconds. */\n seekToMs: (timeMs: number) => void;\n /** Set the playback rate multiplier. */\n setPlaybackRate: (rate: number) => void;\n /** Read the current position in milliseconds. */\n getCurrentTimeMs: () => number;\n /** Read the duration in milliseconds (`0` when unknown). */\n getDurationMs: () => number;\n /** Whether playback is currently paused (reads the element directly). */\n isPaused: () => boolean;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nconst toMs = (seconds: number): number =>\n Number.isFinite(seconds) ? seconds * 1000 : 0;\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Headless playback transport for a native `HTMLMediaElement`.\n *\n * Owns the derived transport state (idle/loading/playing/paused/error),\n * position, duration and playback rate, and exposes imperative controls — all\n * in **milliseconds**. It never creates or renders an element: pass the\n * returned {@link UseMediaTransportReturn.setMediaElement} callback to an\n * `<audio>`/`<video>` `ref` and the hook wires the native listeners.\n *\n * Shared by `MediaPlayer` (ms API) and TranscriptPlayer's `usePlayback` (seconds,\n * converted at the boundary). The WaveSurfer waveform variant is\n * out of scope; this hook targets native media transport only.\n *\n * @example\n * ```tsx\n * function Player({ src }: { src: string }) {\n * const t = useMediaTransport({ onEnded: () => console.log('done') });\n * return (\n * <div>\n * <video ref={t.setMediaElement} src={src} controls />\n * <button onClick={t.togglePlay}>{t.isPlaying ? 'Pause' : 'Play'}</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaTransport(\n options: UseMediaTransportOptions = {}\n): UseMediaTransportReturn {\n const {\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n initialRate = 1,\n endedState = 'paused',\n resetTimeOnEnd = false,\n errorLabel = 'media',\n } = options;\n\n const [element, setElementState] = useState<HTMLMediaElement | null>(null);\n const [state, setStateRaw] = useState<MediaTransportState>('idle');\n const stateRef = useRef<MediaTransportState>('idle');\n const [currentTimeMs, setCurrentTimeMs] = useState(0);\n const [durationMs, setDurationMs] = useState(0);\n const [playbackRate, setPlaybackRateState] = useState(initialRate);\n\n const elementRef = useRef<HTMLMediaElement | null>(null);\n\n // Keep the latest callbacks/options in a ref so the listener effect does not\n // re-subscribe on every render when callers pass inline functions.\n const handlers = useRef({\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n endedState,\n resetTimeOnEnd,\n errorLabel,\n });\n handlers.current = {\n onTimeUpdate,\n onStateChange,\n onEnded,\n onError,\n endedState,\n resetTimeOnEnd,\n errorLabel,\n };\n\n const setState = useCallback((next: MediaTransportState) => {\n if (stateRef.current === next) return;\n stateRef.current = next;\n setStateRaw(next);\n // Consumers often update parent state here. Keep that notification outside\n // React's state updater, which may run during render or twice in StrictMode.\n handlers.current.onStateChange?.(next);\n }, []);\n\n const setMediaElement = useCallback((el: HTMLMediaElement | null) => {\n elementRef.current = el;\n setElementState(el);\n }, []);\n\n // Attach native transport listeners whenever an element mounts.\n useEffect(() => {\n if (!element) {\n // A detached element (e.g. the source was removed) leaves nothing playing or loaded;\n // an error stays visible to hosts that swap the element for an error panel.\n if (stateRef.current !== 'error') setState('idle');\n setDurationMs(0);\n setCurrentTimeMs(0);\n return;\n }\n\n const emitTime = () => {\n const ms = toMs(element.currentTime);\n const dur = toMs(element.duration);\n setCurrentTimeMs(ms);\n handlers.current.onTimeUpdate?.(ms, dur);\n };\n\n const onLoadStart = () => setState('loading');\n // canplay can also follow a seek mid-playback; only leave the loading state here.\n const onCanPlay = () => {\n if (stateRef.current === 'loading')\n setState(element.paused ? 'idle' : 'playing');\n };\n const onLoadedMetadata = () => {\n setDurationMs(toMs(element.duration));\n emitTime();\n };\n // MediaRecorder webm often reports Infinity until the end is known.\n const onDurationChange = () => setDurationMs(toMs(element.duration));\n const onTimeUpdateEvt = () => emitTime();\n // The speed can change outside these controls (iOS's own full screen player).\n const onRateChange = () => setPlaybackRateState(element.playbackRate);\n const onPlay = () => setState('playing');\n const onPlaying = () => setState('playing');\n const onPause = () => setState('paused');\n const onEndedEvt = () => {\n setState(handlers.current.endedState);\n if (handlers.current.resetTimeOnEnd) {\n element.currentTime = 0;\n setCurrentTimeMs(0);\n }\n handlers.current.onEnded?.();\n };\n const onErrorEvt = () => {\n setState('error');\n handlers.current.onError?.(\n new Error(`Failed to load ${handlers.current.errorLabel}`)\n );\n };\n\n element.addEventListener('loadstart', onLoadStart);\n element.addEventListener('canplay', onCanPlay);\n element.addEventListener('loadedmetadata', onLoadedMetadata);\n element.addEventListener('durationchange', onDurationChange);\n element.addEventListener('timeupdate', onTimeUpdateEvt);\n element.addEventListener('ratechange', onRateChange);\n element.addEventListener('play', onPlay);\n element.addEventListener('playing', onPlaying);\n element.addEventListener('pause', onPause);\n element.addEventListener('ended', onEndedEvt);\n element.addEventListener('error', onErrorEvt);\n\n // Sync on attach; a new element must not inherit the previous one's duration or play state.\n setDurationMs(toMs(element.duration));\n if (element.paused && stateRef.current === 'playing') setState('paused');\n // e.g. a 404 that failed before hydration attached these listeners.\n if (element.error) onErrorEvt();\n\n return () => {\n element.removeEventListener('loadstart', onLoadStart);\n element.removeEventListener('canplay', onCanPlay);\n element.removeEventListener('loadedmetadata', onLoadedMetadata);\n element.removeEventListener('durationchange', onDurationChange);\n element.removeEventListener('timeupdate', onTimeUpdateEvt);\n element.removeEventListener('ratechange', onRateChange);\n element.removeEventListener('play', onPlay);\n element.removeEventListener('playing', onPlaying);\n element.removeEventListener('pause', onPause);\n element.removeEventListener('ended', onEndedEvt);\n element.removeEventListener('error', onErrorEvt);\n };\n }, [element, setState]);\n\n // Keep the element's playback rate in sync with hook state.\n useEffect(() => {\n if (element) element.playbackRate = playbackRate;\n }, [element, playbackRate]);\n\n const play = useCallback(() => {\n void elementRef.current?.play();\n }, []);\n\n const pause = useCallback(() => {\n elementRef.current?.pause();\n }, []);\n\n const togglePlay = useCallback(() => {\n const el = elementRef.current;\n if (!el) return;\n // A refused start (autoplay policy) leaves it paused; nothing to report.\n if (el.paused) void el.play()?.catch(() => {});\n else el.pause();\n }, []);\n\n const seekToMs = useCallback((timeMs: number) => {\n const el = elementRef.current;\n if (el) {\n el.currentTime = timeMs / 1000;\n setCurrentTimeMs(timeMs);\n }\n }, []);\n\n const setPlaybackRate = useCallback((rate: number) => {\n setPlaybackRateState(rate);\n if (elementRef.current) elementRef.current.playbackRate = rate;\n }, []);\n\n const getCurrentTimeMs = useCallback(\n () => toMs(elementRef.current?.currentTime ?? 0),\n []\n );\n\n const getDurationMs = useCallback(\n () => toMs(elementRef.current?.duration ?? 0),\n []\n );\n\n const isPaused = useCallback(() => elementRef.current?.paused ?? true, []);\n\n return {\n setMediaElement,\n mediaElement: element,\n state,\n currentTimeMs,\n durationMs,\n playbackRate,\n isPlaying: state === 'playing',\n isLoading: state === 'loading',\n play,\n pause,\n togglePlay,\n seekToMs,\n setPlaybackRate,\n getCurrentTimeMs,\n getDurationMs,\n isPaused,\n };\n}\n"]}
@@ -87,6 +87,64 @@ var miewebUISafelist = [
87
87
  // MediaFeed vertical snapping, bounded captions, fullscreen viewport and
88
88
  // SuperChat attachment previews for consumers that do not scan package code.
89
89
  "h-[min(48rem,80dvh)]",
90
+ // MediaPlayer overlay controls and MediaFeed's overlay layout: scrims,
91
+ // round overlay buttons, pointer-aware sizes and the narrow-player
92
+ // container query and pointer variants (these need Tailwind 4.1).
93
+ "@container",
94
+ "w-fit",
95
+ // Previous/Next at the top end on a short screen (a phone held sideways).
96
+ "[@media(max-height:30rem)]:top-2",
97
+ "[@media(max-height:30rem)]:translate-y-0",
98
+ "via-black/30",
99
+ "pb-3",
100
+ "max-w-xl",
101
+ "px-14",
102
+ "dark:bg-white/30",
103
+ "@max-md:basis-full",
104
+ "@max-md:me-auto",
105
+ "@max-md:mx-2",
106
+ "@max-md:order-first",
107
+ "@max-xs:hidden",
108
+ "@max-xs:pt-2",
109
+ "[text-shadow:0_1px_2px_rgb(0_0_0/0.6)]",
110
+ "active:bg-black/75",
111
+ "backdrop-blur-sm",
112
+ "bg-black",
113
+ "bg-black/45",
114
+ "bg-gradient-to-b",
115
+ "dark:active:bg-black/75",
116
+ "dark:hover:bg-black/65",
117
+ "fill-current",
118
+ "focus-visible:-outline-offset-2",
119
+ "focus-visible:outline-2",
120
+ "focus-visible:outline-white/70",
121
+ "focus-visible:ring-offset-0",
122
+ "focus-visible:ring-white",
123
+ "from-black/75",
124
+ "from-black/55",
125
+ "via-black/55",
126
+ "gap-x-1",
127
+ "hover:bg-black/65",
128
+ "leading-5",
129
+ "mx-1",
130
+ "pb-10",
131
+ "pointer-coarse:[&_[data-slot=slider-track-wrapper]]:py-4",
132
+ "pointer-events-auto",
133
+ "pointer-fine:h-14",
134
+ "pointer-fine:h-9",
135
+ "pointer-fine:w-14",
136
+ "pointer-fine:w-9",
137
+ "sm:px-2",
138
+ "snap-always",
139
+ "via-black/40",
140
+ "[&_[data-slot=media-controls-bar]]:bg-none",
141
+ "[&_[data-slot=media-controls-bar]]:bg-black/55",
142
+ "pt-16",
143
+ "ring-white/70",
144
+ "self-stretch",
145
+ // MediaPlayer's box with the browser's controls: the video lays out as
146
+ // if it weren't there.
147
+ "contents",
90
148
  "h-dvh",
91
149
  "sm:h-[calc(100dvh-2rem)]",
92
150
  "max-h-[40%]",
@@ -1919,5 +1977,5 @@ var miewebUIPreset = {
1919
1977
  var tailwind_preset_default = miewebUIPreset;
1920
1978
 
1921
1979
  export { miewebUIPreset, miewebUISafelist, tailwind_preset_default };
1922
- //# sourceMappingURL=chunk-MNXIN4MS.js.map
1923
- //# sourceMappingURL=chunk-MNXIN4MS.js.map
1980
+ //# sourceMappingURL=chunk-5WA6IYZD.js.map
1981
+ //# sourceMappingURL=chunk-5WA6IYZD.js.map