@open-mercato/ui 0.6.8-develop.7005.1.70a5747c3e → 0.6.8-develop.7012.1.1dbd6f5fbd

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.
@@ -134,7 +134,10 @@ function NotesSectionImpl({
134
134
  iconSuggestions,
135
135
  readMarkdownPreference,
136
136
  writeMarkdownPreference,
137
- disableMarkdown
137
+ disableMarkdown,
138
+ forceMarkdown,
139
+ hideMarkdownToggle,
140
+ disableAppearance
138
141
  }) {
139
142
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
140
143
  const t = React.useMemo(() => translator ?? ((key, fallback) => fallback ?? key), [translator]);
@@ -246,13 +249,16 @@ function NotesSectionImpl({
246
249
  const [draftIcon, setDraftIcon] = React.useState(null);
247
250
  const [draftColor, setDraftColor] = React.useState(null);
248
251
  const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false);
252
+ const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled);
253
+ const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle;
254
+ const showAppearanceControls = !disableAppearance;
249
255
  const textareaRef = React.useRef(null);
250
256
  const formRef = React.useRef(null);
251
257
  const focusComposer = React.useCallback(() => {
252
258
  if (!hasEntity) return;
253
259
  setComposerOpen(true);
254
260
  window.requestAnimationFrame(() => {
255
- if (isMarkdownEnabled) {
261
+ if (isMarkdownActive) {
256
262
  const markdownTextarea = formRef.current?.querySelector("textarea");
257
263
  if (markdownTextarea instanceof HTMLTextAreaElement) {
258
264
  markdownTextarea.focus();
@@ -265,7 +271,7 @@ function NotesSectionImpl({
265
271
  element.focus();
266
272
  element.scrollIntoView({ behavior: "smooth", block: "center" });
267
273
  });
268
- }, [formRef, hasEntity, isMarkdownEnabled]);
274
+ }, [formRef, hasEntity, isMarkdownActive]);
269
275
  const [appearanceDialogState, setAppearanceDialogState] = React.useState(null);
270
276
  const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false);
271
277
  const [appearanceDialogError, setAppearanceDialogError] = React.useState(null);
@@ -369,7 +375,7 @@ function NotesSectionImpl({
369
375
  }, []);
370
376
  React.useEffect(() => {
371
377
  adjustTextareaSize(textareaRef.current);
372
- }, [adjustTextareaSize, draftBody, isMarkdownEnabled, composerOpen]);
378
+ }, [adjustTextareaSize, draftBody, isMarkdownActive, composerOpen]);
373
379
  React.useEffect(() => {
374
380
  const preference = readMarkdownPreference ? readMarkdownPreference() : null;
375
381
  if (preference !== null) {
@@ -731,7 +737,7 @@ function NotesSectionImpl({
731
737
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
732
738
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: label("addLabel") }),
733
739
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [
734
- /* @__PURE__ */ jsxs(
740
+ showAppearanceControls ? /* @__PURE__ */ jsxs(
735
741
  Button,
736
742
  {
737
743
  type: "button",
@@ -747,19 +753,19 @@ function NotesSectionImpl({
747
753
  /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
748
754
  ]
749
755
  }
750
- ),
751
- disableMarkdown ? null : /* @__PURE__ */ jsx(
756
+ ) : null,
757
+ showMarkdownToggle ? /* @__PURE__ */ jsx(
752
758
  Button,
753
759
  {
754
760
  type: "button",
755
- variant: isMarkdownEnabled ? "secondary" : "ghost",
761
+ variant: isMarkdownActive ? "secondary" : "ghost",
756
762
  size: "icon",
757
763
  onClick: handleMarkdownToggle,
758
- "aria-pressed": isMarkdownEnabled,
764
+ "aria-pressed": isMarkdownActive,
759
765
  disabled: isSubmitting || isLoading,
760
766
  children: /* @__PURE__ */ jsx(FileCode, { className: "h-4 w-4" })
761
767
  }
762
- ),
768
+ ) : null,
763
769
  /* @__PURE__ */ jsx(
764
770
  Button,
765
771
  {
@@ -829,7 +835,7 @@ function NotesSectionImpl({
829
835
  {
830
836
  value: draftBody,
831
837
  onChange: setDraftBody,
832
- isMarkdownEnabled,
838
+ isMarkdownEnabled: isMarkdownActive,
833
839
  disableMarkdown,
834
840
  rows: 1,
835
841
  placeholder: label("placeholder"),
@@ -952,7 +958,7 @@ function NotesSectionImpl({
952
958
  children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
953
959
  }
954
960
  ),
955
- /* @__PURE__ */ jsx(
961
+ showAppearanceControls ? /* @__PURE__ */ jsx(
956
962
  Button,
957
963
  {
958
964
  type: "button",
@@ -971,7 +977,7 @@ function NotesSectionImpl({
971
977
  disabled: appearanceDialogSaving && editingAppearanceNoteId === note.id,
972
978
  children: isAppearanceSaving ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Palette, { className: "h-4 w-4" })
973
979
  }
974
- ),
980
+ ) : null,
975
981
  /* @__PURE__ */ jsx(
976
982
  Button,
977
983
  {
@@ -996,7 +1002,7 @@ function NotesSectionImpl({
996
1002
  {
997
1003
  value: contentEditor.value,
998
1004
  onChange: (nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue })),
999
- isMarkdownEnabled,
1005
+ isMarkdownEnabled: isMarkdownActive,
1000
1006
  disableMarkdown,
1001
1007
  rows: 3,
1002
1008
  textareaRef: contentTextareaRef,
@@ -1012,19 +1018,19 @@ function NotesSectionImpl({
1012
1018
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1013
1019
  label("saving")
1014
1020
  ] }) : inlineLabel("saveShortcut") }),
1015
- disableMarkdown ? null : /* @__PURE__ */ jsx(
1021
+ showMarkdownToggle ? /* @__PURE__ */ jsx(
1016
1022
  Button,
1017
1023
  {
1018
1024
  type: "button",
1019
1025
  variant: "ghost",
1020
1026
  size: "icon",
1021
1027
  onClick: handleMarkdownToggle,
1022
- "aria-pressed": isMarkdownEnabled,
1023
- className: isMarkdownEnabled ? "text-primary" : void 0,
1028
+ "aria-pressed": isMarkdownActive,
1029
+ className: isMarkdownActive ? "text-primary" : void 0,
1024
1030
  disabled: contentSavingId === note.id,
1025
1031
  children: /* @__PURE__ */ jsx(FileCode, { className: "h-4 w-4" })
1026
1032
  }
1027
- ),
1033
+ ) : null,
1028
1034
  /* @__PURE__ */ jsx(
1029
1035
  Button,
1030
1036
  {
@@ -1070,7 +1076,7 @@ function NotesSectionImpl({
1070
1076
  ),
1071
1077
  isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleLoadMore, children: loadMoreLabel }) })
1072
1078
  ] }),
1073
- /* @__PURE__ */ jsx(
1079
+ showAppearanceControls ? /* @__PURE__ */ jsx(
1074
1080
  AppearanceDialog,
1075
1081
  {
1076
1082
  open: appearanceDialogOpen,
@@ -1091,7 +1097,7 @@ function NotesSectionImpl({
1091
1097
  savingLabel: appearanceDialogSavingLabel,
1092
1098
  cancelLabel: label("appearance.cancel")
1093
1099
  }
1094
- ),
1100
+ ) : null,
1095
1101
  ConfirmDialogElement
1096
1102
  ] });
1097
1103
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/detail/NotesSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { PluggableList } from 'unified'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { AppearanceDialog } from '@open-mercato/core/modules/customers/components/detail/AppearanceDialog'\nimport type { IconOption } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { flash } from '../FlashMessages'\nimport { surfaceRecordConflict } from '../conflicts'\nimport { SwitchableMarkdownInput } from '../inputs/SwitchableMarkdownInput'\nimport { ErrorMessage } from './ErrorMessage'\nimport { LoadingMessage } from './LoadingMessage'\nimport { TabEmptyState } from './TabEmptyState'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nconst NOTES_PAGE_SIZE = 20\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type CommentSummary = {\n id: string\n body: string\n createdAt: string\n updatedAt?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesCreatePayload = {\n entityId: string\n body: string\n appearanceIcon: string | null\n appearanceColor: string | null\n dealId?: string | null\n}\n\nexport type NotesUpdatePayload = {\n body?: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId: string | null; context?: C }) => Promise<CommentSummary[]>\n listPage?: (params: {\n entityId: string | null\n dealId: string | null\n page: number\n pageSize: number\n context?: C\n }) => Promise<{\n items: CommentSummary[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n }>\n create: (params: NotesCreatePayload & { context?: C }) => Promise<Partial<CommentSummary> | void>\n update: (params: { id: string; patch: NotesUpdatePayload; updatedAt?: string | null; context?: C }) => Promise<void>\n delete: (params: { id: string; updatedAt?: string | null; context?: C }) => Promise<void>\n}\n\ntype RenderIconFn = (icon: string, className?: string) => React.ReactNode\ntype RenderColorFn = (color: string, className?: string) => React.ReactNode\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nfunction generateTempId() {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n return `tmp_${Math.random().toString(36).slice(2)}`\n}\n\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {icon && renderIcon ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/50', wrapperSize].join(' ')}>\n {renderIcon(icon, iconSizeClass)}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type NotesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n emptyLabel: string\n viewerUserId: string | null\n viewerName?: string | null\n viewerEmail?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n labelPrefix?: string\n inlineLabelPrefix?: string\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n dataAdapter: NotesDataAdapter<C>\n dataContext?: C\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n iconSuggestions?: IconOption[]\n readMarkdownPreference?: () => boolean | null\n writeMarkdownPreference?: (value: boolean) => void\n disableMarkdown?: boolean\n}\n\nexport function sanitizeHexColor(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n return /^#([0-9a-f]{6})$/i.test(trimmed) ? trimmed.toLowerCase() : null\n}\n\nexport function mapCommentSummary(input: unknown): CommentSummary {\n const data = (typeof input === 'object' && input !== null ? input : {}) as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : generateTempId()\n const body = typeof data.body === 'string' ? data.body : ''\n const createdAt =\n typeof data.createdAt === 'string'\n ? data.createdAt\n : typeof data.created_at === 'string'\n ? data.created_at\n : new Date().toISOString()\n const updatedAt =\n typeof data.updatedAt === 'string'\n ? data.updatedAt\n : typeof data.updated_at === 'string'\n ? data.updated_at\n : null\n const authorUserId =\n typeof data.authorUserId === 'string'\n ? data.authorUserId\n : typeof data.author_user_id === 'string'\n ? data.author_user_id\n : null\n const authorName =\n typeof data.authorName === 'string'\n ? data.authorName\n : typeof data.author_name === 'string'\n ? data.author_name\n : null\n const authorEmail =\n typeof data.authorEmail === 'string'\n ? data.authorEmail\n : typeof data.author_email === 'string'\n ? data.author_email\n : null\n const dealId =\n typeof data.dealId === 'string'\n ? data.dealId\n : typeof data.deal_id === 'string'\n ? data.deal_id\n : null\n const dealTitle =\n typeof data.dealTitle === 'string'\n ? data.dealTitle\n : typeof data.deal_title === 'string'\n ? data.deal_title\n : null\n const appearanceIcon =\n typeof data.appearanceIcon === 'string'\n ? data.appearanceIcon\n : typeof data.appearance_icon === 'string'\n ? data.appearance_icon\n : null\n const appearanceColor =\n typeof data.appearanceColor === 'string'\n ? data.appearanceColor\n : typeof data.appearance_color === 'string'\n ? data.appearance_color\n : null\n return {\n id,\n body,\n createdAt,\n updatedAt,\n authorUserId,\n authorName,\n authorEmail,\n dealId,\n dealTitle,\n appearanceIcon,\n appearanceColor,\n }\n}\n\nfunction NotesSectionImpl<C = unknown>({\n entityId,\n dealId,\n emptyLabel,\n viewerUserId,\n viewerName,\n viewerEmail,\n addActionLabel,\n emptyState,\n onActionChange,\n translator,\n labelPrefix = 'customers.people.detail.notes',\n inlineLabelPrefix = 'customers.people.detail.inline',\n onLoadingChange,\n dealOptions,\n entityOptions,\n dataAdapter,\n dataContext,\n renderIcon,\n renderColor,\n iconSuggestions,\n readMarkdownPreference,\n writeMarkdownPreference,\n disableMarkdown,\n}: NotesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n const inlineLabel = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${inlineLabelPrefix}.${suffix}`, fallback, params),\n [inlineLabelPrefix, t],\n )\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const dealLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n normalizedDealOptions.forEach((option) => {\n map.set(option.id, option.label)\n })\n return map\n }, [normalizedDealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const [selectedDealId, setSelectedDealId] = React.useState<string>(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n return initial\n })\n React.useEffect(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n if (initial !== selectedDealId) {\n setSelectedDealId(initial)\n }\n }, [dealId, selectedDealId])\n\n const [selectedEntityId, setSelectedEntityId] = React.useState<string>(() => {\n if (normalizedEntityOptions.length) return normalizedEntityOptions[0].id\n return typeof entityId === 'string' ? entityId : ''\n })\n React.useEffect(() => {\n if (normalizedEntityOptions.length) {\n if (!normalizedEntityOptions.some((option) => option.id === selectedEntityId)) {\n setSelectedEntityId(normalizedEntityOptions[0].id)\n }\n } else {\n const initial = typeof entityId === 'string' ? entityId : ''\n if (initial !== selectedEntityId) {\n setSelectedEntityId(initial)\n }\n }\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedEntityId = React.useMemo(() => {\n if (normalizedEntityOptions.length) return selectedEntityId\n return typeof entityId === 'string' ? entityId : ''\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedDealId = React.useMemo(() => {\n const trimmed = typeof selectedDealId === 'string' ? selectedDealId.trim() : ''\n return trimmed\n }, [selectedDealId])\n\n const hasEntity = resolvedEntityId.length > 0\n\n const [notes, setNotes] = React.useState<CommentSummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => Boolean(entityId || dealId))\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const pendingCounterRef = React.useRef(0)\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChange?.(true)\n }\n }, [onLoadingChange])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChange?.(false)\n }\n }, [onLoadingChange])\n\n const [composerOpen, setComposerOpen] = React.useState(false)\n const [draftBody, setDraftBody] = React.useState('')\n const [draftIcon, setDraftIcon] = React.useState<string | null>(null)\n const [draftColor, setDraftColor] = React.useState<string | null>(null)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const formRef = React.useRef<HTMLFormElement | null>(null)\n const focusComposer = React.useCallback(() => {\n if (!hasEntity) return\n setComposerOpen(true)\n window.requestAnimationFrame(() => {\n if (isMarkdownEnabled) {\n const markdownTextarea = formRef.current?.querySelector('textarea')\n if (markdownTextarea instanceof HTMLTextAreaElement) {\n markdownTextarea.focus()\n markdownTextarea.scrollIntoView({ behavior: 'smooth', block: 'center' })\n return\n }\n }\n const element = textareaRef.current\n if (!element) return\n element.focus()\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n }, [formRef, hasEntity, isMarkdownEnabled])\n const [appearanceDialogState, setAppearanceDialogState] = React.useState<\n | { mode: 'create'; icon: string | null; color: string | null }\n | { mode: 'edit'; noteId: string; icon: string | null; color: string | null }\n | null\n >(null)\n const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false)\n const [appearanceDialogError, setAppearanceDialogError] = React.useState<string | null>(null)\n const [contentEditor, setContentEditor] = React.useState<{ id: string; value: string }>({ id: '', value: '' })\n const [contentSavingId, setContentSavingId] = React.useState<string | null>(null)\n const [contentError, setContentError] = React.useState<string | null>(null)\n const contentTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const [currentPage, setCurrentPage] = React.useState(1)\n // Short-page termination instead of `currentPage >= totalPages` \u2014 see\n // `hasMoreFromPage`. `/api/customers/comments` is a `makeCrudRoute` list, so\n // a page past the end comes back empty rather than clamped.\n const [hasMore, setHasMore] = React.useState(false)\n const [deletingNoteId, setDeletingNoteId] = React.useState<string | null>(null)\n const pagedMode = typeof dataAdapter.listPage === 'function'\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n if (!queryEntityId && !queryDealId) {\n setNotes([])\n setLoadError(null)\n setIsLoading(false)\n setCurrentPage(1)\n setHasMore(false)\n return\n }\n let cancelled = false\n setIsLoading(true)\n setLoadError(null)\n pushLoading()\n async function loadNotes() {\n try {\n if (dataAdapter.listPage) {\n const pageResult = await dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(pageResult.items)\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n return\n }\n const mapped = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(mapped)\n setCurrentPage(1)\n setHasMore(false)\n } catch (err) {\n if (cancelled) return\n const message =\n err instanceof Error ? err.message : label('loadError', 'Failed to load notes.')\n setNotes([])\n setLoadError(message)\n setCurrentPage(1)\n setHasMore(false)\n flash(message, 'error')\n } finally {\n if (!cancelled) setIsLoading(false)\n popLoading()\n }\n }\n loadNotes().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [dataAdapter, dataContext, dealId, entityId, label, popLoading, pushLoading])\n\n const youLabel = label('you', 'You')\n const viewerLabel = React.useMemo(() => viewerName ?? viewerEmail ?? null, [viewerEmail, viewerName])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => {\n const next = !prev\n if (writeMarkdownPreference) {\n writeMarkdownPreference(next)\n }\n return next\n })\n }, [writeMarkdownPreference])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!notes.length) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || isLoading || !hasEntity,\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n })\n return () => onActionChange(null)\n }, [onActionChange, addActionLabel, focusComposer, hasEntity, isLoading, isSubmitting, notes.length])\n\n const adjustTextareaSize = React.useCallback((element: HTMLTextAreaElement | null) => {\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [])\n\n React.useEffect(() => {\n adjustTextareaSize(textareaRef.current)\n }, [adjustTextareaSize, draftBody, isMarkdownEnabled, composerOpen])\n\n React.useEffect(() => {\n const preference = readMarkdownPreference ? readMarkdownPreference() : null\n if (preference !== null) {\n setIsMarkdownEnabled(preference)\n }\n }, [readMarkdownPreference])\n\n React.useEffect(() => {\n if (pagedMode) {\n setVisibleCount(notes.length)\n return\n }\n if (!notes.length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, notes.length)\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(Math.max(prev, baseline), notes.length)\n })\n }, [notes.length, pagedMode])\n\n React.useEffect(() => {\n if (hasEntity) return\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }, [hasEntity])\n\n const visibleNotes = React.useMemo(\n () => (pagedMode ? notes : notes.slice(0, visibleCount)),\n [notes, pagedMode, visibleCount],\n )\n const hasVisibleNotes = React.useMemo(\n () => (pagedMode ? notes.length > 0 : visibleCount > 0),\n [notes.length, pagedMode, visibleCount],\n )\n\n const loadMoreLabel = label('loadMore')\n\n const handleCreateNote = React.useCallback(\n async (input: { body: string; appearanceIcon: string | null; appearanceColor: string | null }) => {\n if (!hasEntity || !resolvedEntityId) {\n flash(label('entityMissing', 'Unable to determine current person.'), 'error')\n return false\n }\n const body = input.body.trim()\n const strippedBody = body\n .replace(/^[\\s#\\-*>_~`|+\\\\\\n\\r]+$/gm, '')\n .replace(/\\s+/g, '')\n if (!body || !strippedBody.length) {\n focusComposer()\n return false\n }\n const icon = input.appearanceIcon && input.appearanceIcon.trim().length ? input.appearanceIcon.trim() : null\n const color = sanitizeHexColor(input.appearanceColor)\n const targetDealId = resolvedDealId.length ? resolvedDealId : null\n const dealLabel = targetDealId ? dealLabelMap.get(targetDealId) ?? null : null\n setIsSubmitting(true)\n pushLoading()\n try {\n const responseBody =\n (await dataAdapter.create({\n entityId: resolvedEntityId,\n body,\n appearanceIcon: icon,\n appearanceColor: color,\n dealId: targetDealId,\n context: dataContext,\n })) ?? {}\n setNotes((prev) => {\n const viewerId = viewerUserId ?? null\n const resolvedAuthorId =\n typeof responseBody?.authorUserId === 'string' ? responseBody.authorUserId : viewerId ?? null\n const resolvedAuthorName = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return youLabel\n }\n return typeof responseBody?.authorName === 'string' ? responseBody.authorName : viewerLabel\n })()\n const resolvedAuthorEmail = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return viewerEmail ?? null\n }\n return typeof responseBody?.authorEmail === 'string' ? responseBody.authorEmail : null\n })()\n const newNote: CommentSummary = {\n id: typeof responseBody?.id === 'string' ? responseBody.id : generateTempId(),\n body,\n createdAt: new Date().toISOString(),\n authorUserId: resolvedAuthorId,\n authorName: resolvedAuthorName,\n authorEmail: resolvedAuthorEmail,\n dealId: targetDealId,\n dealTitle: dealLabel,\n appearanceIcon: icon,\n appearanceColor: color,\n }\n return [newNote, ...prev]\n })\n setVisibleCount((prev) => Math.max(prev, 1))\n flash(label('success'), 'success')\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : label('error')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n return false\n } finally {\n setIsSubmitting(false)\n popLoading()\n }\n },\n [dataAdapter, dataContext, dealLabelMap, focusComposer, hasEntity, popLoading, pushLoading, resolvedDealId, resolvedEntityId, t, viewerEmail, viewerLabel, viewerUserId, youLabel],\n )\n\n const handleUpdateNote = React.useCallback(\n async (noteId: string, patch: { body?: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const sanitizedBody = patch.body\n const sanitizedIcon =\n patch.appearanceIcon !== undefined && patch.appearanceIcon !== null && patch.appearanceIcon.trim().length\n ? patch.appearanceIcon.trim()\n : patch.appearanceIcon === null\n ? null\n : undefined\n const sanitizedColor =\n patch.appearanceColor !== undefined ? sanitizeHexColor(patch.appearanceColor ?? null) : undefined\n const noteUpdatedAt = notes.find((comment) => comment.id === noteId)?.updatedAt ?? null\n try {\n await dataAdapter.update({\n id: noteId,\n patch: {\n body: sanitizedBody,\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n },\n updatedAt: noteUpdatedAt,\n context: dataContext,\n })\n setNotes((prev) => {\n const nextComments = prev.map((comment) => {\n if (comment.id !== noteId) return comment\n const next = { ...comment }\n if (sanitizedBody !== undefined) next.body = sanitizedBody\n if (sanitizedIcon !== undefined) next.appearanceIcon = sanitizedIcon ?? null\n if (sanitizedColor !== undefined) next.appearanceColor = sanitizedColor ?? null\n return next\n })\n return nextComments\n })\n flash(label('updateSuccess'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : label('updateError')\n if (!surfaceRecordConflict(error, t)) flash(message, 'error')\n throw error instanceof Error ? error : new Error(message)\n }\n },\n [dataAdapter, dataContext, notes, t],\n )\n\n const handleDeleteNote = React.useCallback(\n async (note: CommentSummary) => {\n const confirmed = await confirm({\n title: label('deleteConfirm', 'Delete this note? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeletingNoteId(note.id)\n pushLoading()\n try {\n await dataAdapter.delete({ id: note.id, updatedAt: note.updatedAt ?? null, context: dataContext })\n setNotes((prev) => prev.filter((existing) => existing.id !== note.id))\n if (pagedMode) {\n setVisibleCount((prev) => Math.max(0, prev - 1))\n }\n flash(label('deleteSuccess', 'Note deleted'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : label('deleteError', 'Failed to delete note')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n } finally {\n setDeletingNoteId(null)\n popLoading()\n }\n },\n [confirm, dataAdapter, dataContext, label, popLoading, pushLoading, t],\n )\n\n const handleSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const created = await handleCreateNote({\n body: draftBody,\n appearanceIcon: draftIcon,\n appearanceColor: draftColor,\n })\n if (created) {\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }\n },\n [draftBody, draftColor, draftIcon, handleCreateNote],\n )\n\n const handleLoadMore = React.useCallback(() => {\n if (pagedMode && dataAdapter.listPage) {\n if (!hasMore || isLoading) return\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n setIsLoading(true)\n pushLoading()\n void dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: currentPage + 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n .then((pageResult) => {\n setNotes((prev) => {\n const merged = new Map(prev.map((note) => [note.id, note]))\n pageResult.items.forEach((note) => merged.set(note.id, note))\n return Array.from(merged.values())\n })\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n })\n .catch((error) => {\n const message =\n error instanceof Error ? error.message : label('loadError', 'Failed to load notes.')\n flash(message, 'error')\n })\n .finally(() => {\n setIsLoading(false)\n popLoading()\n })\n return\n }\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(prev + 5, notes.length)\n })\n }, [currentPage, dataAdapter, dataContext, dealId, entityId, flash, hasMore, isLoading, label, notes.length, pagedMode, popLoading, pushLoading])\n\n const handleAppearanceDialogSubmit = React.useCallback(async () => {\n if (!appearanceDialogState) return\n setAppearanceDialogError(null)\n const sanitizedIcon =\n appearanceDialogState.icon && appearanceDialogState.icon.trim().length\n ? appearanceDialogState.icon.trim()\n : null\n const sanitizedColor = sanitizeHexColor(appearanceDialogState.color ?? null)\n if (appearanceDialogState.mode === 'create') {\n setDraftIcon(sanitizedIcon)\n setDraftColor(sanitizedColor)\n setAppearanceDialogState(null)\n return\n }\n setAppearanceDialogSaving(true)\n try {\n await handleUpdateNote(appearanceDialogState.noteId, {\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n })\n setAppearanceDialogState(null)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : label('appearance.error', 'Failed to update appearance.')\n setAppearanceDialogError(message)\n } finally {\n setAppearanceDialogSaving(false)\n }\n }, [appearanceDialogState, handleUpdateNote, t])\n\n const handleAppearanceDialogClose = React.useCallback(() => {\n if (appearanceDialogSaving) return\n setAppearanceDialogState(null)\n setAppearanceDialogError(null)\n }, [appearanceDialogSaving])\n\n const handleContentSave = React.useCallback(async () => {\n if (!contentEditor.id) return\n const trimmed = contentEditor.value.trim()\n if (!trimmed) {\n setContentError(label('updateError', 'Failed to update note'))\n return\n }\n setContentSavingId(contentEditor.id)\n setContentError(null)\n try {\n await handleUpdateNote(contentEditor.id, { body: trimmed })\n setContentEditor({ id: '', value: '' })\n } catch (err) {\n const message =\n err instanceof Error ? err.message : label('updateError', 'Failed to update note')\n setContentError(message)\n } finally {\n setContentSavingId(null)\n }\n }, [contentEditor, handleUpdateNote, t])\n\n const handleContentEditorKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!contentEditor.id) return\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!contentSavingId) void handleContentSave()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setContentEditor({ id: '', value: '' })\n setContentError(null)\n }\n },\n [contentEditor.id, contentSavingId, handleContentSave],\n )\n\n const handleComposerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n formRef.current?.requestSubmit()\n }\n },\n [],\n )\n\n const handleContentKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>, note: CommentSummary) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setContentEditor({ id: note.id, value: note.body })\n }\n },\n [],\n )\n\n const noteAuthorLabel = React.useCallback(\n (note: CommentSummary) => {\n if (note.authorUserId && viewerUserId && note.authorUserId === viewerUserId) {\n return youLabel\n }\n return note.authorName ?? note.authorEmail ?? youLabel\n },\n [viewerUserId, youLabel],\n )\n\n const noteAppearanceLabels = React.useMemo<AppearanceSelectorLabels>(\n () => ({\n colorLabel: label('appearance.colorLabel'),\n colorHelp: label('appearance.colorHelp'),\n colorClearLabel: label('appearance.clearColor'),\n iconLabel: label('appearance.iconLabel'),\n iconPlaceholder: label('appearance.iconPlaceholder'),\n iconPickerTriggerLabel: label('appearance.iconPicker'),\n iconSearchPlaceholder: label('appearance.iconSearchPlaceholder'),\n iconSearchEmptyLabel: label('appearance.iconSearchEmpty'),\n iconSuggestionsLabel: label('appearance.iconSuggestions'),\n iconClearLabel: label('appearance.iconClear'),\n previewEmptyLabel: label('appearance.previewEmpty'),\n }),\n [label],\n )\n\n const composerAuthor = React.useMemo(\n () => youLabel,\n [youLabel],\n )\n const composerHasAppearance = Boolean(draftIcon) || Boolean(draftColor)\n const appearanceDialogOpen = appearanceDialogState !== null\n const editingAppearanceNoteId =\n appearanceDialogState?.mode === 'edit' ? appearanceDialogState.noteId : null\n const addNoteShortcutLabel = label('addShortcut', 'Add note \u2318\u23CE / Ctrl+Enter')\n const saveAppearanceShortcutLabel = label('appearance.saveShortcut', 'Save appearance \u2318\u23CE / Ctrl+Enter')\n const composerSubmitLabel = addNoteShortcutLabel\n const appearanceDialogPrimaryLabel = saveAppearanceShortcutLabel\n const appearanceDialogSavingLabel =\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.saving')\n : label('saving', 'Saving note\u2026')\n\n return (\n <div className=\"mt-0 space-y-2\">\n <div\n className={[\n 'overflow-hidden rounded-xl transition-all duration-300 ease-out',\n composerOpen ? 'max-h-[1200px] bg-muted/30 p-4 opacity-100' : 'pointer-events-none max-h-0 p-0 opacity-0',\n ].join(' ')}\n aria-hidden={!composerOpen}\n >\n {composerOpen ? (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n onKeyDown={handleComposerKeyDown}\n className=\"space-y-3\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-sm font-medium\">{label('addLabel')}</h3>\n <div className=\"flex flex-wrap items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n setAppearanceDialogError(null)\n setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })\n }}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <span className=\"sr-only\">{label('appearance.toggleOpen', 'Customize appearance')}</span>\n <Palette className=\"h-4 w-4\" />\n </Button>\n {disableMarkdown ? null : (\n <Button\n type=\"button\"\n variant={isMarkdownEnabled ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n disabled={isSubmitting || isLoading}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting || isLoading}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n {(normalizedEntityOptions.length || normalizedDealOptions.length) ? (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {normalizedEntityOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-entity-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.entity', 'Assign to customer')}\n </label>\n <Select\n value={selectedEntityId || undefined}\n onValueChange={(next) => setSelectedEntityId(next ?? '')}\n disabled={isSubmitting || isLoading || !normalizedEntityOptions.length}\n >\n <SelectTrigger id=\"note-entity-select\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {normalizedEntityOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n {normalizedDealOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-deal-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.deal', 'Link to deal (optional)')}\n </label>\n <Select\n value={selectedDealId || undefined}\n onValueChange={(next) => setSelectedDealId(next ?? '')}\n disabled={isSubmitting || isLoading}\n >\n <SelectTrigger id=\"note-deal-select\">\n <SelectValue placeholder={label('fields.dealPlaceholder', 'No linked deal')} />\n </SelectTrigger>\n <SelectContent>\n {normalizedDealOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n ) : null}\n <SwitchableMarkdownInput\n value={draftBody}\n onChange={setDraftBody}\n isMarkdownEnabled={isMarkdownEnabled}\n disableMarkdown={disableMarkdown}\n rows={1}\n placeholder={label('placeholder')}\n textareaRef={textareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n disabled={isSubmitting || isLoading || !hasEntity}\n remarkPlugins={markdownPlugins}\n />\n {composerHasAppearance ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-dashed border-muted-foreground/40 px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n {draftIcon && renderIcon ? (\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-muted/50\">\n {renderIcon(draftIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <span className=\"font-semibold text-foreground\">{composerAuthor}</span>\n {draftColor && renderColor ? (\n <span className=\"flex items-center gap-2\">\n {renderColor(draftColor, 'h-3.5 w-3.5 rounded-full border border-border')}\n <span className=\"text-xs font-medium uppercase text-muted-foreground\">{draftColor}</span>\n </span>\n ) : null}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting}\n >\n {label('appearance.clearAll', 'Clear')}\n </Button>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n {isSubmitting ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {composerSubmitLabel}\n </Button>\n </div>\n </form>\n ) : null}\n </div>\n\n {loadError ? <ErrorMessage label={loadError} className=\"mt-3\" /> : null}\n\n <div className=\"space-y-3\">\n {!composerOpen && hasVisibleNotes && !onActionChange ? (\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={focusComposer}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <Plus className=\"size-4\" />\n {addActionLabel}\n </Button>\n </div>\n ) : null}\n {isLoading ? (\n <LoadingMessage\n label={label('loading', 'Loading notes\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : hasVisibleNotes ? (\n visibleNotes.map((note) => {\n const author = noteAuthorLabel(note)\n const isAppearanceSaving = appearanceDialogSaving && editingAppearanceNoteId === note.id\n const isEditingContent = contentEditor.id === note.id\n const displayIcon = note.appearanceIcon ?? null\n const displayColor = note.appearanceColor ?? null\n const timestampValue = note.createdAt\n const fallbackTimestampLabel = formatDateTime(note.createdAt) ?? emptyLabel\n return (\n <div key={note.id} className=\"group space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={author}\n timestamp={timestampValue}\n fallbackTimestampLabel={fallbackTimestampLabel}\n icon={displayIcon}\n color={displayColor}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {note.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <a\n href={`/backend/customers/deals/${encodeURIComponent(note.dealId)}`}\n className=\"font-medium text-foreground hover:underline\"\n >\n {note.dealTitle && note.dealTitle.length\n ? note.dealTitle\n : label('linkedDeal', 'Linked deal')}\n </a>\n </div>\n ) : null}\n </div>\n <div\n className={`flex items-center gap-2 transition-opacity ${\n isEditingContent ? 'opacity-100' : 'opacity-100 md:opacity-0 md:group-hover:opacity-100 focus-within:opacity-100'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n setAppearanceDialogError(null)\n setAppearanceDialogState({\n mode: 'edit',\n noteId: note.id,\n icon: note.appearanceIcon ?? null,\n color: note.appearanceColor ?? null,\n })\n }}\n disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}\n >\n {isAppearanceSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Palette className=\"h-4 w-4\" />}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n void handleDeleteNote(note)\n }}\n disabled={deletingNoteId === note.id}\n >\n {deletingNoteId === note.id ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {isEditingContent ? (\n <div className=\"space-y-2\" onKeyDown={handleContentEditorKeyDown}>\n <SwitchableMarkdownInput\n value={contentEditor.value}\n onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}\n isMarkdownEnabled={isMarkdownEnabled}\n disableMarkdown={disableMarkdown}\n rows={3}\n textareaRef={contentTextareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n textareaClassName=\"w-full resize-none overflow-hidden rounded-md border border-border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n editorWrapperClassName=\"w-full rounded-md border border-muted-foreground/20 bg-background p-2\"\n remarkPlugins={markdownPlugins}\n />\n {contentError ? <p className=\"text-xs text-status-error-text\">{contentError}</p> : null}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleContentSave} disabled={contentSavingId === note.id}>\n {contentSavingId === note.id ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {label('saving')}\n </>\n ) : (\n inlineLabel('saveShortcut')\n )}\n </Button>\n {disableMarkdown ? null : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownEnabled}\n className={isMarkdownEnabled ? 'text-primary' : undefined}\n disabled={contentSavingId === note.id}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n )}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setContentEditor({ id: '', value: '' })}\n disabled={contentSavingId === note.id}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer text-sm\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n onKeyDown={(event) => handleContentKeyDown(event, note)}\n >\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n className=\"break-words text-foreground [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n >\n {note.body}\n </MarkdownPreview>\n </div>\n )}\n </div>\n )\n })\n ) : composerOpen ? null : (\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: emptyState.actionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || !hasEntity,\n }}\n />\n )}\n {isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore}>\n {loadMoreLabel}\n </Button>\n </div>\n )}\n </div>\n <AppearanceDialog\n open={appearanceDialogOpen}\n title={\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.edit')\n : label('appearance.toggleOpen', 'Customize appearance')\n }\n icon={appearanceDialogState?.icon ?? null}\n color={appearanceDialogState?.color ?? null}\n labels={noteAppearanceLabels}\n iconSuggestions={iconSuggestions}\n onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}\n onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}\n onSubmit={() => {\n void handleAppearanceDialogSubmit()\n }}\n onClose={handleAppearanceDialogClose}\n isSaving={appearanceDialogSaving}\n errorMessage={appearanceDialogError}\n primaryLabel={appearanceDialogPrimaryLabel}\n savingLabel={appearanceDialogSavingLabel}\n cancelLabel={label('appearance.cancel')}\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function NotesSection<C = unknown>(props: NotesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'NotesSection')\n const Resolved = useRegisteredComponent<NotesSectionProps<C>>(\n handle,\n NotesSectionImpl as React.ComponentType<NotesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { PluggableList } from 'unified'\nimport type { AppearanceSelectorLabels } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { AppearanceDialog } from '@open-mercato/core/modules/customers/components/detail/AppearanceDialog'\nimport type { IconOption } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { hasMoreFromPage } from '@open-mercato/shared/lib/pagination/load-more'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { flash } from '../FlashMessages'\nimport { surfaceRecordConflict } from '../conflicts'\nimport { SwitchableMarkdownInput } from '../inputs/SwitchableMarkdownInput'\nimport { ErrorMessage } from './ErrorMessage'\nimport { LoadingMessage } from './LoadingMessage'\nimport { TabEmptyState } from './TabEmptyState'\nimport { useConfirmDialog } from '../confirm-dialog'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { MarkdownPreview } from '../markdown'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\ntype Translator = (key: string, fallback?: string, params?: Record<string, string | number>) => string\n\nconst NOTES_PAGE_SIZE = 20\n\nconst isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined')\n\nexport type SectionAction = {\n label: React.ReactNode\n onClick: () => void\n disabled?: boolean\n icon?: React.ReactNode\n}\n\nexport type TabEmptyStateConfig = {\n title: string\n actionLabel: string\n description?: string\n}\n\nexport type CommentSummary = {\n id: string\n body: string\n createdAt: string\n updatedAt?: string | null\n authorUserId?: string | null\n authorName?: string | null\n authorEmail?: string | null\n dealId?: string | null\n dealTitle?: string | null\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesCreatePayload = {\n entityId: string\n body: string\n appearanceIcon: string | null\n appearanceColor: string | null\n dealId?: string | null\n}\n\nexport type NotesUpdatePayload = {\n body?: string\n appearanceIcon?: string | null\n appearanceColor?: string | null\n}\n\nexport type NotesDataAdapter<C = unknown> = {\n list: (params: { entityId: string | null; dealId: string | null; context?: C }) => Promise<CommentSummary[]>\n listPage?: (params: {\n entityId: string | null\n dealId: string | null\n page: number\n pageSize: number\n context?: C\n }) => Promise<{\n items: CommentSummary[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n }>\n create: (params: NotesCreatePayload & { context?: C }) => Promise<Partial<CommentSummary> | void>\n update: (params: { id: string; patch: NotesUpdatePayload; updatedAt?: string | null; context?: C }) => Promise<void>\n delete: (params: { id: string; updatedAt?: string | null; context?: C }) => Promise<void>\n}\n\ntype RenderIconFn = (icon: string, className?: string) => React.ReactNode\ntype RenderColorFn = (color: string, className?: string) => React.ReactNode\n\nlet markdownPluginsPromise: Promise<PluggableList> | null = null\n\nasync function loadMarkdownPlugins(): Promise<PluggableList> {\n if (isTestEnv) return []\n if (!markdownPluginsPromise) {\n markdownPluginsPromise = import('remark-gfm')\n .then((mod) => [mod.default ?? mod] as PluggableList)\n .catch(() => [])\n }\n return markdownPluginsPromise\n}\n\nfunction generateTempId() {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n return `tmp_${Math.random().toString(36).slice(2)}`\n}\n\n\n\ntype TimelineItemHeaderProps = {\n title: React.ReactNode\n subtitle?: React.ReactNode\n timestamp?: string | Date | null\n fallbackTimestampLabel?: React.ReactNode\n icon?: string | null\n color?: string | null\n iconSize?: 'sm' | 'md'\n className?: string\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n}\n\nfunction TimelineItemHeader({\n title,\n subtitle,\n timestamp,\n fallbackTimestampLabel,\n icon,\n color,\n iconSize = 'md',\n className,\n renderIcon,\n renderColor,\n}: TimelineItemHeaderProps) {\n const wrapperSize = iconSize === 'sm' ? 'h-6 w-6' : 'h-8 w-8'\n const iconSizeClass = iconSize === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const resolvedTimestamp = React.useMemo(() => {\n if (subtitle) return subtitle\n if (!timestamp) return fallbackTimestampLabel ?? null\n const value = typeof timestamp === 'string' ? timestamp : timestamp.toISOString()\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return fallbackTimestampLabel ?? null\n const now = Date.now()\n const diff = Math.abs(now - date.getTime())\n const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000\n const relativeLabel = diff <= THIRTY_DAYS_MS ? formatRelativeTime(value) : null\n const absoluteLabel = formatDateTime(value)\n if (relativeLabel) {\n return (\n <span title={absoluteLabel ?? undefined}>\n {relativeLabel}\n </span>\n )\n }\n return absoluteLabel ?? fallbackTimestampLabel ?? null\n }, [fallbackTimestampLabel, subtitle, timestamp])\n\n return (\n <div className={['flex items-start gap-3', className].filter(Boolean).join(' ')}>\n {icon && renderIcon ? (\n <span className={['inline-flex items-center justify-center rounded border border-border bg-muted/50', wrapperSize].join(' ')}>\n {renderIcon(icon, iconSizeClass)}\n </span>\n ) : null}\n <div className=\"space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n {color && renderColor ? renderColor(color, 'h-3 w-3 rounded-full border border-border') : null}\n </div>\n {resolvedTimestamp ? <div className=\"text-xs text-muted-foreground\">{resolvedTimestamp}</div> : null}\n </div>\n </div>\n )\n}\n\nexport type NotesSectionProps<C = unknown> = {\n entityId: string | null\n dealId?: string | null\n emptyLabel: string\n viewerUserId: string | null\n viewerName?: string | null\n viewerEmail?: string | null\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n translator?: Translator\n labelPrefix?: string\n inlineLabelPrefix?: string\n onLoadingChange?: (isLoading: boolean) => void\n dealOptions?: Array<{ id: string; label: string }>\n entityOptions?: Array<{ id: string; label: string }>\n dataAdapter: NotesDataAdapter<C>\n dataContext?: C\n renderIcon?: RenderIconFn\n renderColor?: RenderColorFn\n iconSuggestions?: IconOption[]\n readMarkdownPreference?: () => boolean | null\n writeMarkdownPreference?: (value: boolean) => void\n /** Force the plain textarea and hide the markdown toggle. Wins over `forceMarkdown`. */\n disableMarkdown?: boolean\n /**\n * Keep the rich markdown editor active for every note and hide the markdown toggle,\n * so the editor shape cannot be switched. Ignored when `disableMarkdown` is set.\n */\n forceMarkdown?: boolean\n /**\n * Hide the markdown toggle without changing which editor renders \u2014 the seeded\n * `readMarkdownPreference` value stays in effect. Implied by `forceMarkdown`.\n */\n hideMarkdownToggle?: boolean\n /** Hide the appearance (icon/color) affordances: the palette buttons and the appearance dialog. */\n disableAppearance?: boolean\n}\n\nexport function sanitizeHexColor(value: string | null): string | null {\n if (!value) return null\n const trimmed = value.trim()\n return /^#([0-9a-f]{6})$/i.test(trimmed) ? trimmed.toLowerCase() : null\n}\n\nexport function mapCommentSummary(input: unknown): CommentSummary {\n const data = (typeof input === 'object' && input !== null ? input : {}) as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : generateTempId()\n const body = typeof data.body === 'string' ? data.body : ''\n const createdAt =\n typeof data.createdAt === 'string'\n ? data.createdAt\n : typeof data.created_at === 'string'\n ? data.created_at\n : new Date().toISOString()\n const updatedAt =\n typeof data.updatedAt === 'string'\n ? data.updatedAt\n : typeof data.updated_at === 'string'\n ? data.updated_at\n : null\n const authorUserId =\n typeof data.authorUserId === 'string'\n ? data.authorUserId\n : typeof data.author_user_id === 'string'\n ? data.author_user_id\n : null\n const authorName =\n typeof data.authorName === 'string'\n ? data.authorName\n : typeof data.author_name === 'string'\n ? data.author_name\n : null\n const authorEmail =\n typeof data.authorEmail === 'string'\n ? data.authorEmail\n : typeof data.author_email === 'string'\n ? data.author_email\n : null\n const dealId =\n typeof data.dealId === 'string'\n ? data.dealId\n : typeof data.deal_id === 'string'\n ? data.deal_id\n : null\n const dealTitle =\n typeof data.dealTitle === 'string'\n ? data.dealTitle\n : typeof data.deal_title === 'string'\n ? data.deal_title\n : null\n const appearanceIcon =\n typeof data.appearanceIcon === 'string'\n ? data.appearanceIcon\n : typeof data.appearance_icon === 'string'\n ? data.appearance_icon\n : null\n const appearanceColor =\n typeof data.appearanceColor === 'string'\n ? data.appearanceColor\n : typeof data.appearance_color === 'string'\n ? data.appearance_color\n : null\n return {\n id,\n body,\n createdAt,\n updatedAt,\n authorUserId,\n authorName,\n authorEmail,\n dealId,\n dealTitle,\n appearanceIcon,\n appearanceColor,\n }\n}\n\nfunction NotesSectionImpl<C = unknown>({\n entityId,\n dealId,\n emptyLabel,\n viewerUserId,\n viewerName,\n viewerEmail,\n addActionLabel,\n emptyState,\n onActionChange,\n translator,\n labelPrefix = 'customers.people.detail.notes',\n inlineLabelPrefix = 'customers.people.detail.inline',\n onLoadingChange,\n dealOptions,\n entityOptions,\n dataAdapter,\n dataContext,\n renderIcon,\n renderColor,\n iconSuggestions,\n readMarkdownPreference,\n writeMarkdownPreference,\n disableMarkdown,\n forceMarkdown,\n hideMarkdownToggle,\n disableAppearance,\n}: NotesSectionProps<C>) {\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])\n const label = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${labelPrefix}.${suffix}`, fallback, params),\n [labelPrefix, t],\n )\n const inlineLabel = React.useCallback(\n (suffix: string, fallback?: string, params?: Record<string, string | number>) =>\n t(`${inlineLabelPrefix}.${suffix}`, fallback, params),\n [inlineLabelPrefix, t],\n )\n const [markdownPlugins, setMarkdownPlugins] = React.useState<PluggableList>([])\n React.useEffect(() => {\n if (isTestEnv) return\n let mounted = true\n void loadMarkdownPlugins().then((plugins) => {\n if (!mounted) return\n setMarkdownPlugins(plugins)\n })\n return () => {\n mounted = false\n }\n }, [])\n\n const normalizedDealOptions = React.useMemo(() => {\n if (!Array.isArray(dealOptions)) return []\n const seen = new Set<string>()\n return dealOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [dealOptions])\n\n const dealLabelMap = React.useMemo(() => {\n const map = new Map<string, string>()\n normalizedDealOptions.forEach((option) => {\n map.set(option.id, option.label)\n })\n return map\n }, [normalizedDealOptions])\n\n const normalizedEntityOptions = React.useMemo(() => {\n if (!Array.isArray(entityOptions)) return []\n const seen = new Set<string>()\n return entityOptions\n .map((option) => {\n if (!option || typeof option !== 'object') return null\n const id = typeof option.id === 'string' ? option.id.trim() : ''\n if (!id || seen.has(id)) return null\n const label =\n typeof option.label === 'string' && option.label.trim().length\n ? option.label.trim()\n : id\n seen.add(id)\n return { id, label }\n })\n .filter((option): option is { id: string; label: string } => !!option)\n }, [entityOptions])\n\n const [selectedDealId, setSelectedDealId] = React.useState<string>(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n return initial\n })\n React.useEffect(() => {\n const initial = typeof dealId === 'string' ? dealId.trim() : ''\n if (initial !== selectedDealId) {\n setSelectedDealId(initial)\n }\n }, [dealId, selectedDealId])\n\n const [selectedEntityId, setSelectedEntityId] = React.useState<string>(() => {\n if (normalizedEntityOptions.length) return normalizedEntityOptions[0].id\n return typeof entityId === 'string' ? entityId : ''\n })\n React.useEffect(() => {\n if (normalizedEntityOptions.length) {\n if (!normalizedEntityOptions.some((option) => option.id === selectedEntityId)) {\n setSelectedEntityId(normalizedEntityOptions[0].id)\n }\n } else {\n const initial = typeof entityId === 'string' ? entityId : ''\n if (initial !== selectedEntityId) {\n setSelectedEntityId(initial)\n }\n }\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedEntityId = React.useMemo(() => {\n if (normalizedEntityOptions.length) return selectedEntityId\n return typeof entityId === 'string' ? entityId : ''\n }, [entityId, normalizedEntityOptions, selectedEntityId])\n\n const resolvedDealId = React.useMemo(() => {\n const trimmed = typeof selectedDealId === 'string' ? selectedDealId.trim() : ''\n return trimmed\n }, [selectedDealId])\n\n const hasEntity = resolvedEntityId.length > 0\n\n const [notes, setNotes] = React.useState<CommentSummary[]>([])\n const [isLoading, setIsLoading] = React.useState<boolean>(() => Boolean(entityId || dealId))\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const pendingCounterRef = React.useRef(0)\n\n const pushLoading = React.useCallback(() => {\n pendingCounterRef.current += 1\n if (pendingCounterRef.current === 1) {\n onLoadingChange?.(true)\n }\n }, [onLoadingChange])\n\n const popLoading = React.useCallback(() => {\n pendingCounterRef.current = Math.max(0, pendingCounterRef.current - 1)\n if (pendingCounterRef.current === 0) {\n onLoadingChange?.(false)\n }\n }, [onLoadingChange])\n\n const [composerOpen, setComposerOpen] = React.useState(false)\n const [draftBody, setDraftBody] = React.useState('')\n const [draftIcon, setDraftIcon] = React.useState<string | null>(null)\n const [draftColor, setDraftColor] = React.useState<string | null>(null)\n const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)\n const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled)\n const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle\n const showAppearanceControls = !disableAppearance\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const formRef = React.useRef<HTMLFormElement | null>(null)\n const focusComposer = React.useCallback(() => {\n if (!hasEntity) return\n setComposerOpen(true)\n window.requestAnimationFrame(() => {\n if (isMarkdownActive) {\n const markdownTextarea = formRef.current?.querySelector('textarea')\n if (markdownTextarea instanceof HTMLTextAreaElement) {\n markdownTextarea.focus()\n markdownTextarea.scrollIntoView({ behavior: 'smooth', block: 'center' })\n return\n }\n }\n const element = textareaRef.current\n if (!element) return\n element.focus()\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n }, [formRef, hasEntity, isMarkdownActive])\n const [appearanceDialogState, setAppearanceDialogState] = React.useState<\n | { mode: 'create'; icon: string | null; color: string | null }\n | { mode: 'edit'; noteId: string; icon: string | null; color: string | null }\n | null\n >(null)\n const [appearanceDialogSaving, setAppearanceDialogSaving] = React.useState(false)\n const [appearanceDialogError, setAppearanceDialogError] = React.useState<string | null>(null)\n const [contentEditor, setContentEditor] = React.useState<{ id: string; value: string }>({ id: '', value: '' })\n const [contentSavingId, setContentSavingId] = React.useState<string | null>(null)\n const [contentError, setContentError] = React.useState<string | null>(null)\n const contentTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [visibleCount, setVisibleCount] = React.useState(0)\n const [currentPage, setCurrentPage] = React.useState(1)\n // Short-page termination instead of `currentPage >= totalPages` \u2014 see\n // `hasMoreFromPage`. `/api/customers/comments` is a `makeCrudRoute` list, so\n // a page past the end comes back empty rather than clamped.\n const [hasMore, setHasMore] = React.useState(false)\n const [deletingNoteId, setDeletingNoteId] = React.useState<string | null>(null)\n const pagedMode = typeof dataAdapter.listPage === 'function'\n\n React.useEffect(() => {\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n if (!queryEntityId && !queryDealId) {\n setNotes([])\n setLoadError(null)\n setIsLoading(false)\n setCurrentPage(1)\n setHasMore(false)\n return\n }\n let cancelled = false\n setIsLoading(true)\n setLoadError(null)\n pushLoading()\n async function loadNotes() {\n try {\n if (dataAdapter.listPage) {\n const pageResult = await dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(pageResult.items)\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n return\n }\n const mapped = await dataAdapter.list({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n context: dataContext,\n })\n if (cancelled) return\n setNotes(mapped)\n setCurrentPage(1)\n setHasMore(false)\n } catch (err) {\n if (cancelled) return\n const message =\n err instanceof Error ? err.message : label('loadError', 'Failed to load notes.')\n setNotes([])\n setLoadError(message)\n setCurrentPage(1)\n setHasMore(false)\n flash(message, 'error')\n } finally {\n if (!cancelled) setIsLoading(false)\n popLoading()\n }\n }\n loadNotes().catch(() => {})\n return () => {\n cancelled = true\n }\n }, [dataAdapter, dataContext, dealId, entityId, label, popLoading, pushLoading])\n\n const youLabel = label('you', 'You')\n const viewerLabel = React.useMemo(() => viewerName ?? viewerEmail ?? null, [viewerEmail, viewerName])\n\n const handleMarkdownToggle = React.useCallback(() => {\n setIsMarkdownEnabled((prev) => {\n const next = !prev\n if (writeMarkdownPreference) {\n writeMarkdownPreference(next)\n }\n return next\n })\n }, [writeMarkdownPreference])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!notes.length) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || isLoading || !hasEntity,\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n })\n return () => onActionChange(null)\n }, [onActionChange, addActionLabel, focusComposer, hasEntity, isLoading, isSubmitting, notes.length])\n\n const adjustTextareaSize = React.useCallback((element: HTMLTextAreaElement | null) => {\n if (!element) return\n element.style.height = 'auto'\n element.style.height = `${element.scrollHeight}px`\n }, [])\n\n React.useEffect(() => {\n adjustTextareaSize(textareaRef.current)\n }, [adjustTextareaSize, draftBody, isMarkdownActive, composerOpen])\n\n React.useEffect(() => {\n const preference = readMarkdownPreference ? readMarkdownPreference() : null\n if (preference !== null) {\n setIsMarkdownEnabled(preference)\n }\n }, [readMarkdownPreference])\n\n React.useEffect(() => {\n if (pagedMode) {\n setVisibleCount(notes.length)\n return\n }\n if (!notes.length) {\n setVisibleCount(0)\n return\n }\n const baseline = Math.min(5, notes.length)\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(Math.max(prev, baseline), notes.length)\n })\n }, [notes.length, pagedMode])\n\n React.useEffect(() => {\n if (hasEntity) return\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }, [hasEntity])\n\n const visibleNotes = React.useMemo(\n () => (pagedMode ? notes : notes.slice(0, visibleCount)),\n [notes, pagedMode, visibleCount],\n )\n const hasVisibleNotes = React.useMemo(\n () => (pagedMode ? notes.length > 0 : visibleCount > 0),\n [notes.length, pagedMode, visibleCount],\n )\n\n const loadMoreLabel = label('loadMore')\n\n const handleCreateNote = React.useCallback(\n async (input: { body: string; appearanceIcon: string | null; appearanceColor: string | null }) => {\n if (!hasEntity || !resolvedEntityId) {\n flash(label('entityMissing', 'Unable to determine current person.'), 'error')\n return false\n }\n const body = input.body.trim()\n const strippedBody = body\n .replace(/^[\\s#\\-*>_~`|+\\\\\\n\\r]+$/gm, '')\n .replace(/\\s+/g, '')\n if (!body || !strippedBody.length) {\n focusComposer()\n return false\n }\n const icon = input.appearanceIcon && input.appearanceIcon.trim().length ? input.appearanceIcon.trim() : null\n const color = sanitizeHexColor(input.appearanceColor)\n const targetDealId = resolvedDealId.length ? resolvedDealId : null\n const dealLabel = targetDealId ? dealLabelMap.get(targetDealId) ?? null : null\n setIsSubmitting(true)\n pushLoading()\n try {\n const responseBody =\n (await dataAdapter.create({\n entityId: resolvedEntityId,\n body,\n appearanceIcon: icon,\n appearanceColor: color,\n dealId: targetDealId,\n context: dataContext,\n })) ?? {}\n setNotes((prev) => {\n const viewerId = viewerUserId ?? null\n const resolvedAuthorId =\n typeof responseBody?.authorUserId === 'string' ? responseBody.authorUserId : viewerId ?? null\n const resolvedAuthorName = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return youLabel\n }\n return typeof responseBody?.authorName === 'string' ? responseBody.authorName : viewerLabel\n })()\n const resolvedAuthorEmail = (() => {\n if (resolvedAuthorId && viewerId && resolvedAuthorId === viewerId) {\n return viewerEmail ?? null\n }\n return typeof responseBody?.authorEmail === 'string' ? responseBody.authorEmail : null\n })()\n const newNote: CommentSummary = {\n id: typeof responseBody?.id === 'string' ? responseBody.id : generateTempId(),\n body,\n createdAt: new Date().toISOString(),\n authorUserId: resolvedAuthorId,\n authorName: resolvedAuthorName,\n authorEmail: resolvedAuthorEmail,\n dealId: targetDealId,\n dealTitle: dealLabel,\n appearanceIcon: icon,\n appearanceColor: color,\n }\n return [newNote, ...prev]\n })\n setVisibleCount((prev) => Math.max(prev, 1))\n flash(label('success'), 'success')\n return true\n } catch (err) {\n const message = err instanceof Error ? err.message : label('error')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n return false\n } finally {\n setIsSubmitting(false)\n popLoading()\n }\n },\n [dataAdapter, dataContext, dealLabelMap, focusComposer, hasEntity, popLoading, pushLoading, resolvedDealId, resolvedEntityId, t, viewerEmail, viewerLabel, viewerUserId, youLabel],\n )\n\n const handleUpdateNote = React.useCallback(\n async (noteId: string, patch: { body?: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const sanitizedBody = patch.body\n const sanitizedIcon =\n patch.appearanceIcon !== undefined && patch.appearanceIcon !== null && patch.appearanceIcon.trim().length\n ? patch.appearanceIcon.trim()\n : patch.appearanceIcon === null\n ? null\n : undefined\n const sanitizedColor =\n patch.appearanceColor !== undefined ? sanitizeHexColor(patch.appearanceColor ?? null) : undefined\n const noteUpdatedAt = notes.find((comment) => comment.id === noteId)?.updatedAt ?? null\n try {\n await dataAdapter.update({\n id: noteId,\n patch: {\n body: sanitizedBody,\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n },\n updatedAt: noteUpdatedAt,\n context: dataContext,\n })\n setNotes((prev) => {\n const nextComments = prev.map((comment) => {\n if (comment.id !== noteId) return comment\n const next = { ...comment }\n if (sanitizedBody !== undefined) next.body = sanitizedBody\n if (sanitizedIcon !== undefined) next.appearanceIcon = sanitizedIcon ?? null\n if (sanitizedColor !== undefined) next.appearanceColor = sanitizedColor ?? null\n return next\n })\n return nextComments\n })\n flash(label('updateSuccess'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : label('updateError')\n if (!surfaceRecordConflict(error, t)) flash(message, 'error')\n throw error instanceof Error ? error : new Error(message)\n }\n },\n [dataAdapter, dataContext, notes, t],\n )\n\n const handleDeleteNote = React.useCallback(\n async (note: CommentSummary) => {\n const confirmed = await confirm({\n title: label('deleteConfirm', 'Delete this note? You can restore it using version history.'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setDeletingNoteId(note.id)\n pushLoading()\n try {\n await dataAdapter.delete({ id: note.id, updatedAt: note.updatedAt ?? null, context: dataContext })\n setNotes((prev) => prev.filter((existing) => existing.id !== note.id))\n if (pagedMode) {\n setVisibleCount((prev) => Math.max(0, prev - 1))\n }\n flash(label('deleteSuccess', 'Note deleted'), 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : label('deleteError', 'Failed to delete note')\n if (!surfaceRecordConflict(err, t)) flash(message, 'error')\n } finally {\n setDeletingNoteId(null)\n popLoading()\n }\n },\n [confirm, dataAdapter, dataContext, label, popLoading, pushLoading, t],\n )\n\n const handleSubmit = React.useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const created = await handleCreateNote({\n body: draftBody,\n appearanceIcon: draftIcon,\n appearanceColor: draftColor,\n })\n if (created) {\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }\n },\n [draftBody, draftColor, draftIcon, handleCreateNote],\n )\n\n const handleLoadMore = React.useCallback(() => {\n if (pagedMode && dataAdapter.listPage) {\n if (!hasMore || isLoading) return\n const queryEntityId = typeof entityId === 'string' ? entityId : ''\n const queryDealId = typeof dealId === 'string' ? dealId : ''\n setIsLoading(true)\n pushLoading()\n void dataAdapter.listPage({\n entityId: queryEntityId || null,\n dealId: queryDealId || null,\n page: currentPage + 1,\n pageSize: NOTES_PAGE_SIZE,\n context: dataContext,\n })\n .then((pageResult) => {\n setNotes((prev) => {\n const merged = new Map(prev.map((note) => [note.id, note]))\n pageResult.items.forEach((note) => merged.set(note.id, note))\n return Array.from(merged.values())\n })\n setCurrentPage(pageResult.page)\n setHasMore(hasMoreFromPage(pageResult.items.length, NOTES_PAGE_SIZE))\n })\n .catch((error) => {\n const message =\n error instanceof Error ? error.message : label('loadError', 'Failed to load notes.')\n flash(message, 'error')\n })\n .finally(() => {\n setIsLoading(false)\n popLoading()\n })\n return\n }\n setVisibleCount((prev) => {\n if (prev >= notes.length) return prev\n return Math.min(prev + 5, notes.length)\n })\n }, [currentPage, dataAdapter, dataContext, dealId, entityId, flash, hasMore, isLoading, label, notes.length, pagedMode, popLoading, pushLoading])\n\n const handleAppearanceDialogSubmit = React.useCallback(async () => {\n if (!appearanceDialogState) return\n setAppearanceDialogError(null)\n const sanitizedIcon =\n appearanceDialogState.icon && appearanceDialogState.icon.trim().length\n ? appearanceDialogState.icon.trim()\n : null\n const sanitizedColor = sanitizeHexColor(appearanceDialogState.color ?? null)\n if (appearanceDialogState.mode === 'create') {\n setDraftIcon(sanitizedIcon)\n setDraftColor(sanitizedColor)\n setAppearanceDialogState(null)\n return\n }\n setAppearanceDialogSaving(true)\n try {\n await handleUpdateNote(appearanceDialogState.noteId, {\n appearanceIcon: sanitizedIcon,\n appearanceColor: sanitizedColor,\n })\n setAppearanceDialogState(null)\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : label('appearance.error', 'Failed to update appearance.')\n setAppearanceDialogError(message)\n } finally {\n setAppearanceDialogSaving(false)\n }\n }, [appearanceDialogState, handleUpdateNote, t])\n\n const handleAppearanceDialogClose = React.useCallback(() => {\n if (appearanceDialogSaving) return\n setAppearanceDialogState(null)\n setAppearanceDialogError(null)\n }, [appearanceDialogSaving])\n\n const handleContentSave = React.useCallback(async () => {\n if (!contentEditor.id) return\n const trimmed = contentEditor.value.trim()\n if (!trimmed) {\n setContentError(label('updateError', 'Failed to update note'))\n return\n }\n setContentSavingId(contentEditor.id)\n setContentError(null)\n try {\n await handleUpdateNote(contentEditor.id, { body: trimmed })\n setContentEditor({ id: '', value: '' })\n } catch (err) {\n const message =\n err instanceof Error ? err.message : label('updateError', 'Failed to update note')\n setContentError(message)\n } finally {\n setContentSavingId(null)\n }\n }, [contentEditor, handleUpdateNote, t])\n\n const handleContentEditorKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!contentEditor.id) return\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!contentSavingId) void handleContentSave()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setContentEditor({ id: '', value: '' })\n setContentError(null)\n }\n },\n [contentEditor.id, contentSavingId, handleContentSave],\n )\n\n const handleComposerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n formRef.current?.requestSubmit()\n }\n },\n [],\n )\n\n const handleContentKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>, note: CommentSummary) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setContentEditor({ id: note.id, value: note.body })\n }\n },\n [],\n )\n\n const noteAuthorLabel = React.useCallback(\n (note: CommentSummary) => {\n if (note.authorUserId && viewerUserId && note.authorUserId === viewerUserId) {\n return youLabel\n }\n return note.authorName ?? note.authorEmail ?? youLabel\n },\n [viewerUserId, youLabel],\n )\n\n const noteAppearanceLabels = React.useMemo<AppearanceSelectorLabels>(\n () => ({\n colorLabel: label('appearance.colorLabel'),\n colorHelp: label('appearance.colorHelp'),\n colorClearLabel: label('appearance.clearColor'),\n iconLabel: label('appearance.iconLabel'),\n iconPlaceholder: label('appearance.iconPlaceholder'),\n iconPickerTriggerLabel: label('appearance.iconPicker'),\n iconSearchPlaceholder: label('appearance.iconSearchPlaceholder'),\n iconSearchEmptyLabel: label('appearance.iconSearchEmpty'),\n iconSuggestionsLabel: label('appearance.iconSuggestions'),\n iconClearLabel: label('appearance.iconClear'),\n previewEmptyLabel: label('appearance.previewEmpty'),\n }),\n [label],\n )\n\n const composerAuthor = React.useMemo(\n () => youLabel,\n [youLabel],\n )\n const composerHasAppearance = Boolean(draftIcon) || Boolean(draftColor)\n const appearanceDialogOpen = appearanceDialogState !== null\n const editingAppearanceNoteId =\n appearanceDialogState?.mode === 'edit' ? appearanceDialogState.noteId : null\n const addNoteShortcutLabel = label('addShortcut', 'Add note \u2318\u23CE / Ctrl+Enter')\n const saveAppearanceShortcutLabel = label('appearance.saveShortcut', 'Save appearance \u2318\u23CE / Ctrl+Enter')\n const composerSubmitLabel = addNoteShortcutLabel\n const appearanceDialogPrimaryLabel = saveAppearanceShortcutLabel\n const appearanceDialogSavingLabel =\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.saving')\n : label('saving', 'Saving note\u2026')\n\n return (\n <div className=\"mt-0 space-y-2\">\n <div\n className={[\n 'overflow-hidden rounded-xl transition-all duration-300 ease-out',\n composerOpen ? 'max-h-[1200px] bg-muted/30 p-4 opacity-100' : 'pointer-events-none max-h-0 p-0 opacity-0',\n ].join(' ')}\n aria-hidden={!composerOpen}\n >\n {composerOpen ? (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n onKeyDown={handleComposerKeyDown}\n className=\"space-y-3\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-sm font-medium\">{label('addLabel')}</h3>\n <div className=\"flex flex-wrap items-center gap-1\">\n {showAppearanceControls ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => {\n setAppearanceDialogError(null)\n setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })\n }}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <span className=\"sr-only\">{label('appearance.toggleOpen', 'Customize appearance')}</span>\n <Palette className=\"h-4 w-4\" />\n </Button>\n ) : null}\n {showMarkdownToggle ? (\n <Button\n type=\"button\"\n variant={isMarkdownActive ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownActive}\n disabled={isSubmitting || isLoading}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setComposerOpen(false)\n setDraftBody('')\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting || isLoading}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n {(normalizedEntityOptions.length || normalizedDealOptions.length) ? (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {normalizedEntityOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-entity-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.entity', 'Assign to customer')}\n </label>\n <Select\n value={selectedEntityId || undefined}\n onValueChange={(next) => setSelectedEntityId(next ?? '')}\n disabled={isSubmitting || isLoading || !normalizedEntityOptions.length}\n >\n <SelectTrigger id=\"note-entity-select\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {normalizedEntityOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n {normalizedDealOptions.length ? (\n <div className=\"flex flex-col gap-1\">\n <label\n htmlFor=\"note-deal-select\"\n className=\"text-xs font-medium text-muted-foreground\"\n >\n {label('fields.deal', 'Link to deal (optional)')}\n </label>\n <Select\n value={selectedDealId || undefined}\n onValueChange={(next) => setSelectedDealId(next ?? '')}\n disabled={isSubmitting || isLoading}\n >\n <SelectTrigger id=\"note-deal-select\">\n <SelectValue placeholder={label('fields.dealPlaceholder', 'No linked deal')} />\n </SelectTrigger>\n <SelectContent>\n {normalizedDealOptions.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n ) : null}\n <SwitchableMarkdownInput\n value={draftBody}\n onChange={setDraftBody}\n isMarkdownEnabled={isMarkdownActive}\n disableMarkdown={disableMarkdown}\n rows={1}\n placeholder={label('placeholder')}\n textareaRef={textareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n disabled={isSubmitting || isLoading || !hasEntity}\n remarkPlugins={markdownPlugins}\n />\n {composerHasAppearance ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-dashed border-muted-foreground/40 px-3 py-2\">\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n {draftIcon && renderIcon ? (\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-muted/50\">\n {renderIcon(draftIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <span className=\"font-semibold text-foreground\">{composerAuthor}</span>\n {draftColor && renderColor ? (\n <span className=\"flex items-center gap-2\">\n {renderColor(draftColor, 'h-3.5 w-3.5 rounded-full border border-border')}\n <span className=\"text-xs font-medium uppercase text-muted-foreground\">{draftColor}</span>\n </span>\n ) : null}\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDraftIcon(null)\n setDraftColor(null)\n }}\n disabled={isSubmitting}\n >\n {label('appearance.clearAll', 'Clear')}\n </Button>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n {isSubmitting ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {composerSubmitLabel}\n </Button>\n </div>\n </form>\n ) : null}\n </div>\n\n {loadError ? <ErrorMessage label={loadError} className=\"mt-3\" /> : null}\n\n <div className=\"space-y-3\">\n {!composerOpen && hasVisibleNotes && !onActionChange ? (\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={focusComposer}\n disabled={isSubmitting || isLoading || !hasEntity}\n >\n <Plus className=\"size-4\" />\n {addActionLabel}\n </Button>\n </div>\n ) : null}\n {isLoading ? (\n <LoadingMessage\n label={label('loading', 'Loading notes\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : hasVisibleNotes ? (\n visibleNotes.map((note) => {\n const author = noteAuthorLabel(note)\n const isAppearanceSaving = appearanceDialogSaving && editingAppearanceNoteId === note.id\n const isEditingContent = contentEditor.id === note.id\n const displayIcon = note.appearanceIcon ?? null\n const displayColor = note.appearanceColor ?? null\n const timestampValue = note.createdAt\n const fallbackTimestampLabel = formatDateTime(note.createdAt) ?? emptyLabel\n return (\n <div key={note.id} className=\"group space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <TimelineItemHeader\n title={author}\n timestamp={timestampValue}\n fallbackTimestampLabel={fallbackTimestampLabel}\n icon={displayIcon}\n color={displayColor}\n renderIcon={renderIcon}\n renderColor={renderColor}\n />\n {note.dealId ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <ArrowUpRightSquare className=\"h-3.5 w-3.5\" />\n <a\n href={`/backend/customers/deals/${encodeURIComponent(note.dealId)}`}\n className=\"font-medium text-foreground hover:underline\"\n >\n {note.dealTitle && note.dealTitle.length\n ? note.dealTitle\n : label('linkedDeal', 'Linked deal')}\n </a>\n </div>\n ) : null}\n </div>\n <div\n className={`flex items-center gap-2 transition-opacity ${\n isEditingContent ? 'opacity-100' : 'opacity-100 md:opacity-0 md:group-hover:opacity-100 focus-within:opacity-100'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n {showAppearanceControls ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n setAppearanceDialogError(null)\n setAppearanceDialogState({\n mode: 'edit',\n noteId: note.id,\n icon: note.appearanceIcon ?? null,\n color: note.appearanceColor ?? null,\n })\n }}\n disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}\n >\n {isAppearanceSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Palette className=\"h-4 w-4\" />}\n </Button>\n ) : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={(event) => {\n event.stopPropagation()\n void handleDeleteNote(note)\n }}\n disabled={deletingNoteId === note.id}\n >\n {deletingNoteId === note.id ? (\n <span className=\"relative flex h-4 w-4 items-center justify-center text-destructive\">\n <span className=\"absolute h-4 w-4 animate-spin rounded-full border border-destructive border-t-transparent\" />\n </span>\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n </div>\n {isEditingContent ? (\n <div className=\"space-y-2\" onKeyDown={handleContentEditorKeyDown}>\n <SwitchableMarkdownInput\n value={contentEditor.value}\n onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}\n isMarkdownEnabled={isMarkdownActive}\n disableMarkdown={disableMarkdown}\n rows={3}\n textareaRef={contentTextareaRef}\n onTextareaInput={(event) => adjustTextareaSize(event.currentTarget)}\n textareaClassName=\"w-full resize-none overflow-hidden rounded-md border border-border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n editorWrapperClassName=\"w-full rounded-md border border-muted-foreground/20 bg-background p-2\"\n remarkPlugins={markdownPlugins}\n />\n {contentError ? <p className=\"text-xs text-status-error-text\">{contentError}</p> : null}\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleContentSave} disabled={contentSavingId === note.id}>\n {contentSavingId === note.id ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {label('saving')}\n </>\n ) : (\n inlineLabel('saveShortcut')\n )}\n </Button>\n {showMarkdownToggle ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleMarkdownToggle}\n aria-pressed={isMarkdownActive}\n className={isMarkdownActive ? 'text-primary' : undefined}\n disabled={contentSavingId === note.id}\n >\n <FileCode className=\"h-4 w-4\" />\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setContentEditor({ id: '', value: '' })}\n disabled={contentSavingId === note.id}\n >\n {inlineLabel('cancel')}\n </Button>\n </div>\n </div>\n ) : (\n <div\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer text-sm\"\n onClick={() => setContentEditor({ id: note.id, value: note.body })}\n onKeyDown={(event) => handleContentKeyDown(event, note)}\n >\n <MarkdownPreview\n remarkPlugins={markdownPlugins}\n className=\"break-words text-foreground [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n >\n {note.body}\n </MarkdownPreview>\n </div>\n )}\n </div>\n )\n })\n ) : composerOpen ? null : (\n <TabEmptyState\n title={emptyState.title}\n description={emptyState.description}\n action={{\n label: emptyState.actionLabel,\n onClick: focusComposer,\n disabled: isSubmitting || !hasEntity,\n }}\n />\n )}\n {isLoading || (pagedMode ? !hasMore : visibleCount >= notes.length) ? null : (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" size=\"sm\" onClick={handleLoadMore}>\n {loadMoreLabel}\n </Button>\n </div>\n )}\n </div>\n {showAppearanceControls ? (\n <AppearanceDialog\n open={appearanceDialogOpen}\n title={\n appearanceDialogState?.mode === 'edit'\n ? label('appearance.edit')\n : label('appearance.toggleOpen', 'Customize appearance')\n }\n icon={appearanceDialogState?.icon ?? null}\n color={appearanceDialogState?.color ?? null}\n labels={noteAppearanceLabels}\n iconSuggestions={iconSuggestions}\n onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}\n onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}\n onSubmit={() => {\n void handleAppearanceDialogSubmit()\n }}\n onClose={handleAppearanceDialogClose}\n isSaving={appearanceDialogSaving}\n errorMessage={appearanceDialogError}\n primaryLabel={appearanceDialogPrimaryLabel}\n savingLabel={appearanceDialogSavingLabel}\n cancelLabel={label('appearance.cancel')}\n />\n ) : null}\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport function NotesSection<C = unknown>(props: NotesSectionProps<C>) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'NotesSection')\n const Resolved = useRegisteredComponent<NotesSectionProps<C>>(\n handle,\n NotesSectionImpl as React.ComponentType<NotesSectionProps<C>>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n"],
5
+ "mappings": ";AAgKQ,SA+mCkB,UA/mClB,KAgBA,YAhBA;AA9JR,YAAY,WAAW;AAGvB,SAAS,wBAAwB;AAEjC,SAAS,oBAAoB,UAAU,SAAS,SAAS,QAAQ,MAAM,cAAc;AACrF,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AACtB,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,mCAAmC;AAC5C,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AAGvC,MAAM,kBAAkB;AAExB,MAAM,YACJ,OAAO,YAAY,gBAClB,QAAQ,IAAI,aAAa,UAAU,OAAO,QAAQ,IAAI,mBAAmB;AAkE5E,IAAI,yBAAwD;AAE5D,eAAe,sBAA8C;AAC3D,MAAI,UAAW,QAAO,CAAC;AACvB,MAAI,CAAC,wBAAwB;AAC3B,6BAAyB,OAAO,YAAY,EACzC,KAAK,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAkB,EACnD,MAAM,MAAM,CAAC,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB;AACxB,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,WAAY,QAAO,OAAO,WAAW;AACvG,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACnD;AAiBA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,cAAc,aAAa,OAAO,YAAY;AACpD,QAAM,gBAAgB,aAAa,OAAO,gBAAgB;AAC1D,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAC5C,QAAI,SAAU,QAAO;AACrB,QAAI,CAAC,UAAW,QAAO,0BAA0B;AACjD,UAAM,QAAQ,OAAO,cAAc,WAAW,YAAY,UAAU,YAAY;AAChF,UAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,QAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO,0BAA0B;AACnE,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAO,KAAK,IAAI,MAAM,KAAK,QAAQ,CAAC;AAC1C,UAAM,iBAAiB,KAAK,KAAK,KAAK,KAAK;AAC3C,UAAM,gBAAgB,QAAQ,iBAAiB,mBAAmB,KAAK,IAAI;AAC3E,UAAM,gBAAgB,eAAe,KAAK;AAC1C,QAAI,eAAe;AACjB,aACE,oBAAC,UAAK,OAAO,iBAAiB,QAC3B,yBACH;AAAA,IAEJ;AACA,WAAO,iBAAiB,0BAA0B;AAAA,EACpD,GAAG,CAAC,wBAAwB,UAAU,SAAS,CAAC;AAEhD,SACE,qBAAC,SAAI,WAAW,CAAC,0BAA0B,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAC3E;AAAA,YAAQ,aACP,oBAAC,UAAK,WAAW,CAAC,oFAAoF,WAAW,EAAE,KAAK,GAAG,GACxH,qBAAW,MAAM,aAAa,GACjC,IACE;AAAA,IACJ,qBAAC,SAAI,WAAU,aACb;AAAA,2BAAC,SAAI,WAAU,qCACb;AAAA,4BAAC,UAAK,WAAU,yCAAyC,iBAAM;AAAA,QAC9D,SAAS,cAAc,YAAY,OAAO,2CAA2C,IAAI;AAAA,SAC5F;AAAA,MACC,oBAAoB,oBAAC,SAAI,WAAU,iCAAiC,6BAAkB,IAAS;AAAA,OAClG;AAAA,KACF;AAEJ;AAyCO,SAAS,iBAAiB,OAAqC;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,oBAAoB,KAAK,OAAO,IAAI,QAAQ,YAAY,IAAI;AACrE;AAEO,SAAS,kBAAkB,OAAgC;AAChE,QAAM,OAAQ,OAAO,UAAU,YAAY,UAAU,OAAO,QAAQ,CAAC;AACrE,QAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,eAAe;AAClE,QAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACzD,QAAM,YACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,eAAe,WACzB,KAAK,cACL,oBAAI,KAAK,GAAE,YAAY;AAC/B,QAAM,YACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,eAAe,WACzB,KAAK,aACL;AACR,QAAM,eACJ,OAAO,KAAK,iBAAiB,WACzB,KAAK,eACL,OAAO,KAAK,mBAAmB,WAC7B,KAAK,iBACL;AACR,QAAM,aACJ,OAAO,KAAK,eAAe,WACvB,KAAK,aACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL;AACR,QAAM,cACJ,OAAO,KAAK,gBAAgB,WACxB,KAAK,cACL,OAAO,KAAK,iBAAiB,WAC3B,KAAK,eACL;AACR,QAAM,SACJ,OAAO,KAAK,WAAW,WACnB,KAAK,SACL,OAAO,KAAK,YAAY,WACtB,KAAK,UACL;AACR,QAAM,YACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,eAAe,WACzB,KAAK,aACL;AACR,QAAM,iBACJ,OAAO,KAAK,mBAAmB,WAC3B,KAAK,iBACL,OAAO,KAAK,oBAAoB,WAC9B,KAAK,kBACL;AACR,QAAM,kBACJ,OAAO,KAAK,oBAAoB,WAC5B,KAAK,kBACL,OAAO,KAAK,qBAAqB,WAC/B,KAAK,mBACL;AACR,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,iBAA8B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB;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;AACF,GAAyB;AACvB,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAC3D,QAAM,IAAI,MAAM,QAAoB,MAAM,eAAe,CAAC,KAAK,aAAa,YAAY,MAAM,CAAC,UAAU,CAAC;AAC1G,QAAM,QAAQ,MAAM;AAAA,IAClB,CAAC,QAAgB,UAAmB,WAClC,EAAE,GAAG,WAAW,IAAI,MAAM,IAAI,UAAU,MAAM;AAAA,IAChD,CAAC,aAAa,CAAC;AAAA,EACjB;AACA,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,QAAgB,UAAmB,WAClC,EAAE,GAAG,iBAAiB,IAAI,MAAM,IAAI,UAAU,MAAM;AAAA,IACtD,CAAC,mBAAmB,CAAC;AAAA,EACvB;AACA,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAwB,CAAC,CAAC;AAC9E,QAAM,UAAU,MAAM;AACpB,QAAI,UAAW;AACf,QAAI,UAAU;AACd,SAAK,oBAAoB,EAAE,KAAK,CAAC,YAAY;AAC3C,UAAI,CAAC,QAAS;AACd,yBAAmB,OAAO;AAAA,IAC5B,CAAC;AACD,WAAO,MAAM;AACX,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,wBAAwB,MAAM,QAAQ,MAAM;AAChD,QAAI,CAAC,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC;AACzC,UAAM,OAAO,oBAAI,IAAY;AAC7B,WAAO,YACJ,IAAI,CAAC,WAAW;AACf,UAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,YAAM,KAAK,OAAO,OAAO,OAAO,WAAW,OAAO,GAAG,KAAK,IAAI;AAC9D,UAAI,CAAC,MAAM,KAAK,IAAI,EAAE,EAAG,QAAO;AAChC,YAAMA,SACJ,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,EAAE,SACpD,OAAO,MAAM,KAAK,IAClB;AACN,WAAK,IAAI,EAAE;AACX,aAAO,EAAE,IAAI,OAAAA,OAAM;AAAA,IACrB,CAAC,EACA,OAAO,CAAC,WAAoD,CAAC,CAAC,MAAM;AAAA,EACzE,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,UAAM,MAAM,oBAAI,IAAoB;AACpC,0BAAsB,QAAQ,CAAC,WAAW;AACxC,UAAI,IAAI,OAAO,IAAI,OAAO,KAAK;AAAA,IACjC,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAM,0BAA0B,MAAM,QAAQ,MAAM;AAClD,QAAI,CAAC,MAAM,QAAQ,aAAa,EAAG,QAAO,CAAC;AAC3C,UAAM,OAAO,oBAAI,IAAY;AAC7B,WAAO,cACJ,IAAI,CAAC,WAAW;AACf,UAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,YAAM,KAAK,OAAO,OAAO,OAAO,WAAW,OAAO,GAAG,KAAK,IAAI;AAC9D,UAAI,CAAC,MAAM,KAAK,IAAI,EAAE,EAAG,QAAO;AAChC,YAAMA,SACJ,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,EAAE,SACpD,OAAO,MAAM,KAAK,IAClB;AACN,WAAK,IAAI,EAAE;AACX,aAAO,EAAE,IAAI,OAAAA,OAAM;AAAA,IACrB,CAAC,EACA,OAAO,CAAC,WAAoD,CAAC,CAAC,MAAM;AAAA,EACzE,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAiB,MAAM;AACvE,UAAM,UAAU,OAAO,WAAW,WAAW,OAAO,KAAK,IAAI;AAC7D,WAAO;AAAA,EACT,CAAC;AACD,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,OAAO,WAAW,WAAW,OAAO,KAAK,IAAI;AAC7D,QAAI,YAAY,gBAAgB;AAC9B,wBAAkB,OAAO;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,CAAC;AAE3B,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAiB,MAAM;AAC3E,QAAI,wBAAwB,OAAQ,QAAO,wBAAwB,CAAC,EAAE;AACtE,WAAO,OAAO,aAAa,WAAW,WAAW;AAAA,EACnD,CAAC;AACD,QAAM,UAAU,MAAM;AACpB,QAAI,wBAAwB,QAAQ;AAClC,UAAI,CAAC,wBAAwB,KAAK,CAAC,WAAW,OAAO,OAAO,gBAAgB,GAAG;AAC7E,4BAAoB,wBAAwB,CAAC,EAAE,EAAE;AAAA,MACnD;AAAA,IACF,OAAO;AACL,YAAM,UAAU,OAAO,aAAa,WAAW,WAAW;AAC1D,UAAI,YAAY,kBAAkB;AAChC,4BAAoB,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,yBAAyB,gBAAgB,CAAC;AAExD,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,QAAI,wBAAwB,OAAQ,QAAO;AAC3C,WAAO,OAAO,aAAa,WAAW,WAAW;AAAA,EACnD,GAAG,CAAC,UAAU,yBAAyB,gBAAgB,CAAC;AAExD,QAAM,iBAAiB,MAAM,QAAQ,MAAM;AACzC,UAAM,UAAU,OAAO,mBAAmB,WAAW,eAAe,KAAK,IAAI;AAC7E,WAAO;AAAA,EACT,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,YAAY,iBAAiB,SAAS;AAE5C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAA2B,CAAC,CAAC;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAkB,MAAM,QAAQ,YAAY,MAAM,CAAC;AAC3F,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAC5D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AACpE,QAAM,oBAAoB,MAAM,OAAO,CAAC;AAExC,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,sBAAkB,WAAW;AAC7B,QAAI,kBAAkB,YAAY,GAAG;AACnC,wBAAkB,IAAI;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,sBAAkB,UAAU,KAAK,IAAI,GAAG,kBAAkB,UAAU,CAAC;AACrE,QAAI,kBAAkB,YAAY,GAAG;AACnC,wBAAkB,KAAK;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAC5D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,EAAE;AACnD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AACpE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAwB,IAAI;AACtE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,KAAK;AACtE,QAAM,mBAAmB,CAAC,oBAAoB,QAAQ,aAAa,KAAK;AACxE,QAAM,qBAAqB,CAAC,mBAAmB,CAAC,iBAAiB,CAAC;AAClE,QAAM,yBAAyB,CAAC;AAChC,QAAM,cAAc,MAAM,OAAmC,IAAI;AACjE,QAAM,UAAU,MAAM,OAA+B,IAAI;AACzD,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,QAAI,CAAC,UAAW;AAChB,oBAAgB,IAAI;AACpB,WAAO,sBAAsB,MAAM;AACjC,UAAI,kBAAkB;AACpB,cAAM,mBAAmB,QAAQ,SAAS,cAAc,UAAU;AAClE,YAAI,4BAA4B,qBAAqB;AACnD,2BAAiB,MAAM;AACvB,2BAAiB,eAAe,EAAE,UAAU,UAAU,OAAO,SAAS,CAAC;AACvE;AAAA,QACF;AAAA,MACF;AACA,YAAM,UAAU,YAAY;AAC5B,UAAI,CAAC,QAAS;AACd,cAAQ,MAAM;AACd,cAAQ,eAAe,EAAE,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,IAChE,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,WAAW,gBAAgB,CAAC;AACzC,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,MAAM,SAI9D,IAAI;AACN,QAAM,CAAC,wBAAwB,yBAAyB,IAAI,MAAM,SAAS,KAAK;AAChF,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,MAAM,SAAwB,IAAI;AAC5F,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwC,EAAE,IAAI,IAAI,OAAO,GAAG,CAAC;AAC7G,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAwB,IAAI;AAChF,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAwB,IAAI;AAC1E,QAAM,qBAAqB,MAAM,OAAmC,IAAI;AACxE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,CAAC;AACxD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,CAAC;AAItD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAwB,IAAI;AAC9E,QAAM,YAAY,OAAO,YAAY,aAAa;AAElD,QAAM,UAAU,MAAM;AACpB,UAAM,gBAAgB,OAAO,aAAa,WAAW,WAAW;AAChE,UAAM,cAAc,OAAO,WAAW,WAAW,SAAS;AAC1D,QAAI,CAAC,iBAAiB,CAAC,aAAa;AAClC,eAAS,CAAC,CAAC;AACX,mBAAa,IAAI;AACjB,mBAAa,KAAK;AAClB,qBAAe,CAAC;AAChB,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,iBAAa,IAAI;AACjB,iBAAa,IAAI;AACjB,gBAAY;AACZ,mBAAe,YAAY;AACzB,UAAI;AACF,YAAI,YAAY,UAAU;AACxB,gBAAM,aAAa,MAAM,YAAY,SAAS;AAAA,YAC5C,UAAU,iBAAiB;AAAA,YAC3B,QAAQ,eAAe;AAAA,YACvB,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS;AAAA,UACX,CAAC;AACD,cAAI,UAAW;AACf,mBAAS,WAAW,KAAK;AACzB,yBAAe,WAAW,IAAI;AAC9B,qBAAW,gBAAgB,WAAW,MAAM,QAAQ,eAAe,CAAC;AACpE;AAAA,QACF;AACA,cAAM,SAAS,MAAM,YAAY,KAAK;AAAA,UACpC,UAAU,iBAAiB;AAAA,UAC3B,QAAQ,eAAe;AAAA,UACvB,SAAS;AAAA,QACX,CAAC;AACD,YAAI,UAAW;AACf,iBAAS,MAAM;AACf,uBAAe,CAAC;AAChB,mBAAW,KAAK;AAAA,MAClB,SAAS,KAAK;AACZ,YAAI,UAAW;AACf,cAAM,UACJ,eAAe,QAAQ,IAAI,UAAU,MAAM,aAAa,uBAAuB;AACjF,iBAAS,CAAC,CAAC;AACX,qBAAa,OAAO;AACpB,uBAAe,CAAC;AAChB,mBAAW,KAAK;AAChB,cAAM,SAAS,OAAO;AAAA,MACxB,UAAE;AACA,YAAI,CAAC,UAAW,cAAa,KAAK;AAClC,mBAAW;AAAA,MACb;AAAA,IACF;AACA,cAAU,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC1B,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,aAAa,aAAa,QAAQ,UAAU,OAAO,YAAY,WAAW,CAAC;AAE/E,QAAM,WAAW,MAAM,OAAO,KAAK;AACnC,QAAM,cAAc,MAAM,QAAQ,MAAM,cAAc,eAAe,MAAM,CAAC,aAAa,UAAU,CAAC;AAEpG,QAAM,uBAAuB,MAAM,YAAY,MAAM;AACnD,yBAAqB,CAAC,SAAS;AAC7B,YAAM,OAAO,CAAC;AACd,UAAI,yBAAyB;AAC3B,gCAAwB,IAAI;AAAA,MAC9B;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,uBAAuB,CAAC;AAE5B,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,eAAgB;AACrB,QAAI,CAAC,MAAM,QAAQ;AACjB,qBAAe,IAAI;AACnB;AAAA,IACF;AACA,mBAAe;AAAA,MACb,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,gBAAgB,aAAa,CAAC;AAAA,MACxC,MAAM,oBAAC,QAAK,WAAU,gBAAe;AAAA,IACvC,CAAC;AACD,WAAO,MAAM,eAAe,IAAI;AAAA,EAClC,GAAG,CAAC,gBAAgB,gBAAgB,eAAe,WAAW,WAAW,cAAc,MAAM,MAAM,CAAC;AAEpG,QAAM,qBAAqB,MAAM,YAAY,CAAC,YAAwC;AACpF,QAAI,CAAC,QAAS;AACd,YAAQ,MAAM,SAAS;AACvB,YAAQ,MAAM,SAAS,GAAG,QAAQ,YAAY;AAAA,EAChD,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,uBAAmB,YAAY,OAAO;AAAA,EACxC,GAAG,CAAC,oBAAoB,WAAW,kBAAkB,YAAY,CAAC;AAElE,QAAM,UAAU,MAAM;AACpB,UAAM,aAAa,yBAAyB,uBAAuB,IAAI;AACvE,QAAI,eAAe,MAAM;AACvB,2BAAqB,UAAU;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW;AACb,sBAAgB,MAAM,MAAM;AAC5B;AAAA,IACF;AACA,QAAI,CAAC,MAAM,QAAQ;AACjB,sBAAgB,CAAC;AACjB;AAAA,IACF;AACA,UAAM,WAAW,KAAK,IAAI,GAAG,MAAM,MAAM;AACzC,oBAAgB,CAAC,SAAS;AACxB,UAAI,QAAQ,MAAM,OAAQ,QAAO;AACjC,aAAO,KAAK,IAAI,KAAK,IAAI,MAAM,QAAQ,GAAG,MAAM,MAAM;AAAA,IACxD,CAAC;AAAA,EACH,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC;AAE5B,QAAM,UAAU,MAAM;AACpB,QAAI,UAAW;AACf,oBAAgB,KAAK;AACrB,iBAAa,EAAE;AACf,iBAAa,IAAI;AACjB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,eAAe,MAAM;AAAA,IACzB,MAAO,YAAY,QAAQ,MAAM,MAAM,GAAG,YAAY;AAAA,IACtD,CAAC,OAAO,WAAW,YAAY;AAAA,EACjC;AACA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAO,YAAY,MAAM,SAAS,IAAI,eAAe;AAAA,IACrD,CAAC,MAAM,QAAQ,WAAW,YAAY;AAAA,EACxC;AAEA,QAAM,gBAAgB,MAAM,UAAU;AAEtC,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO,UAA2F;AAChG,UAAI,CAAC,aAAa,CAAC,kBAAkB;AACnC,cAAM,MAAM,iBAAiB,qCAAqC,GAAG,OAAO;AAC5E,eAAO;AAAA,MACT;AACA,YAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,YAAM,eAAe,KAClB,QAAQ,6BAA6B,EAAE,EACvC,QAAQ,QAAQ,EAAE;AACrB,UAAI,CAAC,QAAQ,CAAC,aAAa,QAAQ;AACjC,sBAAc;AACd,eAAO;AAAA,MACT;AACA,YAAM,OAAO,MAAM,kBAAkB,MAAM,eAAe,KAAK,EAAE,SAAS,MAAM,eAAe,KAAK,IAAI;AACxG,YAAM,QAAQ,iBAAiB,MAAM,eAAe;AACpD,YAAM,eAAe,eAAe,SAAS,iBAAiB;AAC9D,YAAM,YAAY,eAAe,aAAa,IAAI,YAAY,KAAK,OAAO;AAC1E,sBAAgB,IAAI;AACpB,kBAAY;AACZ,UAAI;AACF,cAAM,eACH,MAAM,YAAY,OAAO;AAAA,UACxB,UAAU;AAAA,UACV;AAAA,UACA,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,QAAQ;AAAA,UACR,SAAS;AAAA,QACX,CAAC,KAAM,CAAC;AACV,iBAAS,CAAC,SAAS;AACjB,gBAAM,WAAW,gBAAgB;AACjC,gBAAM,mBACJ,OAAO,cAAc,iBAAiB,WAAW,aAAa,eAAe,YAAY;AAC3F,gBAAM,sBAAsB,MAAM;AAChC,gBAAI,oBAAoB,YAAY,qBAAqB,UAAU;AACjE,qBAAO;AAAA,YACT;AACA,mBAAO,OAAO,cAAc,eAAe,WAAW,aAAa,aAAa;AAAA,UAClF,GAAG;AACH,gBAAM,uBAAuB,MAAM;AACjC,gBAAI,oBAAoB,YAAY,qBAAqB,UAAU;AACjE,qBAAO,eAAe;AAAA,YACxB;AACA,mBAAO,OAAO,cAAc,gBAAgB,WAAW,aAAa,cAAc;AAAA,UACpF,GAAG;AACH,gBAAM,UAA0B;AAAA,YAC9B,IAAI,OAAO,cAAc,OAAO,WAAW,aAAa,KAAK,eAAe;AAAA,YAC5E;AAAA,YACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,cAAc;AAAA,YACd,YAAY;AAAA,YACZ,aAAa;AAAA,YACb,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,gBAAgB;AAAA,YAChB,iBAAiB;AAAA,UACnB;AACA,iBAAO,CAAC,SAAS,GAAG,IAAI;AAAA,QAC1B,CAAC;AACD,wBAAgB,CAAC,SAAS,KAAK,IAAI,MAAM,CAAC,CAAC;AAC3C,cAAM,MAAM,SAAS,GAAG,SAAS;AACjC,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,MAAM,OAAO;AAClE,YAAI,CAAC,sBAAsB,KAAK,CAAC,EAAG,OAAM,SAAS,OAAO;AAC1D,eAAO;AAAA,MACT,UAAE;AACA,wBAAgB,KAAK;AACrB,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,CAAC,aAAa,aAAa,cAAc,eAAe,WAAW,YAAY,aAAa,gBAAgB,kBAAkB,GAAG,aAAa,aAAa,cAAc,QAAQ;AAAA,EACnL;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO,QAAgB,UAA8F;AACnH,YAAM,gBAAgB,MAAM;AAC5B,YAAM,gBACJ,MAAM,mBAAmB,UAAa,MAAM,mBAAmB,QAAQ,MAAM,eAAe,KAAK,EAAE,SAC/F,MAAM,eAAe,KAAK,IAC1B,MAAM,mBAAmB,OACvB,OACA;AACR,YAAM,iBACJ,MAAM,oBAAoB,SAAY,iBAAiB,MAAM,mBAAmB,IAAI,IAAI;AAC1F,YAAM,gBAAgB,MAAM,KAAK,CAAC,YAAY,QAAQ,OAAO,MAAM,GAAG,aAAa;AACnF,UAAI;AACF,cAAM,YAAY,OAAO;AAAA,UACvB,IAAI;AAAA,UACJ,OAAO;AAAA,YACL,MAAM;AAAA,YACN,gBAAgB;AAAA,YAChB,iBAAiB;AAAA,UACnB;AAAA,UACA,WAAW;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AACD,iBAAS,CAAC,SAAS;AACjB,gBAAM,eAAe,KAAK,IAAI,CAAC,YAAY;AACzC,gBAAI,QAAQ,OAAO,OAAQ,QAAO;AAClC,kBAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,gBAAI,kBAAkB,OAAW,MAAK,OAAO;AAC7C,gBAAI,kBAAkB,OAAW,MAAK,iBAAiB,iBAAiB;AACxE,gBAAI,mBAAmB,OAAW,MAAK,kBAAkB,kBAAkB;AAC3E,mBAAO;AAAA,UACT,CAAC;AACD,iBAAO;AAAA,QACT,CAAC;AACD,cAAM,MAAM,eAAe,GAAG,SAAS;AAAA,MACzC,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,MAAM,aAAa;AAC5E,YAAI,CAAC,sBAAsB,OAAO,CAAC,EAAG,OAAM,SAAS,OAAO;AAC5D,cAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO;AAAA,MAC1D;AAAA,IACF;AAAA,IACA,CAAC,aAAa,aAAa,OAAO,CAAC;AAAA,EACrC;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO,SAAyB;AAC9B,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B,OAAO,MAAM,iBAAiB,6DAA6D;AAAA,QAC3F,SAAS;AAAA,MACX,CAAC;AACD,UAAI,CAAC,UAAW;AAChB,wBAAkB,KAAK,EAAE;AACzB,kBAAY;AACZ,UAAI;AACF,cAAM,YAAY,OAAO,EAAE,IAAI,KAAK,IAAI,WAAW,KAAK,aAAa,MAAM,SAAS,YAAY,CAAC;AACjG,iBAAS,CAAC,SAAS,KAAK,OAAO,CAAC,aAAa,SAAS,OAAO,KAAK,EAAE,CAAC;AACrE,YAAI,WAAW;AACb,0BAAgB,CAAC,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,QACjD;AACA,cAAM,MAAM,iBAAiB,cAAc,GAAG,SAAS;AAAA,MACzD,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,MAAM,eAAe,uBAAuB;AACjG,YAAI,CAAC,sBAAsB,KAAK,CAAC,EAAG,OAAM,SAAS,OAAO;AAAA,MAC5D,UAAE;AACA,0BAAkB,IAAI;AACtB,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,aAAa,OAAO,YAAY,aAAa,CAAC;AAAA,EACvE;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,UAA4C;AACjD,YAAM,eAAe;AACrB,YAAM,UAAU,MAAM,iBAAiB;AAAA,QACrC,MAAM;AAAA,QACN,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,SAAS;AACX,qBAAa,EAAE;AACf,qBAAa,IAAI;AACjB,sBAAc,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,IACA,CAAC,WAAW,YAAY,WAAW,gBAAgB;AAAA,EACrD;AAEA,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,QAAI,aAAa,YAAY,UAAU;AACrC,UAAI,CAAC,WAAW,UAAW;AAC3B,YAAM,gBAAgB,OAAO,aAAa,WAAW,WAAW;AAChE,YAAM,cAAc,OAAO,WAAW,WAAW,SAAS;AAC1D,mBAAa,IAAI;AACjB,kBAAY;AACZ,WAAK,YAAY,SAAS;AAAA,QACxB,UAAU,iBAAiB;AAAA,QAC3B,QAAQ,eAAe;AAAA,QACvB,MAAM,cAAc;AAAA,QACpB,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC,EACE,KAAK,CAAC,eAAe;AACpB,iBAAS,CAAC,SAAS;AACjB,gBAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC1D,qBAAW,MAAM,QAAQ,CAAC,SAAS,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC;AAC5D,iBAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AAAA,QACnC,CAAC;AACD,uBAAe,WAAW,IAAI;AAC9B,mBAAW,gBAAgB,WAAW,MAAM,QAAQ,eAAe,CAAC;AAAA,MACtE,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,cAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU,MAAM,aAAa,uBAAuB;AACrF,cAAM,SAAS,OAAO;AAAA,MACxB,CAAC,EACA,QAAQ,MAAM;AACb,qBAAa,KAAK;AAClB,mBAAW;AAAA,MACb,CAAC;AACH;AAAA,IACF;AACA,oBAAgB,CAAC,SAAS;AACxB,UAAI,QAAQ,MAAM,OAAQ,QAAO;AACjC,aAAO,KAAK,IAAI,OAAO,GAAG,MAAM,MAAM;AAAA,IACxC,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,aAAa,aAAa,QAAQ,UAAU,OAAO,SAAS,WAAW,OAAO,MAAM,QAAQ,WAAW,YAAY,WAAW,CAAC;AAEhJ,QAAM,+BAA+B,MAAM,YAAY,YAAY;AACjE,QAAI,CAAC,sBAAuB;AAC5B,6BAAyB,IAAI;AAC7B,UAAM,gBACJ,sBAAsB,QAAQ,sBAAsB,KAAK,KAAK,EAAE,SAC5D,sBAAsB,KAAK,KAAK,IAChC;AACN,UAAM,iBAAiB,iBAAiB,sBAAsB,SAAS,IAAI;AAC3E,QAAI,sBAAsB,SAAS,UAAU;AAC3C,mBAAa,aAAa;AAC1B,oBAAc,cAAc;AAC5B,+BAAyB,IAAI;AAC7B;AAAA,IACF;AACA,8BAA0B,IAAI;AAC9B,QAAI;AACF,YAAM,iBAAiB,sBAAsB,QAAQ;AAAA,QACnD,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,MACnB,CAAC;AACD,+BAAyB,IAAI;AAAA,IAC/B,SAAS,KAAK;AACZ,YAAM,UACJ,eAAe,QACX,IAAI,UACJ,MAAM,oBAAoB,8BAA8B;AAC9D,+BAAyB,OAAO;AAAA,IAClC,UAAE;AACA,gCAA0B,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,uBAAuB,kBAAkB,CAAC,CAAC;AAE/C,QAAM,8BAA8B,MAAM,YAAY,MAAM;AAC1D,QAAI,uBAAwB;AAC5B,6BAAyB,IAAI;AAC7B,6BAAyB,IAAI;AAAA,EAC/B,GAAG,CAAC,sBAAsB,CAAC;AAE3B,QAAM,oBAAoB,MAAM,YAAY,YAAY;AACtD,QAAI,CAAC,cAAc,GAAI;AACvB,UAAM,UAAU,cAAc,MAAM,KAAK;AACzC,QAAI,CAAC,SAAS;AACZ,sBAAgB,MAAM,eAAe,uBAAuB,CAAC;AAC7D;AAAA,IACF;AACA,uBAAmB,cAAc,EAAE;AACnC,oBAAgB,IAAI;AACpB,QAAI;AACF,YAAM,iBAAiB,cAAc,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC1D,uBAAiB,EAAE,IAAI,IAAI,OAAO,GAAG,CAAC;AAAA,IACxC,SAAS,KAAK;AACZ,YAAM,UACJ,eAAe,QAAQ,IAAI,UAAU,MAAM,eAAe,uBAAuB;AACnF,sBAAgB,OAAO;AAAA,IACzB,UAAE;AACA,yBAAmB,IAAI;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,eAAe,kBAAkB,CAAC,CAAC;AAEvC,QAAM,6BAA6B,MAAM;AAAA,IACvC,CAAC,UAA+B;AAC9B,UAAI,CAAC,cAAc,GAAI;AACvB,WAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,SAAS;AAC7D,cAAM,eAAe;AACrB,YAAI,CAAC,gBAAiB,MAAK,kBAAkB;AAC7C;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,UAAU;AAC1B,cAAM,eAAe;AACrB,yBAAiB,EAAE,IAAI,IAAI,OAAO,GAAG,CAAC;AACtC,wBAAgB,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,IACA,CAAC,cAAc,IAAI,iBAAiB,iBAAiB;AAAA,EACvD;AAEA,QAAM,wBAAwB,MAAM;AAAA,IAClC,CAAC,UAAgD;AAC/C,WAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,SAAS;AAC7D,cAAM,eAAe;AACrB,gBAAQ,SAAS,cAAc;AAAA,MACjC;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,uBAAuB,MAAM;AAAA,IACjC,CAAC,OAA4C,SAAyB;AACpE,UAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,cAAM,eAAe;AACrB,yBAAiB,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,CAAC,SAAyB;AACxB,UAAI,KAAK,gBAAgB,gBAAgB,KAAK,iBAAiB,cAAc;AAC3E,eAAO;AAAA,MACT;AACA,aAAO,KAAK,cAAc,KAAK,eAAe;AAAA,IAChD;AAAA,IACA,CAAC,cAAc,QAAQ;AAAA,EACzB;AAEA,QAAM,uBAAuB,MAAM;AAAA,IACjC,OAAO;AAAA,MACL,YAAY,MAAM,uBAAuB;AAAA,MACzC,WAAW,MAAM,sBAAsB;AAAA,MACvC,iBAAiB,MAAM,uBAAuB;AAAA,MAC9C,WAAW,MAAM,sBAAsB;AAAA,MACvC,iBAAiB,MAAM,4BAA4B;AAAA,MACnD,wBAAwB,MAAM,uBAAuB;AAAA,MACrD,uBAAuB,MAAM,kCAAkC;AAAA,MAC/D,sBAAsB,MAAM,4BAA4B;AAAA,MACxD,sBAAsB,MAAM,4BAA4B;AAAA,MACxD,gBAAgB,MAAM,sBAAsB;AAAA,MAC5C,mBAAmB,MAAM,yBAAyB;AAAA,IACpD;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,MAAM;AAAA,IACN,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,wBAAwB,QAAQ,SAAS,KAAK,QAAQ,UAAU;AACtE,QAAM,uBAAuB,0BAA0B;AACvD,QAAM,0BACJ,uBAAuB,SAAS,SAAS,sBAAsB,SAAS;AAC1E,QAAM,uBAAuB,MAAM,eAAe,oCAA0B;AAC5E,QAAM,8BAA8B,MAAM,2BAA2B,2CAAiC;AACtG,QAAM,sBAAsB;AAC5B,QAAM,+BAA+B;AACrC,QAAM,8BACJ,uBAAuB,SAAS,SAC5B,MAAM,mBAAmB,IACzB,MAAM,UAAU,mBAAc;AAEpC,SACE,qBAAC,SAAI,WAAU,kBACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,eAAe,+CAA+C;AAAA,QAChE,EAAE,KAAK,GAAG;AAAA,QACV,eAAa,CAAC;AAAA,QAEb,yBACC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,UAAU;AAAA,YACV,WAAW;AAAA,YACX,WAAU;AAAA,YAEV;AAAA,mCAAC,SAAI,WAAU,qDACb;AAAA,oCAAC,QAAG,WAAU,uBAAuB,gBAAM,UAAU,GAAE;AAAA,gBACvD,qBAAC,SAAI,WAAU,qCACZ;AAAA,2CACC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,SAAS,MAAM;AACb,iDAAyB,IAAI;AAC7B,iDAAyB,EAAE,MAAM,UAAU,MAAM,WAAW,OAAO,WAAW,CAAC;AAAA,sBACjF;AAAA,sBACA,UAAU,gBAAgB,aAAa,CAAC;AAAA,sBAExC;AAAA,4CAAC,UAAK,WAAU,WAAW,gBAAM,yBAAyB,sBAAsB,GAAE;AAAA,wBAClF,oBAAC,WAAQ,WAAU,WAAU;AAAA;AAAA;AAAA,kBAC/B,IACE;AAAA,kBACH,qBACC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAS,mBAAmB,cAAc;AAAA,sBAC1C,MAAK;AAAA,sBACL,SAAS;AAAA,sBACT,gBAAc;AAAA,sBACd,UAAU,gBAAgB;AAAA,sBAE1B,8BAAC,YAAS,WAAU,WAAU;AAAA;AAAA,kBAChC,IACE;AAAA,kBACJ;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,SAAS,MAAM;AACb,wCAAgB,KAAK;AACrB,qCAAa,EAAE;AACf,qCAAa,IAAI;AACjB,sCAAc,IAAI;AAAA,sBACpB;AAAA,sBACA,UAAU,gBAAgB;AAAA,sBAEzB,sBAAY,QAAQ;AAAA;AAAA,kBACvB;AAAA,mBACF;AAAA,iBACF;AAAA,cACE,wBAAwB,UAAU,sBAAsB,SACxD,qBAAC,SAAI,WAAU,6BACZ;AAAA,wCAAwB,SACvB,qBAAC,SAAI,WAAU,uBACb;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,WAAU;AAAA,sBAET,gBAAM,iBAAiB,oBAAoB;AAAA;AAAA,kBAC9C;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,oBAAoB;AAAA,sBAC3B,eAAe,CAAC,SAAS,oBAAoB,QAAQ,EAAE;AAAA,sBACvD,UAAU,gBAAgB,aAAa,CAAC,wBAAwB;AAAA,sBAEhE;AAAA,4CAAC,iBAAc,IAAG,sBAChB,8BAAC,eAAY,GACf;AAAA,wBACA,oBAAC,iBACE,kCAAwB,IAAI,CAAC,WAC5B,oBAAC,cAA2B,OAAO,OAAO,IACvC,iBAAO,SADO,OAAO,EAExB,CACD,GACH;AAAA;AAAA;AAAA,kBACF;AAAA,mBACF,IACE;AAAA,gBACH,sBAAsB,SACrB,qBAAC,SAAI,WAAU,uBACb;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,WAAU;AAAA,sBAET,gBAAM,eAAe,yBAAyB;AAAA;AAAA,kBACjD;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,kBAAkB;AAAA,sBACzB,eAAe,CAAC,SAAS,kBAAkB,QAAQ,EAAE;AAAA,sBACrD,UAAU,gBAAgB;AAAA,sBAE1B;AAAA,4CAAC,iBAAc,IAAG,oBAChB,8BAAC,eAAY,aAAa,MAAM,0BAA0B,gBAAgB,GAAG,GAC/E;AAAA,wBACA,oBAAC,iBACE,gCAAsB,IAAI,CAAC,WAC1B,oBAAC,cAA2B,OAAO,OAAO,IACvC,iBAAO,SADO,OAAO,EAExB,CACD,GACH;AAAA;AAAA;AAAA,kBACF;AAAA,mBACF,IACE;AAAA,iBACN,IACE;AAAA,cACJ;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,mBAAmB;AAAA,kBACnB;AAAA,kBACA,MAAM;AAAA,kBACN,aAAa,MAAM,aAAa;AAAA,kBAChC;AAAA,kBACA,iBAAiB,CAAC,UAAU,mBAAmB,MAAM,aAAa;AAAA,kBAClE,UAAU,gBAAgB,aAAa,CAAC;AAAA,kBACxC,eAAe;AAAA;AAAA,cACjB;AAAA,cACC,wBACC,qBAAC,SAAI,WAAU,0HACb;AAAA,qCAAC,SAAI,WAAU,6CACZ;AAAA,+BAAa,aACZ,oBAAC,UAAK,WAAU,4FACb,qBAAW,WAAW,SAAS,GAClC,IACE;AAAA,kBACJ,oBAAC,UAAK,WAAU,iCAAiC,0BAAe;AAAA,kBAC/D,cAAc,cACb,qBAAC,UAAK,WAAU,2BACb;AAAA,gCAAY,YAAY,+CAA+C;AAAA,oBACxE,oBAAC,UAAK,WAAU,uDAAuD,sBAAW;AAAA,qBACpF,IACE;AAAA,mBACN;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,MAAK;AAAA,oBACL,SAAQ;AAAA,oBACR,SAAS,MAAM;AACb,mCAAa,IAAI;AACjB,oCAAc,IAAI;AAAA,oBACpB;AAAA,oBACA,UAAU;AAAA,oBAET,gBAAM,uBAAuB,OAAO;AAAA;AAAA,gBACvC;AAAA,iBACF,IACE;AAAA,cACJ,oBAAC,SAAI,WAAU,oBACb;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,UAAU,gBAAgB,aAAa,CAAC;AAAA,kBAEvC;AAAA,mCAAe,oBAAC,WAAQ,WAAU,6BAA4B,IAAK;AAAA,oBACnE;AAAA;AAAA;AAAA,cACH,GACF;AAAA;AAAA;AAAA,QACF,IACE;AAAA;AAAA,IACN;AAAA,IAEC,YAAY,oBAAC,gBAAa,OAAO,WAAW,WAAU,QAAO,IAAK;AAAA,IAEnE,qBAAC,SAAI,WAAU,aACZ;AAAA,OAAC,gBAAgB,mBAAmB,CAAC,iBACpC,oBAAC,SAAI,WAAU,oBACb;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU,gBAAgB,aAAa,CAAC;AAAA,UAExC;AAAA,gCAAC,QAAK,WAAU,UAAS;AAAA,YACxB;AAAA;AAAA;AAAA,MACH,GACF,IACE;AAAA,MACH,YACC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM,WAAW,qBAAgB;AAAA,UACxC,WAAU;AAAA;AAAA,MACZ,IACE,kBACF,aAAa,IAAI,CAAC,SAAS;AACzB,cAAM,SAAS,gBAAgB,IAAI;AACnC,cAAM,qBAAqB,0BAA0B,4BAA4B,KAAK;AACtF,cAAM,mBAAmB,cAAc,OAAO,KAAK;AACnD,cAAM,cAAc,KAAK,kBAAkB;AAC3C,cAAM,eAAe,KAAK,mBAAmB;AAC7C,cAAM,iBAAiB,KAAK;AAC5B,cAAM,yBAAyB,eAAe,KAAK,SAAS,KAAK;AACjE,eACE,qBAAC,SAAkB,WAAU,iDAC3B;AAAA,+BAAC,SAAI,WAAU,oDACb;AAAA,iCAAC,SAAI,WAAU,aACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,WAAW;AAAA,kBACX;AAAA,kBACA,MAAM;AAAA,kBACN,OAAO;AAAA,kBACP;AAAA,kBACA;AAAA;AAAA,cACF;AAAA,cACC,KAAK,SACJ,qBAAC,SAAI,WAAU,yDACb;AAAA,oCAAC,sBAAmB,WAAU,eAAc;AAAA,gBAC5C;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAM,4BAA4B,mBAAmB,KAAK,MAAM,CAAC;AAAA,oBACjE,WAAU;AAAA,oBAET,eAAK,aAAa,KAAK,UAAU,SAC9B,KAAK,YACL,MAAM,cAAc,aAAa;AAAA;AAAA,gBACvC;AAAA,iBACF,IACE;AAAA,eACN;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,8CACT,mBAAmB,gBAAgB,8EACrC;AAAA,gBAEA;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,SAAS,MAAM,iBAAiB,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,CAAC;AAAA,sBAEjE,8BAAC,UAAO,WAAU,WAAU;AAAA;AAAA,kBAC9B;AAAA,kBACC,yBACC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,SAAS,CAAC,UAAU;AAClB,8BAAM,gBAAgB;AACtB,iDAAyB,IAAI;AAC7B,iDAAyB;AAAA,0BACvB,MAAM;AAAA,0BACN,QAAQ,KAAK;AAAA,0BACb,MAAM,KAAK,kBAAkB;AAAA,0BAC7B,OAAO,KAAK,mBAAmB;AAAA,wBACjC,CAAC;AAAA,sBACH;AAAA,sBACA,UAAU,0BAA0B,4BAA4B,KAAK;AAAA,sBAEpE,+BAAqB,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,kBACpG,IACE;AAAA,kBACJ;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,SAAS,CAAC,UAAU;AAClB,8BAAM,gBAAgB;AACtB,6BAAK,iBAAiB,IAAI;AAAA,sBAC5B;AAAA,sBACA,UAAU,mBAAmB,KAAK;AAAA,sBAEjC,6BAAmB,KAAK,KACvB,oBAAC,UAAK,WAAU,sEACd,8BAAC,UAAK,WAAU,6FAA4F,GAC9G,IAEA,oBAAC,UAAO,WAAU,WAAU;AAAA;AAAA,kBAEhC;AAAA;AAAA;AAAA,YACF;AAAA,aACF;AAAA,UACC,mBACC,qBAAC,SAAI,WAAU,aAAY,WAAW,4BACpC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO,cAAc;AAAA,gBACrB,UAAU,CAAC,cAAc,iBAAiB,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,UAAU,EAAE;AAAA,gBACnF,mBAAmB;AAAA,gBACnB;AAAA,gBACA,MAAM;AAAA,gBACN,aAAa;AAAA,gBACb,iBAAiB,CAAC,UAAU,mBAAmB,MAAM,aAAa;AAAA,gBAClE,mBAAkB;AAAA,gBAClB,wBAAuB;AAAA,gBACvB,eAAe;AAAA;AAAA,YACjB;AAAA,YACC,eAAe,oBAAC,OAAE,WAAU,kCAAkC,wBAAa,IAAO;AAAA,YACnF,qBAAC,SAAI,WAAU,qCACb;AAAA,kCAAC,UAAO,MAAK,UAAS,MAAK,MAAK,SAAS,mBAAmB,UAAU,oBAAoB,KAAK,IAC5F,8BAAoB,KAAK,KACxB,iCACE;AAAA,oCAAC,WAAQ,WAAU,6BAA4B;AAAA,gBAC9C,MAAM,QAAQ;AAAA,iBACjB,IAEA,YAAY,cAAc,GAE9B;AAAA,cACC,qBACC;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,gBAAc;AAAA,kBACd,WAAW,mBAAmB,iBAAiB;AAAA,kBAC/C,UAAU,oBAAoB,KAAK;AAAA,kBAEnC,8BAAC,YAAS,WAAU,WAAU;AAAA;AAAA,cAChC,IACE;AAAA,cACJ;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,SAAS,MAAM,iBAAiB,EAAE,IAAI,IAAI,OAAO,GAAG,CAAC;AAAA,kBACrD,UAAU,oBAAoB,KAAK;AAAA,kBAElC,sBAAY,QAAQ;AAAA;AAAA,cACvB;AAAA,eACF;AAAA,aACF,IAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU;AAAA,cACV,WAAU;AAAA,cACV,SAAS,MAAM,iBAAiB,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,CAAC;AAAA,cACjE,WAAW,CAAC,UAAU,qBAAqB,OAAO,IAAI;AAAA,cAEtD;AAAA,gBAAC;AAAA;AAAA,kBACC,eAAe;AAAA,kBACf,WAAU;AAAA,kBAET,eAAK;AAAA;AAAA,cACR;AAAA;AAAA,UACF;AAAA,aA/IM,KAAK,EAiJf;AAAA,MAEJ,CAAC,IACC,eAAe,OACjB;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB,aAAa,WAAW;AAAA,UACxB,QAAQ;AAAA,YACN,OAAO,WAAW;AAAA,YAClB,SAAS;AAAA,YACT,UAAU,gBAAgB,CAAC;AAAA,UAC7B;AAAA;AAAA,MACF;AAAA,MAED,cAAc,YAAY,CAAC,UAAU,gBAAgB,MAAM,UAAU,OACpE,oBAAC,SAAI,WAAU,uBACb,8BAAC,UAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,gBAC1C,yBACH,GACF;AAAA,OAEJ;AAAA,IACC,yBACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OACE,uBAAuB,SAAS,SAC5B,MAAM,iBAAiB,IACvB,MAAM,yBAAyB,sBAAsB;AAAA,QAE3D,MAAM,uBAAuB,QAAQ;AAAA,QACrC,OAAO,uBAAuB,SAAS;AAAA,QACvC,QAAQ;AAAA,QACR;AAAA,QACA,cAAc,CAAC,UAAU,yBAAyB,CAAC,SAAU,OAAO,EAAE,GAAG,MAAM,MAAM,SAAS,KAAK,IAAI,IAAK;AAAA,QAC5G,eAAe,CAAC,UAAU,yBAAyB,CAAC,SAAU,OAAO,EAAE,GAAG,MAAM,OAAO,SAAS,KAAK,IAAI,IAAK;AAAA,QAC9G,UAAU,MAAM;AACd,eAAK,6BAA6B;AAAA,QACpC;AAAA,QACA,SAAS;AAAA,QACT,UAAU;AAAA,QACV,cAAc;AAAA,QACd,cAAc;AAAA,QACd,aAAa;AAAA,QACb,aAAa,MAAM,mBAAmB;AAAA;AAAA,IACxC,IACE;AAAA,IACH;AAAA,KACH;AAEJ;AAEO,SAAS,aAA0B,OAA6B;AACrE,QAAM,SAAS,4BAA4B,QAAQ,aAAa,cAAc;AAC9E,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,SAAI,yBAAuB,QAC1B,8BAAC,YAAU,GAAG,OAAO,GACvB;AAEJ;",
6
6
  "names": ["label"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.6.8-develop.7005.1.70a5747c3e",
3
+ "version": "0.6.8-develop.7012.1.1dbd6f5fbd",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -155,14 +155,14 @@
155
155
  "remark-gfm": "^4.0.1"
156
156
  },
