@open-mercato/ui 0.6.4-develop.4371.1.8f3030407e → 0.6.4

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 (133) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/AGENTS.md +2 -1
  3. package/dist/backend/AppShell.js +6 -0
  4. package/dist/backend/AppShell.js.map +2 -2
  5. package/dist/backend/CrudForm.js +212 -85
  6. package/dist/backend/CrudForm.js.map +3 -3
  7. package/dist/backend/DataTable.js +77 -26
  8. package/dist/backend/DataTable.js.map +3 -3
  9. package/dist/backend/FlashMessages.js +21 -2
  10. package/dist/backend/FlashMessages.js.map +2 -2
  11. package/dist/backend/JsonBuilder.js +64 -41
  12. package/dist/backend/JsonBuilder.js.map +2 -2
  13. package/dist/backend/PerspectiveSidebar.js +8 -1
  14. package/dist/backend/PerspectiveSidebar.js.map +2 -2
  15. package/dist/backend/RowActions.js +1 -1
  16. package/dist/backend/RowActions.js.map +2 -2
  17. package/dist/backend/conflicts/RecordConflictBanner.js +73 -0
  18. package/dist/backend/conflicts/RecordConflictBanner.js.map +7 -0
  19. package/dist/backend/conflicts/index.js +32 -0
  20. package/dist/backend/conflicts/index.js.map +7 -0
  21. package/dist/backend/conflicts/store.js +50 -0
  22. package/dist/backend/conflicts/store.js.map +7 -0
  23. package/dist/backend/detail/ActivitiesSection.js +1 -0
  24. package/dist/backend/detail/ActivitiesSection.js.map +2 -2
  25. package/dist/backend/detail/InlineEditors.js +2 -51
  26. package/dist/backend/detail/InlineEditors.js.map +3 -3
  27. package/dist/backend/detail/RecordNotFoundState.js +10 -5
  28. package/dist/backend/detail/RecordNotFoundState.js.map +2 -2
  29. package/dist/backend/filters/AdvancedFilterPanel.js +14 -0
  30. package/dist/backend/filters/AdvancedFilterPanel.js.map +2 -2
  31. package/dist/backend/filters/FilteredEmptyResults.js +12 -4
  32. package/dist/backend/filters/FilteredEmptyResults.js.map +2 -2
  33. package/dist/backend/filters/ListEmptyState.js +44 -0
  34. package/dist/backend/filters/ListEmptyState.js.map +7 -0
  35. package/dist/backend/filters/SearchEmptyResults.js +28 -0
  36. package/dist/backend/filters/SearchEmptyResults.js.map +7 -0
  37. package/dist/backend/icons/lucideRegistry.generated.js +8 -0
  38. package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
  39. package/dist/backend/injection/useGuardedMutation.js +8 -1
  40. package/dist/backend/injection/useGuardedMutation.js.map +2 -2
  41. package/dist/backend/inputs/MarkdownField.js +20 -0
  42. package/dist/backend/inputs/MarkdownField.js.map +7 -0
  43. package/dist/backend/inputs/MdxEditorImpl.js +98 -0
  44. package/dist/backend/inputs/MdxEditorImpl.js.map +7 -0
  45. package/dist/backend/inputs/SwitchableMarkdownInput.js +6 -38
  46. package/dist/backend/inputs/SwitchableMarkdownInput.js.map +2 -2
  47. package/dist/backend/markdown/markdownToPlainText.js +29 -0
  48. package/dist/backend/markdown/markdownToPlainText.js.map +2 -2
  49. package/dist/backend/messages/EmailThreadsPanel.js +212 -0
  50. package/dist/backend/messages/EmailThreadsPanel.js.map +7 -0
  51. package/dist/backend/messages/index.js +4 -0
  52. package/dist/backend/messages/index.js.map +2 -2
  53. package/dist/backend/messages/mergeOptimisticEmailThreads.js +55 -0
  54. package/dist/backend/messages/mergeOptimisticEmailThreads.js.map +7 -0
  55. package/dist/backend/notifications/NotificationDispatcher.js +1 -0
  56. package/dist/backend/notifications/NotificationDispatcher.js.map +2 -2
  57. package/dist/backend/notifications/useNotificationsPoll.js +7 -0
  58. package/dist/backend/notifications/useNotificationsPoll.js.map +2 -2
  59. package/dist/backend/notifications/useNotificationsSse.js +8 -0
  60. package/dist/backend/notifications/useNotificationsSse.js.map +2 -2
  61. package/dist/backend/sidebar/SidebarCustomizationEditor.js +23 -7
  62. package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
  63. package/dist/backend/utils/optimisticLock.js +34 -0
  64. package/dist/backend/utils/optimisticLock.js.map +7 -0
  65. package/dist/portal/hooks/useTenantContext.js +1 -1
  66. package/dist/portal/hooks/useTenantContext.js.map +2 -2
  67. package/dist/primitives/alert.js.map +1 -1
  68. package/dist/primitives/checkbox.js +2 -1
  69. package/dist/primitives/checkbox.js.map +2 -2
  70. package/dist/types/css.d.js +1 -0
  71. package/dist/types/css.d.js.map +7 -0
  72. package/package.json +17 -13
  73. package/src/backend/AppShell.tsx +10 -0
  74. package/src/backend/CrudForm.tsx +320 -97
  75. package/src/backend/DataTable.tsx +113 -34
  76. package/src/backend/FlashMessages.tsx +31 -2
  77. package/src/backend/JsonBuilder.tsx +79 -26
  78. package/src/backend/PerspectiveSidebar.tsx +8 -1
  79. package/src/backend/RowActions.tsx +1 -1
  80. package/src/backend/__tests__/CrudForm.custom-fields.test.tsx +0 -1
  81. package/src/backend/__tests__/CrudForm.datetime.test.tsx +0 -1
  82. package/src/backend/__tests__/CrudForm.defaults.test.tsx +0 -1
  83. package/src/backend/__tests__/CrudForm.dotPath.test.tsx +311 -0
  84. package/src/backend/__tests__/CrudForm.doubleSubmit.test.tsx +0 -1
  85. package/src/backend/__tests__/CrudForm.navigation.test.tsx +120 -1
  86. package/src/backend/__tests__/CrudForm.optimisticLock.test.tsx +287 -0
  87. package/src/backend/__tests__/CrudForm.render.test.tsx +74 -2
  88. package/src/backend/__tests__/CrudForm.sortable.test.tsx +0 -1
  89. package/src/backend/__tests__/CrudForm.transformData.test.tsx +0 -1
  90. package/src/backend/__tests__/CrudForm.unsavedNavigation.test.tsx +51 -3
  91. package/src/backend/__tests__/CrudForm.validation.test.tsx +0 -1
  92. package/src/backend/__tests__/CustomDataSection.test.tsx +0 -1
  93. package/src/backend/__tests__/FlashMessages.test.tsx +47 -0
  94. package/src/backend/__tests__/JsonBuilder.test.tsx +135 -0
  95. package/src/backend/__tests__/lazy-heavy-libraries.test.ts +16 -3
  96. package/src/backend/conflicts/RecordConflictBanner.tsx +77 -0
  97. package/src/backend/conflicts/__tests__/RecordConflictBanner.test.tsx +61 -0
  98. package/src/backend/conflicts/__tests__/store.test.ts +78 -0
  99. package/src/backend/conflicts/index.ts +49 -0
  100. package/src/backend/conflicts/store.ts +94 -0
  101. package/src/backend/detail/ActivitiesSection.tsx +1 -0
  102. package/src/backend/detail/InlineEditors.tsx +14 -76
  103. package/src/backend/detail/RecordNotFoundState.tsx +13 -6
  104. package/src/backend/detail/__tests__/RecordNotFoundState.test.tsx +80 -0
  105. package/src/backend/filters/AdvancedFilterPanel.tsx +27 -0
  106. package/src/backend/filters/FilteredEmptyResults.tsx +23 -10
  107. package/src/backend/filters/ListEmptyState.tsx +69 -0
  108. package/src/backend/filters/SearchEmptyResults.tsx +42 -0
  109. package/src/backend/filters/__tests__/AdvancedFilterPanel.test.tsx +98 -0
  110. package/src/backend/filters/__tests__/ListEmptyState.test.tsx +53 -0
  111. package/src/backend/icons/lucideRegistry.generated.tsx +8 -0
  112. package/src/backend/injection/__tests__/useGuardedMutation.optimisticLock.test.tsx +118 -0
  113. package/src/backend/injection/useGuardedMutation.ts +16 -1
  114. package/src/backend/inputs/MarkdownField.tsx +34 -0
  115. package/src/backend/inputs/MdxEditorImpl.tsx +113 -0
  116. package/src/backend/inputs/SwitchableMarkdownInput.tsx +5 -54
  117. package/src/backend/markdown/__tests__/markdownToPlainText.test.ts +13 -0
  118. package/src/backend/markdown/markdownToPlainText.ts +41 -0
  119. package/src/backend/messages/EmailThreadsPanel.tsx +351 -0
  120. package/src/backend/messages/__tests__/mergeOptimisticEmailThreads.test.ts +126 -0
  121. package/src/backend/messages/index.ts +12 -0
  122. package/src/backend/messages/mergeOptimisticEmailThreads.ts +84 -0
  123. package/src/backend/notifications/NotificationDispatcher.ts +2 -0
  124. package/src/backend/notifications/useNotificationsPoll.ts +5 -0
  125. package/src/backend/notifications/useNotificationsSse.ts +6 -0
  126. package/src/backend/sidebar/SidebarCustomizationEditor.tsx +35 -11
  127. package/src/backend/utils/__tests__/optimisticLock.test.ts +126 -0
  128. package/src/backend/utils/optimisticLock.ts +70 -0
  129. package/src/portal/hooks/useTenantContext.ts +7 -2
  130. package/src/primitives/__tests__/checkbox.test.tsx +28 -0
  131. package/src/primitives/alert.tsx +1 -1
  132. package/src/primitives/checkbox.tsx +2 -1
  133. package/src/types/css.d.ts +5 -0
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { SearchX, X } from "lucide-react";
4
+ import { Button } from "../../primitives/button.js";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ function SearchEmptyResults({ query, entityNamePlural, onClearSearch }) {
7
+ const t = useT();
8
+ const entity = entityNamePlural ?? t("ui.dataTable.search.empty.genericEntity", "results");
9
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full max-w-md flex-col items-center gap-4 py-10 text-center", "data-testid": "search-empty-results", children: [
10
+ /* @__PURE__ */ jsx("div", { className: "flex size-16 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx(SearchX, { className: "size-7 text-muted-foreground", "aria-hidden": true }) }),
11
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
12
+ /* @__PURE__ */ jsx("div", { className: "text-base font-semibold text-foreground", children: t("ui.dataTable.search.empty.title", "No results found") }),
13
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t(
14
+ "ui.dataTable.search.empty.description",
15
+ "We couldn\u2019t find any {entity} matching \u201C{query}\u201D. Try adjusting your search or filters.",
16
+ { entity, query }
17
+ ) })
18
+ ] }),
19
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: onClearSearch, children: [
20
+ /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": true }),
21
+ t("ui.dataTable.search.empty.clear", "Clear search")
22
+ ] })
23
+ ] });
24
+ }
25
+ export {
26
+ SearchEmptyResults
27
+ };
28
+ //# sourceMappingURL=SearchEmptyResults.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/filters/SearchEmptyResults.tsx"],
4
+ "sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { SearchX, X } from 'lucide-react'\nimport { Button } from '../../primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport type SearchEmptyResultsProps = {\n /** The active search query, shown back to the user. */\n query: string\n /** Plural entity label (e.g. \"companies\"); falls back to a generic word. */\n entityNamePlural?: string\n /** Clears the active search. */\n onClearSearch: () => void\n}\n\nexport function SearchEmptyResults({ query, entityNamePlural, onClearSearch }: SearchEmptyResultsProps) {\n const t = useT()\n const entity = entityNamePlural ?? t('ui.dataTable.search.empty.genericEntity', 'results')\n return (\n <div className=\"flex w-full max-w-md flex-col items-center gap-4 py-10 text-center\" data-testid=\"search-empty-results\">\n <div className=\"flex size-16 items-center justify-center rounded-full bg-muted\">\n <SearchX className=\"size-7 text-muted-foreground\" aria-hidden />\n </div>\n <div className=\"flex flex-col items-center gap-1\">\n <div className=\"text-base font-semibold text-foreground\">\n {t('ui.dataTable.search.empty.title', 'No results found')}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ui.dataTable.search.empty.description',\n 'We couldn\u2019t find any {entity} matching \u201C{query}\u201D. Try adjusting your search or filters.',\n { entity, query },\n )}\n </p>\n </div>\n <Button type=\"button\" onClick={onClearSearch}>\n <X className=\"size-4\" aria-hidden />\n {t('ui.dataTable.search.empty.clear', 'Clear search')}\n </Button>\n </div>\n )\n}\n"],
5
+ "mappings": ";AAqBQ,cAEF,YAFE;AAnBR,SAAS,SAAS,SAAS;AAC3B,SAAS,cAAc;AACvB,SAAS,YAAY;AAWd,SAAS,mBAAmB,EAAE,OAAO,kBAAkB,cAAc,GAA4B;AACtG,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,oBAAoB,EAAE,2CAA2C,SAAS;AACzF,SACE,qBAAC,SAAI,WAAU,sEAAqE,eAAY,wBAC9F;AAAA,wBAAC,SAAI,WAAU,kEACb,8BAAC,WAAQ,WAAU,gCAA+B,eAAW,MAAC,GAChE;AAAA,IACA,qBAAC,SAAI,WAAU,oCACb;AAAA,0BAAC,SAAI,WAAU,2CACZ,YAAE,mCAAmC,kBAAkB,GAC1D;AAAA,MACA,oBAAC,OAAE,WAAU,iCACV;AAAA,QACC;AAAA,QACA;AAAA,QACA,EAAE,QAAQ,MAAM;AAAA,MAClB,GACF;AAAA,OACF;AAAA,IACA,qBAAC,UAAO,MAAK,UAAS,SAAS,eAC7B;AAAA,0BAAC,KAAE,WAAU,UAAS,eAAW,MAAC;AAAA,MACjC,EAAE,mCAAmC,cAAc;AAAA,OACtD;AAAA,KACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -13,6 +13,7 @@ import {
13
13
  BarChart3,
14
14
  Bell,
15
15
  Bolt,
16
+ Book,
16
17
  Bookmark,
17
18
  Box,
18
19
  Briefcase,
@@ -78,6 +79,7 @@ import {
78
79
  Mail,
79
80
  MailOpen,
80
81
  MapPin,
82
+ MessageCircle,
81
83
  MinusCircle,
82
84
  Notebook,
83
85
  Package,
@@ -93,6 +95,7 @@ import {
93
95
  Receipt,
94
96
  ReceiptText,
95
97
  RefreshCcw,
98
+ RefreshCw,
96
99
  Reply,
97
100
  RotateCcw,
98
101
  Ruler,
@@ -134,6 +137,7 @@ import {
134
137
  UserX,
135
138
  Wallet,
136
139
  Webhook,
140
+ Workflow,
137
141
  Wrench,
138
142
  X,
139
143
  XCircle
@@ -152,6 +156,7 @@ const LUCIDE_ICON_REGISTRY = {
152
156
  "bar-chart-3": BarChart3,
153
157
  "bell": Bell,
154
158
  "bolt": Bolt,
159
+ "book": Book,
155
160
  "bookmark": Bookmark,
156
161
  "box": Box,
157
162
  "briefcase": Briefcase,
@@ -217,6 +222,7 @@ const LUCIDE_ICON_REGISTRY = {
217
222
  "mail": Mail,
218
223
  "mail-open": MailOpen,
219
224
  "map-pin": MapPin,
225
+ "message-circle": MessageCircle,
220
226
  "minus-circle": MinusCircle,
221
227
  "notebook": Notebook,
222
228
  "package": Package,
@@ -232,6 +238,7 @@ const LUCIDE_ICON_REGISTRY = {
232
238
  "receipt": Receipt,
233
239
  "receipt-text": ReceiptText,
234
240
  "refresh-ccw": RefreshCcw,
241
+ "refresh-cw": RefreshCw,
235
242
  "reply": Reply,
236
243
  "rotate-ccw": RotateCcw,
237
244
  "ruler": Ruler,
@@ -273,6 +280,7 @@ const LUCIDE_ICON_REGISTRY = {
273
280
  "users": Users,
274
281
  "wallet": Wallet,
275
282
  "webhook": Webhook,
283
+ "workflow": Workflow,
276
284
  "wrench": Wrench,
277
285
  "x": X,
278
286
  "x-circle": XCircle
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/icons/lucideRegistry.generated.tsx"],
4
- "sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Activity,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Archive,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Bookmark,\n Box,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileMinus,\n FilePenLine,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Lightbulb,\n LineChart,\n Link,\n List,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n MapPin,\n MinusCircle,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageX,\n PauseCircle,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Receipt,\n ReceiptText,\n RefreshCcw,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Wallet,\n Webhook,\n Wrench,\n X,\n XCircle,\n} from 'lucide-react'\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'archive': Archive,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'bookmark': Bookmark,\n 'box': Box,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map-pin': MapPin,\n 'minus-circle': MinusCircle,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'wallet': Wallet,\n 'webhook': Webhook,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n}\n\nfunction normalizeKebabIconName(input: string): string {\n const trimmed = input.trim()\n if (!trimmed) return ''\n const withoutPrefix = trimmed.startsWith('lucide:') ? trimmed.slice('lucide:'.length) : trimmed\n if (!withoutPrefix) return ''\n if (!withoutPrefix.includes('-') && !withoutPrefix.includes('_') && !withoutPrefix.includes(' ') && /[A-Z]/.test(withoutPrefix)) {\n return withoutPrefix\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n }\n return withoutPrefix\n .replace(/[_\\s]+/g, '-')\n .replace(/-+/g, '-')\n .toLowerCase()\n}\n\nexport function resolveRegisteredLucideIcon(name: string | undefined): LucideIcon | null {\n if (!name) return null\n const normalized = normalizeKebabIconName(name)\n if (!normalized) return null\n return (LUCIDE_ICON_REGISTRY as Record<string, LucideIcon | undefined>)[normalized] ?? null\n}\n\nexport function resolveRegisteredLucideIconNode(\n name: string | undefined,\n className: string\n): React.ReactNode | null {\n const Icon = resolveRegisteredLucideIcon(name)\n if (!Icon) return null\n return <Icon className={className} />\n}\n"],
5
- "mappings": "AA4TS;AAtTT;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,uBAAmD;AAAA,EAC9D,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,eAAe;AAAA,EACf,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,eAAe;AAAA,EACf,SAAS;AAAA,EACT,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,cAAc;AAAA,EACd,SAAS;AAAA,EACT,eAAe;AAAA,EACf,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU;AAAA,EACV,aAAa;AAAA,EACb,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,cAAc;AAAA,EACd,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,SAAS;AAAA,EACT,cAAc;AAAA,EACd,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,SAAS;AAAA,EACT,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA,EACV,KAAK;AAAA,EACL,YAAY;AACd;AAEA,SAAS,uBAAuB,OAAuB;AACrD,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,gBAAgB,QAAQ,WAAW,SAAS,IAAI,QAAQ,MAAM,UAAU,MAAM,IAAI;AACxF,MAAI,CAAC,cAAe,QAAO;AAC3B,MAAI,CAAC,cAAc,SAAS,GAAG,KAAK,CAAC,cAAc,SAAS,GAAG,KAAK,CAAC,cAAc,SAAS,GAAG,KAAK,QAAQ,KAAK,aAAa,GAAG;AAC/H,WAAO,cACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,wBAAwB,OAAO,EACvC,YAAY;AAAA,EACjB;AACA,SAAO,cACJ,QAAQ,WAAW,GAAG,EACtB,QAAQ,OAAO,GAAG,EAClB,YAAY;AACjB;AAEO,SAAS,4BAA4B,MAA6C;AACvF,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,aAAa,uBAAuB,IAAI;AAC9C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAQ,qBAAgE,UAAU,KAAK;AACzF;AAEO,SAAS,gCACd,MACA,WACwB;AACxB,QAAM,OAAO,4BAA4B,IAAI;AAC7C,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,oBAAC,QAAK,WAAsB;AACrC;",
4
+ "sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Activity,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Archive,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Box,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileMinus,\n FilePenLine,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Lightbulb,\n LineChart,\n Link,\n List,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n MapPin,\n MessageCircle,\n MinusCircle,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageX,\n PauseCircle,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Wallet,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n} from 'lucide-react'\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'archive': Archive,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'box': Box,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map-pin': MapPin,\n 'message-circle': MessageCircle,\n 'minus-circle': MinusCircle,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'wallet': Wallet,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n}\n\nfunction normalizeKebabIconName(input: string): string {\n const trimmed = input.trim()\n if (!trimmed) return ''\n const withoutPrefix = trimmed.startsWith('lucide:') ? trimmed.slice('lucide:'.length) : trimmed\n if (!withoutPrefix) return ''\n if (!withoutPrefix.includes('-') && !withoutPrefix.includes('_') && !withoutPrefix.includes(' ') && /[A-Z]/.test(withoutPrefix)) {\n return withoutPrefix\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n }\n return withoutPrefix\n .replace(/[_\\s]+/g, '-')\n .replace(/-+/g, '-')\n .toLowerCase()\n}\n\nexport function resolveRegisteredLucideIcon(name: string | undefined): LucideIcon | null {\n if (!name) return null\n const normalized = normalizeKebabIconName(name)\n if (!normalized) return null\n return (LUCIDE_ICON_REGISTRY as Record<string, LucideIcon | undefined>)[normalized] ?? null\n}\n\nexport function resolveRegisteredLucideIconNode(\n name: string | undefined,\n className: string\n): React.ReactNode | null {\n const Icon = resolveRegisteredLucideIcon(name)\n if (!Icon) return null\n return <Icon className={className} />\n}\n"],
5
+ "mappings": "AAoUS;AA9TT;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAC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",
6
6
  "names": []
7
7
  }
@@ -3,11 +3,14 @@ import * as React from "react";
3
3
  import { useInjectionSpotEvents } from "./InjectionSpot.js";
4
4
  import { GLOBAL_MUTATION_INJECTION_SPOT_ID, dispatchBackendMutationError } from "./mutationEvents.js";
5
5
  import { withScopedApiRequestHeaders } from "../utils/apiCall.js";
6
+ import { surfaceRecordConflict } from "../conflicts/index.js";
7
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
8
  function useGuardedMutation({
7
9
  contextId,
8
10
  blockedMessage = "Save blocked by validation",
9
11
  spotId = GLOBAL_MUTATION_INJECTION_SPOT_ID
10
12
  }) {
13
+ const t = useT();
11
14
  const { triggerEvent } = useInjectionSpotEvents(spotId);
12
15
  const lastMutationRef = React.useRef(null);
13
16
  const emitMutationSaveError = React.useCallback((error) => {
@@ -16,7 +19,11 @@ function useGuardedMutation({
16
19
  formId: contextId,
17
20
  error
18
21
  });
19
- }, [contextId]);
22
+ try {
23
+ surfaceRecordConflict(error, t);
24
+ } catch {
25
+ }
26
+ }, [contextId, t]);
20
27
  const runMutation = React.useCallback(async ({
21
28
  operation,
22
29
  context,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/injection/useGuardedMutation.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useInjectionSpotEvents } from './InjectionSpot'\nimport { GLOBAL_MUTATION_INJECTION_SPOT_ID, dispatchBackendMutationError } from './mutationEvents'\nimport { withScopedApiRequestHeaders } from '../utils/apiCall'\n\ntype GuardedMutationContext = Record<string, unknown>\n\ntype GuardedMutationRunInput<TResult, TContext extends GuardedMutationContext> = {\n operation: () => Promise<TResult>\n context: TContext\n mutationPayload?: Record<string, unknown>\n}\n\ntype UseGuardedMutationOptions = {\n contextId: string\n blockedMessage?: string\n spotId?: string\n}\n\ntype StoredMutation<TContext extends GuardedMutationContext> = {\n operation: () => Promise<unknown>\n context: TContext\n mutationPayload: Record<string, unknown>\n}\n\nexport function useGuardedMutation<TContext extends GuardedMutationContext>({\n contextId,\n blockedMessage = 'Save blocked by validation',\n spotId = GLOBAL_MUTATION_INJECTION_SPOT_ID,\n}: UseGuardedMutationOptions) {\n const { triggerEvent } = useInjectionSpotEvents<TContext, Record<string, unknown>>(spotId)\n const lastMutationRef = React.useRef<StoredMutation<TContext> | null>(null)\n\n const emitMutationSaveError = React.useCallback((error: unknown) => {\n dispatchBackendMutationError({\n contextId,\n formId: contextId,\n error,\n })\n }, [contextId])\n\n const runMutation = React.useCallback(async <TResult,>({\n operation,\n context,\n mutationPayload,\n }: GuardedMutationRunInput<TResult, TContext>): Promise<TResult> => {\n const payload = mutationPayload ?? {}\n lastMutationRef.current = {\n operation: operation as () => Promise<unknown>,\n context,\n mutationPayload: payload,\n }\n\n const beforeSave = await triggerEvent('onBeforeSave', payload, context)\n if (!beforeSave.ok) {\n emitMutationSaveError(beforeSave.details ?? beforeSave)\n throw new Error(beforeSave.message || blockedMessage)\n }\n\n try {\n const result =\n beforeSave.requestHeaders && Object.keys(beforeSave.requestHeaders).length > 0\n ? await withScopedApiRequestHeaders(beforeSave.requestHeaders, operation)\n : await operation()\n\n try {\n await triggerEvent('onAfterSave', payload, context)\n } catch (error) {\n console.error('[useGuardedMutation] Error in onAfterSave injection event:', error)\n }\n\n return result\n } catch (error) {\n emitMutationSaveError(error)\n throw error\n }\n }, [blockedMessage, emitMutationSaveError, triggerEvent])\n\n const retryLastMutation = React.useCallback(async (): Promise<boolean> => {\n const lastMutation = lastMutationRef.current\n if (!lastMutation) return false\n\n try {\n await runMutation({\n operation: lastMutation.operation,\n context: lastMutation.context,\n mutationPayload: lastMutation.mutationPayload,\n })\n return true\n } catch {\n return false\n }\n }, [runMutation])\n\n return {\n runMutation,\n retryLastMutation,\n }\n}\n"],
5
- "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,8BAA8B;AACvC,SAAS,mCAAmC,oCAAoC;AAChF,SAAS,mCAAmC;AAsBrC,SAAS,mBAA4D;AAAA,EAC1E;AAAA,EACA,iBAAiB;AAAA,EACjB,SAAS;AACX,GAA8B;AAC5B,QAAM,EAAE,aAAa,IAAI,uBAA0D,MAAM;AACzF,QAAM,kBAAkB,MAAM,OAAwC,IAAI;AAE1E,QAAM,wBAAwB,MAAM,YAAY,CAAC,UAAmB;AAClE,iCAA6B;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,cAAc,MAAM,YAAY,OAAiB;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAoE;AAClE,UAAM,UAAU,mBAAmB,CAAC;AACpC,oBAAgB,UAAU;AAAA,MACxB;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAEA,UAAM,aAAa,MAAM,aAAa,gBAAgB,SAAS,OAAO;AACtE,QAAI,CAAC,WAAW,IAAI;AAClB,4BAAsB,WAAW,WAAW,UAAU;AACtD,YAAM,IAAI,MAAM,WAAW,WAAW,cAAc;AAAA,IACtD;AAEA,QAAI;AACF,YAAM,SACJ,WAAW,kBAAkB,OAAO,KAAK,WAAW,cAAc,EAAE,SAAS,IACzE,MAAM,4BAA4B,WAAW,gBAAgB,SAAS,IACtE,MAAM,UAAU;AAEtB,UAAI;AACF,cAAM,aAAa,eAAe,SAAS,OAAO;AAAA,MACpD,SAAS,OAAO;AACd,gBAAQ,MAAM,8DAA8D,KAAK;AAAA,MACnF;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,4BAAsB,KAAK;AAC3B,YAAM;AAAA,IACR;AAAA,EACF,GAAG,CAAC,gBAAgB,uBAAuB,YAAY,CAAC;AAExD,QAAM,oBAAoB,MAAM,YAAY,YAA8B;AACxE,UAAM,eAAe,gBAAgB;AACrC,QAAI,CAAC,aAAc,QAAO;AAE1B,QAAI;AACF,YAAM,YAAY;AAAA,QAChB,WAAW,aAAa;AAAA,QACxB,SAAS,aAAa;AAAA,QACtB,iBAAiB,aAAa;AAAA,MAChC,CAAC;AACD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useInjectionSpotEvents } from './InjectionSpot'\nimport { GLOBAL_MUTATION_INJECTION_SPOT_ID, dispatchBackendMutationError } from './mutationEvents'\nimport { withScopedApiRequestHeaders } from '../utils/apiCall'\nimport { surfaceRecordConflict } from '../conflicts'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype GuardedMutationContext = Record<string, unknown>\n\ntype GuardedMutationRunInput<TResult, TContext extends GuardedMutationContext> = {\n operation: () => Promise<TResult>\n context: TContext\n mutationPayload?: Record<string, unknown>\n}\n\ntype UseGuardedMutationOptions = {\n contextId: string\n blockedMessage?: string\n spotId?: string\n}\n\ntype StoredMutation<TContext extends GuardedMutationContext> = {\n operation: () => Promise<unknown>\n context: TContext\n mutationPayload: Record<string, unknown>\n}\n\nexport function useGuardedMutation<TContext extends GuardedMutationContext>({\n contextId,\n blockedMessage = 'Save blocked by validation',\n spotId = GLOBAL_MUTATION_INJECTION_SPOT_ID,\n}: UseGuardedMutationOptions) {\n const t = useT()\n const { triggerEvent } = useInjectionSpotEvents<TContext, Record<string, unknown>>(spotId)\n const lastMutationRef = React.useRef<StoredMutation<TContext> | null>(null)\n\n const emitMutationSaveError = React.useCallback((error: unknown) => {\n dispatchBackendMutationError({\n contextId,\n formId: contextId,\n error,\n })\n // Default UX for OSS optimistic-lock conflicts (spec\n // .ai/specs/2026-05-25-oss-optimistic-locking.md \u00A73.6): when the\n // server returns 409 with `code: 'optimistic_lock_conflict'`,\n // surface the conflict on the unified, persistent, error-styled\n // RecordConflictBanner (rendered in AppShell) instead of a transient\n // toast. Callers that have their own handler still see the dispatched\n // event above and can suppress/override by catching the error first.\n try {\n surfaceRecordConflict(error, t)\n } catch {\n // ignore conflict-bar dispatch failures\n }\n }, [contextId, t])\n\n const runMutation = React.useCallback(async <TResult,>({\n operation,\n context,\n mutationPayload,\n }: GuardedMutationRunInput<TResult, TContext>): Promise<TResult> => {\n const payload = mutationPayload ?? {}\n lastMutationRef.current = {\n operation: operation as () => Promise<unknown>,\n context,\n mutationPayload: payload,\n }\n\n const beforeSave = await triggerEvent('onBeforeSave', payload, context)\n if (!beforeSave.ok) {\n emitMutationSaveError(beforeSave.details ?? beforeSave)\n throw new Error(beforeSave.message || blockedMessage)\n }\n\n try {\n const result =\n beforeSave.requestHeaders && Object.keys(beforeSave.requestHeaders).length > 0\n ? await withScopedApiRequestHeaders(beforeSave.requestHeaders, operation)\n : await operation()\n\n try {\n await triggerEvent('onAfterSave', payload, context)\n } catch (error) {\n console.error('[useGuardedMutation] Error in onAfterSave injection event:', error)\n }\n\n return result\n } catch (error) {\n emitMutationSaveError(error)\n throw error\n }\n }, [blockedMessage, emitMutationSaveError, triggerEvent])\n\n const retryLastMutation = React.useCallback(async (): Promise<boolean> => {\n const lastMutation = lastMutationRef.current\n if (!lastMutation) return false\n\n try {\n await runMutation({\n operation: lastMutation.operation,\n context: lastMutation.context,\n mutationPayload: lastMutation.mutationPayload,\n })\n return true\n } catch {\n return false\n }\n }, [runMutation])\n\n return {\n runMutation,\n retryLastMutation,\n }\n}\n"],
5
+ "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,8BAA8B;AACvC,SAAS,mCAAmC,oCAAoC;AAChF,SAAS,mCAAmC;AAC5C,SAAS,6BAA6B;AACtC,SAAS,YAAY;AAsBd,SAAS,mBAA4D;AAAA,EAC1E;AAAA,EACA,iBAAiB;AAAA,EACjB,SAAS;AACX,GAA8B;AAC5B,QAAM,IAAI,KAAK;AACf,QAAM,EAAE,aAAa,IAAI,uBAA0D,MAAM;AACzF,QAAM,kBAAkB,MAAM,OAAwC,IAAI;AAE1E,QAAM,wBAAwB,MAAM,YAAY,CAAC,UAAmB;AAClE,iCAA6B;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAQD,QAAI;AACF,4BAAsB,OAAO,CAAC;AAAA,IAChC,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,WAAW,CAAC,CAAC;AAEjB,QAAM,cAAc,MAAM,YAAY,OAAiB;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAoE;AAClE,UAAM,UAAU,mBAAmB,CAAC;AACpC,oBAAgB,UAAU;AAAA,MACxB;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAEA,UAAM,aAAa,MAAM,aAAa,gBAAgB,SAAS,OAAO;AACtE,QAAI,CAAC,WAAW,IAAI;AAClB,4BAAsB,WAAW,WAAW,UAAU;AACtD,YAAM,IAAI,MAAM,WAAW,WAAW,cAAc;AAAA,IACtD;AAEA,QAAI;AACF,YAAM,SACJ,WAAW,kBAAkB,OAAO,KAAK,WAAW,cAAc,EAAE,SAAS,IACzE,MAAM,4BAA4B,WAAW,gBAAgB,SAAS,IACtE,MAAM,UAAU;AAEtB,UAAI;AACF,cAAM,aAAa,eAAe,SAAS,OAAO;AAAA,MACpD,SAAS,OAAO;AACd,gBAAQ,MAAM,8DAA8D,KAAK;AAAA,MACnF;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,4BAAsB,KAAK;AAC3B,YAAM;AAAA,IACR;AAAA,EACF,GAAG,CAAC,gBAAgB,uBAAuB,YAAY,CAAC;AAExD,QAAM,oBAAoB,MAAM,YAAY,YAA8B;AACxE,UAAM,eAAe,gBAAgB;AACrC,QAAI,CAAC,aAAc,QAAO;AAE1B,QAAI;AACF,YAAM,YAAY;AAAA,QAChB,WAAW,aAAa;AAAA,QACxB,SAAS,aAAa;AAAA,QACtB,iBAAiB,aAAa;AAAA,MAChC,CAAC;AACD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import dynamic from "next/dynamic";
4
+ const isTestEnv = typeof process !== "undefined" && (process.env.NODE_ENV === "test" || typeof process.env.JEST_WORKER_ID !== "undefined");
5
+ const MarkdownFieldTestStub = ({ value, onChange }) => /* @__PURE__ */ jsx(
6
+ "textarea",
7
+ {
8
+ "data-testid": "markdown-field",
9
+ className: "min-h-[160px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
10
+ value: value ?? "",
11
+ onChange: (event) => onChange(event.target.value)
12
+ }
13
+ );
14
+ const MarkdownFieldLive = dynamic(() => import("./MdxEditorImpl.js"), { ssr: false });
15
+ const MarkdownField = isTestEnv ? MarkdownFieldTestStub : MarkdownFieldLive;
16
+ var MarkdownField_default = MarkdownField;
17
+ export {
18
+ MarkdownField_default as default
19
+ };
20
+ //# sourceMappingURL=MarkdownField.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/inputs/MarkdownField.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport dynamic from 'next/dynamic'\nimport type { ComponentType } from 'react'\n\nexport type MarkdownFieldProps = {\n value?: string\n onChange: (markdown: string) => void\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\n// Lightweight stand-in for unit tests (jsdom): MDXEditor/Lexical pull in ESM + CSS that jest\n// would have to transform, so under test we render a plain controlled textarea instead.\nconst MarkdownFieldTestStub: ComponentType<MarkdownFieldProps> = ({ value, onChange }) => (\n <textarea\n data-testid=\"markdown-field\"\n className=\"min-h-[160px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={value ?? ''}\n onChange={(event) => onChange(event.target.value)}\n />\n)\n\nconst MarkdownFieldLive = dynamic(() => import('./MdxEditorImpl'), { ssr: false }) as ComponentType<MarkdownFieldProps>\n\n// Canonical Markdown editor for the whole app: a WYSIWYG MDXEditor (Lexical) that emits Markdown.\n// Client-only (Lexical needs the DOM), loaded via next/dynamic with ssr disabled. Use this\n// everywhere a Markdown editing field is needed.\nconst MarkdownField: ComponentType<MarkdownFieldProps> = isTestEnv ? MarkdownFieldTestStub : MarkdownFieldLive\n\nexport default MarkdownField\n"],
5
+ "mappings": ";AAkBE;AAfF,OAAO,aAAa;AAQpB,MAAM,YACJ,OAAO,YAAY,gBAClB,QAAQ,IAAI,aAAa,UAAU,OAAO,QAAQ,IAAI,mBAAmB;AAI5E,MAAM,wBAA2D,CAAC,EAAE,OAAO,SAAS,MAClF;AAAA,EAAC;AAAA;AAAA,IACC,eAAY;AAAA,IACZ,WAAU;AAAA,IACV,OAAO,SAAS;AAAA,IAChB,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA;AAClD;AAGF,MAAM,oBAAoB,QAAQ,MAAM,OAAO,iBAAiB,GAAG,EAAE,KAAK,MAAM,CAAC;AAKjF,MAAM,gBAAmD,YAAY,wBAAwB;AAE7F,IAAO,wBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import {
5
+ MDXEditor,
6
+ headingsPlugin,
7
+ listsPlugin,
8
+ quotePlugin,
9
+ thematicBreakPlugin,
10
+ linkPlugin,
11
+ linkDialogPlugin,
12
+ imagePlugin,
13
+ tablePlugin,
14
+ markdownShortcutPlugin,
15
+ diffSourcePlugin,
16
+ toolbarPlugin,
17
+ UndoRedo,
18
+ BoldItalicUnderlineToggles,
19
+ CodeToggle,
20
+ StrikeThroughSupSubToggles,
21
+ ListsToggle,
22
+ BlockTypeSelect,
23
+ CreateLink,
24
+ InsertImage,
25
+ InsertTable,
26
+ InsertThematicBreak,
27
+ Separator,
28
+ DiffSourceToggleWrapper
29
+ } from "@mdxeditor/editor";
30
+ import "@mdxeditor/editor/style.css";
31
+ import { cn } from "@open-mercato/shared/lib/utils";
32
+ import { useTheme } from "../../theme/index.js";
33
+ function MdxEditorImpl({ value = "", onChange }) {
34
+ const editorRef = React.useRef(null);
35
+ const latestRef = React.useRef(value);
36
+ const typingRef = React.useRef(false);
37
+ const { resolvedTheme } = useTheme();
38
+ React.useEffect(() => {
39
+ if (typingRef.current) return;
40
+ if (value !== latestRef.current) {
41
+ latestRef.current = value;
42
+ editorRef.current?.setMarkdown(value ?? "");
43
+ }
44
+ }, [value]);
45
+ const commit = React.useCallback(() => {
46
+ if (!typingRef.current) return;
47
+ typingRef.current = false;
48
+ onChange(latestRef.current);
49
+ }, [onChange]);
50
+ return /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-md border border-input bg-background", onBlur: commit, children: /* @__PURE__ */ jsx(
51
+ MDXEditor,
52
+ {
53
+ ref: editorRef,
54
+ markdown: value ?? "",
55
+ onChange: (markdown) => {
56
+ typingRef.current = true;
57
+ latestRef.current = markdown;
58
+ },
59
+ className: cn("om-mdx-editor", resolvedTheme === "dark" && "dark-theme"),
60
+ contentEditableClassName: "om-mdx-prose",
61
+ plugins: [
62
+ headingsPlugin(),
63
+ listsPlugin(),
64
+ quotePlugin(),
65
+ thematicBreakPlugin(),
66
+ linkPlugin(),
67
+ linkDialogPlugin(),
68
+ imagePlugin(),
69
+ tablePlugin(),
70
+ markdownShortcutPlugin(),
71
+ diffSourcePlugin({ viewMode: "rich-text" }),
72
+ toolbarPlugin({
73
+ toolbarContents: () => /* @__PURE__ */ jsxs(DiffSourceToggleWrapper, { children: [
74
+ /* @__PURE__ */ jsx(UndoRedo, {}),
75
+ /* @__PURE__ */ jsx(Separator, {}),
76
+ /* @__PURE__ */ jsx(BoldItalicUnderlineToggles, {}),
77
+ /* @__PURE__ */ jsx(CodeToggle, {}),
78
+ /* @__PURE__ */ jsx(StrikeThroughSupSubToggles, {}),
79
+ /* @__PURE__ */ jsx(Separator, {}),
80
+ /* @__PURE__ */ jsx(ListsToggle, {}),
81
+ /* @__PURE__ */ jsx(Separator, {}),
82
+ /* @__PURE__ */ jsx(BlockTypeSelect, {}),
83
+ /* @__PURE__ */ jsx(Separator, {}),
84
+ /* @__PURE__ */ jsx(CreateLink, {}),
85
+ /* @__PURE__ */ jsx(InsertImage, {}),
86
+ /* @__PURE__ */ jsx(Separator, {}),
87
+ /* @__PURE__ */ jsx(InsertTable, {}),
88
+ /* @__PURE__ */ jsx(InsertThematicBreak, {})
89
+ ] })
90
+ })
91
+ ]
92
+ }
93
+ ) });
94
+ }
95
+ export {
96
+ MdxEditorImpl as default
97
+ };
98
+ //# sourceMappingURL=MdxEditorImpl.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/inputs/MdxEditorImpl.tsx"],
4
+ "sourcesContent": ["/// <reference path=\"../../types/css.d.ts\" />\n\"use client\"\n\nimport * as React from 'react'\nimport {\n MDXEditor,\n type MDXEditorMethods,\n headingsPlugin,\n listsPlugin,\n quotePlugin,\n thematicBreakPlugin,\n linkPlugin,\n linkDialogPlugin,\n imagePlugin,\n tablePlugin,\n markdownShortcutPlugin,\n diffSourcePlugin,\n toolbarPlugin,\n UndoRedo,\n BoldItalicUnderlineToggles,\n CodeToggle,\n StrikeThroughSupSubToggles,\n ListsToggle,\n BlockTypeSelect,\n CreateLink,\n InsertImage,\n InsertTable,\n InsertThematicBreak,\n Separator,\n DiffSourceToggleWrapper,\n} from '@mdxeditor/editor'\nimport '@mdxeditor/editor/style.css'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useTheme } from '../../theme'\n\ntype MdxEditorImplProps = {\n value?: string\n onChange: (markdown: string) => void\n}\n\n// WYSIWYG Markdown editor (MDXEditor / Lexical) wired to the CrudForm value/onChange\n// contract. Markdown is the source of truth. Edits are buffered locally and committed\n// on blur to avoid re-rendering the whole form on every keystroke.\nexport default function MdxEditorImpl({ value = '', onChange }: MdxEditorImplProps) {\n const editorRef = React.useRef<MDXEditorMethods>(null)\n const latestRef = React.useRef<string>(value)\n const typingRef = React.useRef(false)\n const { resolvedTheme } = useTheme()\n\n // External value changes (form reset / async initial load) are pushed into the editor,\n // but never while the user is actively typing (would reset the caret).\n React.useEffect(() => {\n if (typingRef.current) return\n if (value !== latestRef.current) {\n latestRef.current = value\n editorRef.current?.setMarkdown(value ?? '')\n }\n }, [value])\n\n const commit = React.useCallback(() => {\n if (!typingRef.current) return\n typingRef.current = false\n onChange(latestRef.current)\n }, [onChange])\n\n return (\n <div className=\"w-full overflow-hidden rounded-md border border-input bg-background\" onBlur={commit}>\n <MDXEditor\n ref={editorRef}\n markdown={value ?? ''}\n onChange={(markdown) => {\n typingRef.current = true\n latestRef.current = markdown\n }}\n className={cn('om-mdx-editor', resolvedTheme === 'dark' && 'dark-theme')}\n contentEditableClassName=\"om-mdx-prose\"\n plugins={[\n headingsPlugin(),\n listsPlugin(),\n quotePlugin(),\n thematicBreakPlugin(),\n linkPlugin(),\n linkDialogPlugin(),\n imagePlugin(),\n tablePlugin(),\n markdownShortcutPlugin(),\n diffSourcePlugin({ viewMode: 'rich-text' }),\n toolbarPlugin({\n toolbarContents: () => (\n <DiffSourceToggleWrapper>\n <UndoRedo />\n <Separator />\n <BoldItalicUnderlineToggles />\n <CodeToggle />\n <StrikeThroughSupSubToggles />\n <Separator />\n <ListsToggle />\n <Separator />\n <BlockTypeSelect />\n <Separator />\n <CreateLink />\n <InsertImage />\n <Separator />\n <InsertTable />\n <InsertThematicBreak />\n </DiffSourceToggleWrapper>\n ),\n }),\n ]}\n />\n </div>\n )\n}\n"],
5
+ "mappings": ";AAyFc,SACE,KADF;AAtFd,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAO;AACP,SAAS,UAAU;AACnB,SAAS,gBAAgB;AAUV,SAAR,cAA+B,EAAE,QAAQ,IAAI,SAAS,GAAuB;AAClF,QAAM,YAAY,MAAM,OAAyB,IAAI;AACrD,QAAM,YAAY,MAAM,OAAe,KAAK;AAC5C,QAAM,YAAY,MAAM,OAAO,KAAK;AACpC,QAAM,EAAE,cAAc,IAAI,SAAS;AAInC,QAAM,UAAU,MAAM;AACpB,QAAI,UAAU,QAAS;AACvB,QAAI,UAAU,UAAU,SAAS;AAC/B,gBAAU,UAAU;AACpB,gBAAU,SAAS,YAAY,SAAS,EAAE;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,SAAS,MAAM,YAAY,MAAM;AACrC,QAAI,CAAC,UAAU,QAAS;AACxB,cAAU,UAAU;AACpB,aAAS,UAAU,OAAO;AAAA,EAC5B,GAAG,CAAC,QAAQ,CAAC;AAEb,SACE,oBAAC,SAAI,WAAU,uEAAsE,QAAQ,QAC3F;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,UAAU,SAAS;AAAA,MACnB,UAAU,CAAC,aAAa;AACtB,kBAAU,UAAU;AACpB,kBAAU,UAAU;AAAA,MACtB;AAAA,MACA,WAAW,GAAG,iBAAiB,kBAAkB,UAAU,YAAY;AAAA,MACvE,0BAAyB;AAAA,MACzB,SAAS;AAAA,QACP,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,oBAAoB;AAAA,QACpB,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,uBAAuB;AAAA,QACvB,iBAAiB,EAAE,UAAU,YAAY,CAAC;AAAA,QAC1C,cAAc;AAAA,UACZ,iBAAiB,MACf,qBAAC,2BACC;AAAA,gCAAC,YAAS;AAAA,YACV,oBAAC,aAAU;AAAA,YACX,oBAAC,8BAA2B;AAAA,YAC5B,oBAAC,cAAW;AAAA,YACZ,oBAAC,8BAA2B;AAAA,YAC5B,oBAAC,aAAU;AAAA,YACX,oBAAC,eAAY;AAAA,YACb,oBAAC,aAAU;AAAA,YACX,oBAAC,mBAAgB;AAAA,YACjB,oBAAC,aAAU;AAAA,YACX,oBAAC,cAAW;AAAA,YACZ,oBAAC,eAAY;AAAA,YACb,oBAAC,aAAU;AAAA,YACX,oBAAC,eAAY;AAAA,YACb,oBAAC,uBAAoB;AAAA,aACvB;AAAA,QAEJ,CAAC;AAAA,MACH;AAAA;AAAA,EACF,GACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -1,59 +1,27 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import dynamic from "next/dynamic";
4
- import { LoadingMessage } from "../detail/LoadingMessage.js";
5
- import { useMarkdownRemarkPlugins } from "../markdown/useMarkdownRemarkPlugins.js";
6
- import { useTheme } from "../../theme/index.js";
7
- const isTestEnv = typeof process !== "undefined" && (process.env.NODE_ENV === "test" || typeof process.env.JEST_WORKER_ID !== "undefined");
8
- const MarkdownEditorTestStub = ({ value, onChange }) => /* @__PURE__ */ jsx(
9
- "textarea",
10
- {
11
- className: "min-h-[160px] w-full rounded border px-3 py-2 text-sm",
12
- value: value ?? "",
13
- onChange: (event) => onChange?.(event.target.value)
14
- }
15
- );
16
- const UiMarkdownEditor = isTestEnv ? MarkdownEditorTestStub : dynamic(() => import("@uiw/react-md-editor"), {
17
- ssr: false,
18
- loading: () => /* @__PURE__ */ jsx(
19
- LoadingMessage,
20
- {
21
- label: "Loading editor...",
22
- className: "min-h-[220px] justify-center"
23
- }
24
- )
25
- });
3
+ import MarkdownField from "./MarkdownField.js";
26
4
  function SwitchableMarkdownInput({
27
5
  value,
28
6
  onChange,
29
7
  isMarkdownEnabled,
30
8
  disableMarkdown,
31
- height = 220,
32
9
  placeholder,
33
10
  rows = 3,
34
11
  textareaRef,
35
12
  onTextareaInput,
36
13
  textareaClassName,
37
- editorWrapperClassName,
38
- editorClassName,
39
- disabled,
40
- remarkPlugins
14
+ disabled
41
15
  }) {
42
- const resolvedPlugins = useMarkdownRemarkPlugins(remarkPlugins);
43
- const { resolvedTheme } = useTheme();
44
- const editorWrapperClasses = editorWrapperClassName ?? "w-full rounded-lg border border-muted-foreground/20 bg-background p-2";
45
- const editorClasses = editorClassName ?? "w-full";
46
16
  const textareaClasses = textareaClassName ?? "w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
47
17
  if (isMarkdownEnabled && !disableMarkdown) {
48
- return /* @__PURE__ */ jsx("div", { className: editorWrapperClasses, children: /* @__PURE__ */ jsx("div", { "data-color-mode": resolvedTheme, className: editorClasses, children: /* @__PURE__ */ jsx(
49
- UiMarkdownEditor,
18
+ return /* @__PURE__ */ jsx(
19
+ MarkdownField,
50
20
  {
51
21
  value,
52
- height,
53
- onChange: (nextValue) => onChange(typeof nextValue === "string" ? nextValue : ""),
54
- previewOptions: resolvedPlugins.length ? { remarkPlugins: resolvedPlugins } : void 0
22
+ onChange: (nextValue) => onChange(typeof nextValue === "string" ? nextValue : "")
55
23
  }
56
- ) }) });
24
+ );
57
25
  }
58
26
  return /* @__PURE__ */ jsx(
59
27
  "textarea",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/inputs/SwitchableMarkdownInput.tsx"],
4
- "sourcesContent": ["/**\n * @deprecated Prefer the DS `RichEditor` primitive from\n * `@open-mercato/ui/primitives/rich-editor` for any new rich-text input.\n *\n * `RichEditor` produces sanitized HTML (with the DS toolbar, color palette,\n * Figma-aligned styling, and auto-overflow toolbar variants). The DS direction\n * is to consolidate on a single rich-text format (HTML) so that user-authored\n * content renders consistently across email, exports, and the customer portal.\n *\n * `SwitchableMarkdownInput` is kept as a backward-compatibility shim for\n * existing Markdown-backed surfaces (notably `customers` Notes). It will be\n * removed once those surfaces migrate their storage format from Markdown to\n * sanitized HTML \u2014 track the migration in the spec referenced from\n * `customers/AGENTS.md`.\n *\n * Migration:\n *\n * ```diff\n * - import { SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs/SwitchableMarkdownInput'\n * - <SwitchableMarkdownInput value={md} onChange={setMd} isMarkdownEnabled={enabled} />\n * + import { RichEditor } from '@open-mercato/ui/primitives/rich-editor'\n * + <RichEditor value={html} onChange={setHtml} variant=\"basic\" />\n * ```\n *\n * For surfaces that genuinely need plain Markdown storage (e.g. agent prompts,\n * code-adjacent docs), keep using `SwitchableMarkdownInput` until a Markdown\n * mode lands on `RichEditor` \u2014 but do NOT introduce new Markdown surfaces.\n */\n\"use client\"\n\nimport * as React from 'react'\nimport dynamic from 'next/dynamic'\nimport type { PluggableList } from 'unified'\nimport { LoadingMessage } from '../detail/LoadingMessage'\nimport { useMarkdownRemarkPlugins } from '../markdown/useMarkdownRemarkPlugins'\nimport { useTheme } from '../../theme'\n\n/** @deprecated See module-level JSDoc. */\nexport type SwitchableMarkdownInputProps = {\n value: string\n onChange: (value: string) => void\n isMarkdownEnabled: boolean\n disableMarkdown?: boolean\n height?: number\n placeholder?: string\n rows?: number\n textareaRef?: React.Ref<HTMLTextAreaElement>\n onTextareaInput?: React.FormEventHandler<HTMLTextAreaElement>\n textareaClassName?: string\n editorWrapperClassName?: string\n editorClassName?: string\n disabled?: boolean\n remarkPlugins?: PluggableList\n}\n\ntype UiMarkdownEditorProps = {\n value?: string\n height?: number\n onChange?: (value?: string) => void\n previewOptions?: { remarkPlugins?: unknown[] }\n}\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nconst MarkdownEditorTestStub: React.ComponentType<UiMarkdownEditorProps> = ({ value, onChange }) => (\n <textarea\n className=\"min-h-[160px] w-full rounded border px-3 py-2 text-sm\"\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n />\n)\n\nconst UiMarkdownEditor = isTestEnv\n ? MarkdownEditorTestStub\n : (dynamic(() => import('@uiw/react-md-editor'), {\n ssr: false,\n loading: () => (\n <LoadingMessage\n label=\"Loading editor...\"\n className=\"min-h-[220px] justify-center\"\n />\n ),\n }) as unknown as React.ComponentType<UiMarkdownEditorProps>)\n\n/** @deprecated Use `RichEditor` from `@open-mercato/ui/primitives/rich-editor` for new code. See module JSDoc. */\nexport function SwitchableMarkdownInput({\n value,\n onChange,\n isMarkdownEnabled,\n disableMarkdown,\n height = 220,\n placeholder,\n rows = 3,\n textareaRef,\n onTextareaInput,\n textareaClassName,\n editorWrapperClassName,\n editorClassName,\n disabled,\n remarkPlugins,\n}: SwitchableMarkdownInputProps) {\n const resolvedPlugins = useMarkdownRemarkPlugins(remarkPlugins)\n const { resolvedTheme } = useTheme()\n const editorWrapperClasses =\n editorWrapperClassName ?? 'w-full rounded-lg border border-muted-foreground/20 bg-background p-2'\n const editorClasses = editorClassName ?? 'w-full'\n const textareaClasses =\n textareaClassName\n ?? 'w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\n if (isMarkdownEnabled && !disableMarkdown) {\n return (\n <div className={editorWrapperClasses}>\n <div data-color-mode={resolvedTheme} className={editorClasses}>\n <UiMarkdownEditor\n value={value}\n height={height}\n onChange={(nextValue) => onChange(typeof nextValue === 'string' ? nextValue : '')}\n previewOptions={resolvedPlugins.length ? { remarkPlugins: resolvedPlugins } : undefined}\n />\n </div>\n </div>\n )\n }\n\n return (\n <textarea\n ref={textareaRef}\n rows={rows}\n className={textareaClasses}\n placeholder={placeholder}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onInput={onTextareaInput}\n disabled={disabled}\n />\n )\n}\n"],
5
- "mappings": ";AAmEE;AApCF,OAAO,aAAa;AAEpB,SAAS,sBAAsB;AAC/B,SAAS,gCAAgC;AACzC,SAAS,gBAAgB;AA2BzB,MAAM,YACJ,OAAO,YAAY,gBAClB,QAAQ,IAAI,aAAa,UAAU,OAAO,QAAQ,IAAI,mBAAmB;AAE5E,MAAM,yBAAqE,CAAC,EAAE,OAAO,SAAS,MAC5F;AAAA,EAAC;AAAA;AAAA,IACC,WAAU;AAAA,IACV,OAAO,SAAS;AAAA,IAChB,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA;AACpD;AAGF,MAAM,mBAAmB,YACrB,yBACC,QAAQ,MAAM,OAAO,sBAAsB,GAAG;AAAA,EAC7C,KAAK;AAAA,EACL,SAAS,MACP;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,WAAU;AAAA;AAAA,EACZ;AAEJ,CAAC;AAGE,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,kBAAkB,yBAAyB,aAAa;AAC9D,QAAM,EAAE,cAAc,IAAI,SAAS;AACnC,QAAM,uBACJ,0BAA0B;AAC5B,QAAM,gBAAgB,mBAAmB;AACzC,QAAM,kBACJ,qBACG;AAEL,MAAI,qBAAqB,CAAC,iBAAiB;AACzC,WACE,oBAAC,SAAI,WAAW,sBACd,8BAAC,SAAI,mBAAiB,eAAe,WAAW,eAC9C;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU,CAAC,cAAc,SAAS,OAAO,cAAc,WAAW,YAAY,EAAE;AAAA,QAChF,gBAAgB,gBAAgB,SAAS,EAAE,eAAe,gBAAgB,IAAI;AAAA;AAAA,IAChF,GACF,GACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,SAAS;AAAA,MACT;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["/**\n * @deprecated Prefer the DS `RichEditor` primitive from\n * `@open-mercato/ui/primitives/rich-editor` for any new rich-text input.\n *\n * `RichEditor` produces sanitized HTML (with the DS toolbar, color palette,\n * Figma-aligned styling, and auto-overflow toolbar variants). The DS direction\n * is to consolidate on a single rich-text format (HTML) so that user-authored\n * content renders consistently across email, exports, and the customer portal.\n *\n * `SwitchableMarkdownInput` is kept as a backward-compatibility shim for\n * existing Markdown-backed surfaces (notably `customers` Notes). It will be\n * removed once those surfaces migrate their storage format from Markdown to\n * sanitized HTML \u2014 track the migration in the spec referenced from\n * `customers/AGENTS.md`.\n *\n * Migration:\n *\n * ```diff\n * - import { SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs/SwitchableMarkdownInput'\n * - <SwitchableMarkdownInput value={md} onChange={setMd} isMarkdownEnabled={enabled} />\n * + import { RichEditor } from '@open-mercato/ui/primitives/rich-editor'\n * + <RichEditor value={html} onChange={setHtml} variant=\"basic\" />\n * ```\n *\n * For surfaces that genuinely need plain Markdown storage (e.g. agent prompts,\n * code-adjacent docs), keep using `SwitchableMarkdownInput` until a Markdown\n * mode lands on `RichEditor` \u2014 but do NOT introduce new Markdown surfaces.\n */\n\"use client\"\n\nimport * as React from 'react'\nimport type { PluggableList } from 'unified'\nimport MarkdownField from './MarkdownField'\n\n/** @deprecated See module-level JSDoc. */\nexport type SwitchableMarkdownInputProps = {\n value: string\n onChange: (value: string) => void\n isMarkdownEnabled: boolean\n disableMarkdown?: boolean\n height?: number\n placeholder?: string\n rows?: number\n textareaRef?: React.Ref<HTMLTextAreaElement>\n onTextareaInput?: React.FormEventHandler<HTMLTextAreaElement>\n textareaClassName?: string\n editorWrapperClassName?: string\n editorClassName?: string\n disabled?: boolean\n remarkPlugins?: PluggableList\n}\n\n/** @deprecated Use `RichEditor` from `@open-mercato/ui/primitives/rich-editor` for new code. See module JSDoc. */\nexport function SwitchableMarkdownInput({\n value,\n onChange,\n isMarkdownEnabled,\n disableMarkdown,\n placeholder,\n rows = 3,\n textareaRef,\n onTextareaInput,\n textareaClassName,\n disabled,\n}: SwitchableMarkdownInputProps) {\n const textareaClasses =\n textareaClassName\n ?? 'w-full resize-none overflow-hidden rounded-lg border border-muted-foreground/20 bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\n if (isMarkdownEnabled && !disableMarkdown) {\n return (\n <MarkdownField\n value={value}\n onChange={(nextValue) => onChange(typeof nextValue === 'string' ? nextValue : '')}\n />\n )\n }\n\n return (\n <textarea\n ref={textareaRef}\n rows={rows}\n className={textareaClasses}\n placeholder={placeholder}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onInput={onTextareaInput}\n disabled={disabled}\n />\n )\n}\n"],
5
+ "mappings": ";AAuEM;AAvCN,OAAO,mBAAmB;AAqBnB,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,kBACJ,qBACG;AAEL,MAAI,qBAAqB,CAAC,iBAAiB;AACzC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,CAAC,cAAc,SAAS,OAAO,cAAc,WAAW,YAAY,EAAE;AAAA;AAAA,IAClF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,SAAS;AAAA,MACT;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,35 @@
1
+ const NAMED_HTML_ENTITIES = {
2
+ amp: "&",
3
+ lt: "<",
4
+ gt: ">",
5
+ quot: '"',
6
+ apos: "'",
7
+ nbsp: " ",
8
+ hellip: "\u2026",
9
+ mdash: "\u2014",
10
+ ndash: "\u2013",
11
+ rsquo: "\u2019",
12
+ lsquo: "\u2018",
13
+ rdquo: "\u201D",
14
+ ldquo: "\u201C"
15
+ };
16
+ function decodeHtmlEntities(value) {
17
+ return value.replace(/&#(\d+);/g, (match, dec) => {
18
+ const code = Number(dec);
19
+ return Number.isFinite(code) ? String.fromCodePoint(code) : match;
20
+ }).replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => {
21
+ const code = parseInt(hex, 16);
22
+ return Number.isFinite(code) ? String.fromCodePoint(code) : match;
23
+ }).replace(
24
+ /&([a-zA-Z][a-zA-Z0-9]*);/g,
25
+ (match, name) => Object.prototype.hasOwnProperty.call(NAMED_HTML_ENTITIES, name) ? NAMED_HTML_ENTITIES[name] : match
26
+ );
27
+ }
1
28
  function markdownToPlainText(input) {
2
29
  if (!input) return "";
3
30
  let text = String(input);
4
31
  text = text.replace(/```[\s\S]*?```/g, " ");
32
+ text = text.replace(/<[^>]+>/g, " ");
5
33
  text = text.replace(/`([^`]+)`/g, "$1");
6
34
  text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, " ");
7
35
  text = text.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
@@ -15,6 +43,7 @@ function markdownToPlainText(input) {
15
43
  text = text.replace(/\*([^*]+)\*/g, "$1");
16
44
  text = text.replace(/_([^_]+)_/g, "$1");
17
45
  text = text.replace(/\|/g, " ");
46
+ text = decodeHtmlEntities(text);
18
47
  text = text.replace(/\s+/g, " ").trim();
19
48
  return text;
20
49
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/markdown/markdownToPlainText.ts"],
4
- "sourcesContent": ["export function markdownToPlainText(input: string | null | undefined): string {\n if (!input) return ''\n let text = String(input)\n text = text.replace(/```[\\s\\S]*?```/g, ' ')\n text = text.replace(/`([^`]+)`/g, '$1')\n text = text.replace(/!\\[[^\\]]*\\]\\([^)]*\\)/g, ' ')\n text = text.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, '$1')\n text = text.replace(/^>\\s?/gm, '')\n text = text.replace(/^#{1,6}\\s+/gm, '')\n text = text.replace(/^\\s*[-*+]\\s+/gm, '')\n text = text.replace(/^\\s*\\d+\\.\\s+/gm, '')\n text = text.replace(/~~([^~]+)~~/g, '$1')\n text = text.replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n text = text.replace(/__([^_]+)__/g, '$1')\n text = text.replace(/\\*([^*]+)\\*/g, '$1')\n text = text.replace(/_([^_]+)_/g, '$1')\n text = text.replace(/\\|/g, ' ')\n text = text.replace(/\\s+/g, ' ').trim()\n return text\n}\n"],
5
- "mappings": "AAAO,SAAS,oBAAoB,OAA0C;AAC5E,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,OAAO,KAAK;AACvB,SAAO,KAAK,QAAQ,mBAAmB,GAAG;AAC1C,SAAO,KAAK,QAAQ,cAAc,IAAI;AACtC,SAAO,KAAK,QAAQ,yBAAyB,GAAG;AAChD,SAAO,KAAK,QAAQ,0BAA0B,IAAI;AAClD,SAAO,KAAK,QAAQ,WAAW,EAAE;AACjC,SAAO,KAAK,QAAQ,gBAAgB,EAAE;AACtC,SAAO,KAAK,QAAQ,kBAAkB,EAAE;AACxC,SAAO,KAAK,QAAQ,kBAAkB,EAAE;AACxC,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,oBAAoB,IAAI;AAC5C,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,cAAc,IAAI;AACtC,SAAO,KAAK,QAAQ,OAAO,GAAG;AAC9B,SAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACtC,SAAO;AACT;",
4
+ "sourcesContent": ["const NAMED_HTML_ENTITIES: Record<string, string> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n nbsp: ' ',\n hellip: '\u2026',\n mdash: '\u2014',\n ndash: '\u2013',\n rsquo: '\u2019',\n lsquo: '\u2018',\n rdquo: '\u201D',\n ldquo: '\u201C',\n}\n\nfunction decodeHtmlEntities(value: string): string {\n return value\n .replace(/&#(\\d+);/g, (match, dec: string) => {\n const code = Number(dec)\n return Number.isFinite(code) ? String.fromCodePoint(code) : match\n })\n .replace(/&#x([0-9a-fA-F]+);/g, (match, hex: string) => {\n const code = parseInt(hex, 16)\n return Number.isFinite(code) ? String.fromCodePoint(code) : match\n })\n .replace(/&([a-zA-Z][a-zA-Z0-9]*);/g, (match, name: string) =>\n Object.prototype.hasOwnProperty.call(NAMED_HTML_ENTITIES, name) ? NAMED_HTML_ENTITIES[name] : match,\n )\n}\n\n/**\n * Converts a Markdown (or legacy HTML) string into a plain-text preview suitable for compact,\n * read-only contexts such as DataTable list cells. Strips Markdown syntax, strips HTML tags, and\n * decodes HTML entities, then collapses whitespace. Handling legacy HTML keeps list previews clean\n * for records authored before the editor moved back to Markdown \u2014 no data migration required.\n *\n * This is NOT a sanitizer \u2014 render rich content with the Markdown components, not this helper.\n */\nexport function markdownToPlainText(input: string | null | undefined): string {\n if (!input) return ''\n let text = String(input)\n text = text.replace(/```[\\s\\S]*?```/g, ' ')\n text = text.replace(/<[^>]+>/g, ' ')\n text = text.replace(/`([^`]+)`/g, '$1')\n text = text.replace(/!\\[[^\\]]*\\]\\([^)]*\\)/g, ' ')\n text = text.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, '$1')\n text = text.replace(/^>\\s?/gm, '')\n text = text.replace(/^#{1,6}\\s+/gm, '')\n text = text.replace(/^\\s*[-*+]\\s+/gm, '')\n text = text.replace(/^\\s*\\d+\\.\\s+/gm, '')\n text = text.replace(/~~([^~]+)~~/g, '$1')\n text = text.replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n text = text.replace(/__([^_]+)__/g, '$1')\n text = text.replace(/\\*([^*]+)\\*/g, '$1')\n text = text.replace(/_([^_]+)_/g, '$1')\n text = text.replace(/\\|/g, ' ')\n text = decodeHtmlEntities(text)\n text = text.replace(/\\s+/g, ' ').trim()\n return text\n}\n"],
5
+ "mappings": "AAAA,MAAM,sBAA8C;AAAA,EAClD,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MACJ,QAAQ,aAAa,CAAC,OAAO,QAAgB;AAC5C,UAAM,OAAO,OAAO,GAAG;AACvB,WAAO,OAAO,SAAS,IAAI,IAAI,OAAO,cAAc,IAAI,IAAI;AAAA,EAC9D,CAAC,EACA,QAAQ,uBAAuB,CAAC,OAAO,QAAgB;AACtD,UAAM,OAAO,SAAS,KAAK,EAAE;AAC7B,WAAO,OAAO,SAAS,IAAI,IAAI,OAAO,cAAc,IAAI,IAAI;AAAA,EAC9D,CAAC,EACA;AAAA,IAAQ;AAAA,IAA6B,CAAC,OAAO,SAC5C,OAAO,UAAU,eAAe,KAAK,qBAAqB,IAAI,IAAI,oBAAoB,IAAI,IAAI;AAAA,EAChG;AACJ;AAUO,SAAS,oBAAoB,OAA0C;AAC5E,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,OAAO,KAAK;AACvB,SAAO,KAAK,QAAQ,mBAAmB,GAAG;AAC1C,SAAO,KAAK,QAAQ,YAAY,GAAG;AACnC,SAAO,KAAK,QAAQ,cAAc,IAAI;AACtC,SAAO,KAAK,QAAQ,yBAAyB,GAAG;AAChD,SAAO,KAAK,QAAQ,0BAA0B,IAAI;AAClD,SAAO,KAAK,QAAQ,WAAW,EAAE;AACjC,SAAO,KAAK,QAAQ,gBAAgB,EAAE;AACtC,SAAO,KAAK,QAAQ,kBAAkB,EAAE;AACxC,SAAO,KAAK,QAAQ,kBAAkB,EAAE;AACxC,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,oBAAoB,IAAI;AAC5C,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,gBAAgB,IAAI;AACxC,SAAO,KAAK,QAAQ,cAAc,IAAI;AACtC,SAAO,KAAK,QAAQ,OAAO,GAAG;AAC9B,SAAO,mBAAmB,IAAI;AAC9B,SAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACtC,SAAO;AACT;",
6
6
  "names": []
7
7
  }