157
157
  "peerDependencies": {
158
- "@open-mercato/shared": "0.6.8-develop.7005.1.70a5747c3e",
158
+ "@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
159
159
  "react": ">=18.0.0",
160
160
  "react-dom": ">=18.0.0",
161
161
  "react-is": ">=18.0.0"
162
162
  },
163
163
  "devDependencies": {
164
164
  "@figma/code-connect": "^1.3.4",
165
- "@open-mercato/shared": "0.6.8-develop.7005.1.70a5747c3e",
165
+ "@open-mercato/shared": "0.6.8-develop.7012.1.1dbd6f5fbd",
166
166
  "@testing-library/dom": "^10.4.1",
167
167
  "@testing-library/jest-dom": "^7.0.0",
168
168
  "@testing-library/react": "^16.3.1",
@@ -7,6 +7,33 @@ import { NotesSection, type NotesDataAdapter } from '../detail/NotesSection'
7
7
  import { dismissRecordConflict, getRecordConflictForTest } from '../conflicts'
8
8
  import { OPTIMISTIC_LOCK_CONFLICT_CODE } from '@open-mercato/shared/lib/crud/optimistic-lock-headers'
9
9
 
10
+ const createDataAdapter = (): NotesDataAdapter => ({
11
+ list: jest.fn(async () => [
12
+ {
13
+ id: 'note-1',
14
+ body: 'Existing note',
15
+ createdAt: '2026-04-10T08:00:00.000Z',
16
+ authorName: 'Ada Lovelace',
17
+ },
18
+ ]),
19
+ create: jest.fn(async () => ({ id: 'note-2' })),
20
+ update: jest.fn(async () => undefined),
21
+ delete: jest.fn(async () => undefined),
22
+ })
23
+
24
+ const baseProps = (dataAdapter: NotesDataAdapter) => ({
25
+ entityId: 'person-1',
26
+ emptyLabel: '—',
27
+ viewerUserId: 'user-1',
28
+ viewerName: 'Ada Lovelace',
29
+ addActionLabel: 'Add note',
30
+ emptyState: {
31
+ title: 'No notes yet',
32
+ actionLabel: 'Add note',
33
+ },
34
+ dataAdapter,
35
+ })
36
+
10
37
  describe('NotesSection', () => {
11
38
  beforeEach(() => {
12
39
  dismissRecordConflict()
@@ -171,6 +198,68 @@ describe('NotesSection', () => {
171
198
  })
172
199
  })
173
200
 
201
+ it('renders the markdown toggle and appearance controls by default', async () => {
202
+ const { container } = renderWithProviders(<NotesSection {...baseProps(createDataAdapter())} />)
203
+
204
+ await screen.findByText('Existing note')
205
+ expect(container.querySelectorAll('svg.lucide-palette').length).toBe(1)
206
+
207
+ fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
208
+
209
+ await waitFor(() => {
210
+ expect(container.querySelector('button[aria-pressed]')).not.toBeNull()
211
+ })
212
+ expect(screen.getByRole('button', { name: 'Customize appearance' })).toBeTruthy()
213
+ expect(screen.queryByTestId('markdown-field')).toBeNull()
214
+ })
215
+
216
+ it('keeps the rich editor active and hides the toggle when forceMarkdown is set', async () => {
217
+ const { container } = renderWithProviders(
218
+ <NotesSection {...baseProps(createDataAdapter())} forceMarkdown />,
219
+ )
220
+
221
+ await screen.findByText('Existing note')
222
+ fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
223
+
224
+ await waitFor(() => {
225
+ expect(screen.getByTestId('markdown-field')).toBeTruthy()
226
+ })
227
+ expect(container.querySelector('button[aria-pressed]')).toBeNull()
228
+ })
229
+
230
+ it('hides the toggle but honors the seeded preference when hideMarkdownToggle is set', async () => {
231
+ const { container } = renderWithProviders(
232
+ <NotesSection
233
+ {...baseProps(createDataAdapter())}
234
+ hideMarkdownToggle
235
+ readMarkdownPreference={() => true}
236
+ />,
237
+ )
238
+
239
+ await screen.findByText('Existing note')
240
+ fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
241
+
242
+ await waitFor(() => {
243
+ expect(screen.getByTestId('markdown-field')).toBeTruthy()
244
+ })
245
+ expect(container.querySelector('button[aria-pressed]')).toBeNull()
246
+ })
247
+
248
+ it('removes every appearance affordance when disableAppearance is set', async () => {
249
+ const { container } = renderWithProviders(
250
+ <NotesSection {...baseProps(createDataAdapter())} disableAppearance />,
251
+ )
252
+
253
+ await screen.findByText('Existing note')
254
+ fireEvent.click(screen.getByRole('button', { name: 'Add note' }))
255
+
256
+ await waitFor(() => {
257
+ expect(container.querySelector('button[aria-pressed]')).not.toBeNull()
258
+ })
259
+ expect(screen.queryByRole('button', { name: 'Customize appearance' })).toBeNull()
260
+ expect(container.querySelectorAll('svg.lucide-palette').length).toBe(0)
261
+ })
262
+
174
263
  it('surfaces the unified conflict bar when a write fails with a 409', async () => {
175
264
  const conflict = {
176
265
  status: 409,
@@ -207,7 +207,20 @@ export type NotesSectionProps<C = unknown> = {
207
207
  iconSuggestions?: IconOption[]
208
208
  readMarkdownPreference?: () => boolean | null
209
209
  writeMarkdownPreference?: (value: boolean) => void
210
+ /** Force the plain textarea and hide the markdown toggle. Wins over `forceMarkdown`. */
210
211
  disableMarkdown?: boolean
212
+ /**
213
+ * Keep the rich markdown editor active for every note and hide the markdown toggle,
214
+ * so the editor shape cannot be switched. Ignored when `disableMarkdown` is set.
215
+ */
216
+ forceMarkdown?: boolean
217
+ /**
218
+ * Hide the markdown toggle without changing which editor renders — the seeded
219
+ * `readMarkdownPreference` value stays in effect. Implied by `forceMarkdown`.
220
+ */
221
+ hideMarkdownToggle?: boolean
222
+ /** Hide the appearance (icon/color) affordances: the palette buttons and the appearance dialog. */
223
+ disableAppearance?: boolean
211
224
  }
212
225
 
213
226
  export function sanitizeHexColor(value: string | null): string | null {
@@ -313,6 +326,9 @@ function NotesSectionImpl<C = unknown>({
313
326
  readMarkdownPreference,
314
327
  writeMarkdownPreference,
315
328
  disableMarkdown,
329
+ forceMarkdown,
330
+ hideMarkdownToggle,
331
+ disableAppearance,
316
332
  }: NotesSectionProps<C>) {
317
333
  const { confirm, ConfirmDialogElement } = useConfirmDialog()
318
334
  const t = React.useMemo<Translator>(() => translator ?? ((key, fallback) => fallback ?? key), [translator])
@@ -448,13 +464,16 @@ function NotesSectionImpl<C = unknown>({
448
464
  const [draftIcon, setDraftIcon] = React.useState<string | null>(null)
449
465
  const [draftColor, setDraftColor] = React.useState<string | null>(null)
450
466
  const [isMarkdownEnabled, setIsMarkdownEnabled] = React.useState(false)
467
+ const isMarkdownActive = !disableMarkdown && (Boolean(forceMarkdown) || isMarkdownEnabled)
468
+ const showMarkdownToggle = !disableMarkdown && !forceMarkdown && !hideMarkdownToggle
469
+ const showAppearanceControls = !disableAppearance
451
470
  const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)
452
471
  const formRef = React.useRef<HTMLFormElement | null>(null)
453
472
  const focusComposer = React.useCallback(() => {
454
473
  if (!hasEntity) return
455
474
  setComposerOpen(true)
456
475
  window.requestAnimationFrame(() => {
457
- if (isMarkdownEnabled) {
476
+ if (isMarkdownActive) {
458
477
  const markdownTextarea = formRef.current?.querySelector('textarea')
459
478
  if (markdownTextarea instanceof HTMLTextAreaElement) {
460
479
  markdownTextarea.focus()
@@ -467,7 +486,7 @@ function NotesSectionImpl<C = unknown>({
467
486
  element.focus()
468
487
  element.scrollIntoView({ behavior: 'smooth', block: 'center' })
469
488
  })
470
- }, [formRef, hasEntity, isMarkdownEnabled])
489
+ }, [formRef, hasEntity, isMarkdownActive])
471
490
  const [appearanceDialogState, setAppearanceDialogState] = React.useState<
472
491
  | { mode: 'create'; icon: string | null; color: string | null }
473
492
  | { mode: 'edit'; noteId: string; icon: string | null; color: string | null }
@@ -584,7 +603,7 @@ function NotesSectionImpl<C = unknown>({
584
603
 
585
604
  React.useEffect(() => {
586
605
  adjustTextareaSize(textareaRef.current)
587
- }, [adjustTextareaSize, draftBody, isMarkdownEnabled, composerOpen])
606
+ }, [adjustTextareaSize, draftBody, isMarkdownActive, composerOpen])
588
607
 
589
608
  React.useEffect(() => {
590
609
  const preference = readMarkdownPreference ? readMarkdownPreference() : null
@@ -990,31 +1009,33 @@ function NotesSectionImpl<C = unknown>({
990
1009
  <div className="flex flex-wrap items-center justify-between gap-2">
991
1010
  <h3 className="text-sm font-medium">{label('addLabel')}</h3>
992
1011
  <div className="flex flex-wrap items-center gap-1">
993
- <Button
994
- type="button"
995
- variant="ghost"
996
- size="icon"
997
- onClick={() => {
998
- setAppearanceDialogError(null)
999
- setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })
1000
- }}
1001
- disabled={isSubmitting || isLoading || !hasEntity}
1002
- >
1003
- <span className="sr-only">{label('appearance.toggleOpen', 'Customize appearance')}</span>
1004
- <Palette className="h-4 w-4" />
1005
- </Button>
1006
- {disableMarkdown ? null : (
1012
+ {showAppearanceControls ? (
1007
1013
  <Button
1008
1014
  type="button"
1009
- variant={isMarkdownEnabled ? 'secondary' : 'ghost'}
1015
+ variant="ghost"
1016
+ size="icon"
1017
+ onClick={() => {
1018
+ setAppearanceDialogError(null)
1019
+ setAppearanceDialogState({ mode: 'create', icon: draftIcon, color: draftColor })
1020
+ }}
1021
+ disabled={isSubmitting || isLoading || !hasEntity}
1022
+ >
1023
+ <span className="sr-only">{label('appearance.toggleOpen', 'Customize appearance')}</span>
1024
+ <Palette className="h-4 w-4" />
1025
+ </Button>
1026
+ ) : null}
1027
+ {showMarkdownToggle ? (
1028
+ <Button
1029
+ type="button"
1030
+ variant={isMarkdownActive ? 'secondary' : 'ghost'}
1010
1031
  size="icon"
1011
1032
  onClick={handleMarkdownToggle}
1012
- aria-pressed={isMarkdownEnabled}
1033
+ aria-pressed={isMarkdownActive}
1013
1034
  disabled={isSubmitting || isLoading}
1014
1035
  >
1015
1036
  <FileCode className="h-4 w-4" />
1016
1037
  </Button>
1017
- )}
1038
+ ) : null}
1018
1039
  <Button
1019
1040
  type="button"
1020
1041
  size="sm"
@@ -1090,7 +1111,7 @@ function NotesSectionImpl<C = unknown>({
1090
1111
  <SwitchableMarkdownInput
1091
1112
  value={draftBody}
1092
1113
  onChange={setDraftBody}
1093
- isMarkdownEnabled={isMarkdownEnabled}
1114
+ isMarkdownEnabled={isMarkdownActive}
1094
1115
  disableMarkdown={disableMarkdown}
1095
1116
  rows={1}
1096
1117
  placeholder={label('placeholder')}
@@ -1214,24 +1235,26 @@ function NotesSectionImpl<C = unknown>({
1214
1235
  >
1215
1236
  <Pencil className="h-4 w-4" />
1216
1237
  </Button>
1217
- <Button
1218
- type="button"
1219
- variant="ghost"
1220
- size="icon"
1221
- onClick={(event) => {
1222
- event.stopPropagation()
1223
- setAppearanceDialogError(null)
1224
- setAppearanceDialogState({
1225
- mode: 'edit',
1226
- noteId: note.id,
1227
- icon: note.appearanceIcon ?? null,
1228
- color: note.appearanceColor ?? null,
1229
- })
1230
- }}
1231
- disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}
1232
- >
1233
- {isAppearanceSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Palette className="h-4 w-4" />}
1234
- </Button>
1238
+ {showAppearanceControls ? (
1239
+ <Button
1240
+ type="button"
1241
+ variant="ghost"
1242
+ size="icon"
1243
+ onClick={(event) => {
1244
+ event.stopPropagation()
1245
+ setAppearanceDialogError(null)
1246
+ setAppearanceDialogState({
1247
+ mode: 'edit',
1248
+ noteId: note.id,
1249
+ icon: note.appearanceIcon ?? null,
1250
+ color: note.appearanceColor ?? null,
1251
+ })
1252
+ }}
1253
+ disabled={appearanceDialogSaving && editingAppearanceNoteId === note.id}
1254
+ >
1255
+ {isAppearanceSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Palette className="h-4 w-4" />}
1256
+ </Button>
1257
+ ) : null}
1235
1258
  <Button
1236
1259
  type="button"
1237
1260
  variant="ghost"
@@ -1257,7 +1280,7 @@ function NotesSectionImpl<C = unknown>({
1257
1280
  <SwitchableMarkdownInput
1258
1281
  value={contentEditor.value}
1259
1282
  onChange={(nextValue) => setContentEditor((prev) => ({ ...prev, value: nextValue }))}
1260
- isMarkdownEnabled={isMarkdownEnabled}
1283
+ isMarkdownEnabled={isMarkdownActive}
1261
1284
  disableMarkdown={disableMarkdown}
1262
1285
  rows={3}
1263
1286
  textareaRef={contentTextareaRef}
@@ -1278,19 +1301,19 @@ function NotesSectionImpl<C = unknown>({
1278
1301
  inlineLabel('saveShortcut')
1279
1302
  )}
1280
1303
  </Button>
1281
- {disableMarkdown ? null : (
1304
+ {showMarkdownToggle ? (
1282
1305
  <Button
1283
1306
  type="button"
1284
1307
  variant="ghost"
1285
1308
  size="icon"
1286
1309
  onClick={handleMarkdownToggle}
1287
- aria-pressed={isMarkdownEnabled}
1288
- className={isMarkdownEnabled ? 'text-primary' : undefined}
1310
+ aria-pressed={isMarkdownActive}
1311
+ className={isMarkdownActive ? 'text-primary' : undefined}
1289
1312
  disabled={contentSavingId === note.id}
1290
1313
  >
1291
1314
  <FileCode className="h-4 w-4" />
1292
1315
  </Button>
1293
- )}
1316
+ ) : null}
1294
1317
  <Button
1295
1318
  type="button"
1296
1319
  size="sm"
@@ -1340,29 +1363,31 @@ function NotesSectionImpl<C = unknown>({
1340
1363
  </div>
1341
1364
  )}
1342
1365
  </div>
1343
- <AppearanceDialog
1344
- open={appearanceDialogOpen}
1345
- title={
1346
- appearanceDialogState?.mode === 'edit'
1347
- ? label('appearance.edit')
1348
- : label('appearance.toggleOpen', 'Customize appearance')
1349
- }
1350
- icon={appearanceDialogState?.icon ?? null}
1351
- color={appearanceDialogState?.color ?? null}
1352
- labels={noteAppearanceLabels}
1353
- iconSuggestions={iconSuggestions}
1354
- onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}
1355
- onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}
1356
- onSubmit={() => {
1357
- void handleAppearanceDialogSubmit()
1358
- }}
1359
- onClose={handleAppearanceDialogClose}
1360
- isSaving={appearanceDialogSaving}
1361
- errorMessage={appearanceDialogError}
1362
- primaryLabel={appearanceDialogPrimaryLabel}
1363
- savingLabel={appearanceDialogSavingLabel}
1364
- cancelLabel={label('appearance.cancel')}
1365
- />
1366
+ {showAppearanceControls ? (
1367
+ <AppearanceDialog
1368
+ open={appearanceDialogOpen}
1369
+ title={
1370
+ appearanceDialogState?.mode === 'edit'
1371
+ ? label('appearance.edit')
1372
+ : label('appearance.toggleOpen', 'Customize appearance')
1373
+ }
1374
+ icon={appearanceDialogState?.icon ?? null}
1375
+ color={appearanceDialogState?.color ?? null}
1376
+ labels={noteAppearanceLabels}
1377
+ iconSuggestions={iconSuggestions}
1378
+ onIconChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, icon: value ?? null } : prev))}
1379
+ onColorChange={(value) => setAppearanceDialogState((prev) => (prev ? { ...prev, color: value ?? null } : prev))}
1380
+ onSubmit={() => {
1381
+ void handleAppearanceDialogSubmit()
1382
+ }}
1383
+ onClose={handleAppearanceDialogClose}
1384
+ isSaving={appearanceDialogSaving}
1385
+ errorMessage={appearanceDialogError}
1386
+ primaryLabel={appearanceDialogPrimaryLabel}
1387
+ savingLabel={appearanceDialogSavingLabel}
1388
+ cancelLabel={label('appearance.cancel')}
1389
+ />
1390
+ ) : null}
1366
1391
  {ConfirmDialogElement}
1367
1392
  </div>
1368
1393
  )