@datum-cloud/datum-ui 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { n as badgeVariants, t as Badge } from "../badge-BTNW_FEU.mjs";
1
+ import { n as badgeVariants, t as Badge } from "../badge-DOZUr5DV.mjs";
2
2
  export { Badge, badgeVariants };
@@ -25,7 +25,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
25
25
  theme: {
26
26
  solid: "",
27
27
  outline: "border",
28
- light: "border"
28
+ light: "border-transparent"
29
29
  }
30
30
  },
31
31
  compoundVariants: [
@@ -42,7 +42,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
42
42
  {
43
43
  type: "primary",
44
44
  theme: "light",
45
- className: "border-[var(--color-badge-primary)]/30 text-[var(--color-badge-primary)] bg-[var(--color-badge-primary)]/10 dark:border-[var(--color-badge-primary)] dark:text-[var(--color-badge-primary)] dark:bg-[var(--color-badge-primary)]/20"
45
+ className: "text-[var(--color-badge-primary)] bg-[var(--color-badge-primary)]/10 dark:text-[var(--color-badge-primary)] dark:bg-[var(--color-badge-primary)]/20"
46
46
  },
47
47
  {
48
48
  type: "secondary",
@@ -57,7 +57,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
57
57
  {
58
58
  type: "secondary",
59
59
  theme: "light",
60
- className: "border-[var(--color-badge-secondary)] text-[var(--color-badge-secondary)] bg-[var(--color-badge-secondary)]/20 dark:border-[var(--color-badge-secondary)] dark:text-[var(--color-badge-secondary)] dark:bg-[var(--color-badge-secondary)]/20"
60
+ className: "text-[var(--color-badge-secondary)] bg-[var(--color-badge-secondary)]/20 dark:text-[var(--color-badge-secondary)] dark:bg-[var(--color-badge-secondary)]/20"
61
61
  },
62
62
  {
63
63
  type: "tertiary",
@@ -72,7 +72,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
72
72
  {
73
73
  type: "tertiary",
74
74
  theme: "light",
75
- className: "border-[var(--color-badge-tertiary)] text-[var(--color-badge-tertiary)] bg-[var(--color-badge-tertiary)]/20 dark:border-[var(--color-badge-tertiary)] dark:text-[var(--color-badge-tertiary)] dark:bg-[var(--color-badge-tertiary)]/20"
75
+ className: "text-[var(--color-badge-tertiary)] bg-[var(--color-badge-tertiary)]/20 dark:text-[var(--color-badge-tertiary)] dark:bg-[var(--color-badge-tertiary)]/20"
76
76
  },
77
77
  {
78
78
  type: "quaternary",
@@ -87,7 +87,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
87
87
  {
88
88
  type: "quaternary",
89
89
  theme: "light",
90
- className: "border-[var(--color-badge-quaternary)] text-[var(--color-badge-quaternary-foreground)] bg-[var(--color-badge-quaternary)]/20 dark:border-[var(--color-badge-quaternary)] dark:text-[var(--color-badge-quaternary-foreground)] dark:bg-[var(--color-badge-quaternary)]/20"
90
+ className: "text-[var(--color-badge-quaternary-foreground)] bg-[var(--color-badge-quaternary)]/20 dark:text-[var(--color-badge-quaternary-foreground)] dark:bg-[var(--color-badge-quaternary)]/20"
91
91
  },
92
92
  {
93
93
  type: "info",
@@ -102,7 +102,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
102
102
  {
103
103
  type: "info",
104
104
  theme: "light",
105
- className: "border-[var(--color-badge-info)] text-[var(--color-badge-info)] bg-[var(--color-badge-info)]/20 dark:border-[var(--color-badge-info)] dark:text-[var(--color-badge-info)] dark:bg-[var(--color-badge-info)]/20"
105
+ className: "text-[var(--color-badge-info)] bg-[var(--color-badge-info)]/20 dark:text-[var(--color-badge-info)] dark:bg-[var(--color-badge-info)]/20"
106
106
  },
107
107
  {
108
108
  type: "warning",
@@ -117,7 +117,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
117
117
  {
118
118
  type: "warning",
119
119
  theme: "light",
120
- className: "border-[var(--color-badge-warning)] text-[var(--color-badge-warning)] bg-[var(--color-badge-warning)]/20 dark:border-[var(--color-badge-warning)] dark:text-[var(--color-badge-warning)] dark:bg-[var(--color-badge-warning)]/20"
120
+ className: "text-[var(--color-badge-warning)] bg-[var(--color-badge-warning)]/20 dark:text-[var(--color-badge-warning)] dark:bg-[var(--color-badge-warning)]/20"
121
121
  },
122
122
  {
123
123
  type: "danger",
@@ -132,7 +132,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
132
132
  {
133
133
  type: "danger",
134
134
  theme: "light",
135
- className: "border-[var(--color-badge-danger)] text-[var(--color-badge-danger)] bg-[var(--color-badge-danger)]/20 dark:border-[var(--color-badge-danger)] dark:text-[var(--color-badge-danger)] dark:bg-[var(--color-badge-danger)]/20"
135
+ className: "text-[var(--color-badge-danger)] bg-[var(--color-badge-danger)]/20 dark:text-[var(--color-badge-danger)] dark:bg-[var(--color-badge-danger)]/20"
136
136
  },
137
137
  {
138
138
  type: "success",
@@ -147,7 +147,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
147
147
  {
148
148
  type: "success",
149
149
  theme: "light",
150
- className: "border-[var(--color-badge-success)] text-[var(--color-badge-success)] bg-[var(--color-badge-success)]/20 dark:border-[var(--color-badge-success)] dark:text-[var(--color-badge-success)] dark:bg-[var(--color-badge-success)]/20"
150
+ className: "text-[var(--color-badge-success)] bg-[var(--color-badge-success)]/20 dark:text-[var(--color-badge-success)] dark:bg-[var(--color-badge-success)]/20"
151
151
  },
152
152
  {
153
153
  type: "muted",
@@ -162,7 +162,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
162
162
  {
163
163
  type: "muted",
164
164
  theme: "light",
165
- className: "border-[var(--color-badge-muted)] text-[var(--color-badge-muted)] bg-[var(--color-badge-muted)]/20 dark:border-[var(--color-badge-muted)] dark:text-[var(--color-badge-muted)] dark:bg-[var(--color-badge-muted)]/20"
165
+ className: "text-[var(--muted-foreground)] bg-[var(--muted-foreground)]/15 dark:text-[var(--color-badge-muted)] dark:bg-[var(--color-badge-muted)]/20"
166
166
  }
167
167
  ],
168
168
  defaultVariants: {
@@ -0,0 +1,2 @@
1
+ export { RichTextContent } from './rich-text-content';
2
+ export type { RichTextContentProps } from './types';
@@ -1,2 +1,2 @@
1
- import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-DXrwZpgq.mjs";
1
+ import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-CJiQQ6TM.mjs";
2
2
  export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, DataTableColumnHeader, DataTableRowActions, createDataTableStore, createSelectionColumn, dataTableFeatures, resolvePath, rowMatchesSearch, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, useNuqsAdapter };
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
- import { t as Badge } from "./badge-BTNW_FEU.mjs";
2
+ import { t as Badge } from "./badge-DOZUr5DV.mjs";
3
3
  import { t as Button } from "./button-DoMqfQLu.mjs";
4
4
  import { t as Checkbox } from "./checkbox-C6P12fio.mjs";
5
5
  import { t as Input } from "./input-j4tbmYYz.mjs";
@@ -11,7 +11,7 @@ import { t as Autocomplete } from "./autocomplete-BxtzndxZ.mjs";
11
11
  import { t as CalendarDatePicker } from "./calendar-date-picker-DPvVAW2I.mjs";
12
12
  import { t as ActionRow } from "./action-row-DTvs91_3.mjs";
13
13
  import { n as useDebouncedSearchInput } from "./use-debounced-search-input-yu_F0QBt.mjs";
14
- import { t as MultiSelect } from "./multi-select-CH8KERkt.mjs";
14
+ import { t as MultiSelect } from "./multi-select-CF0Yq5yf.mjs";
15
15
  import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, Search, X } from "lucide-react";
16
16
  import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
17
17
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,2 +1,2 @@
1
- import { t as GroupedTable } from "../grouped-table-DewB9KoB.mjs";
1
+ import { t as GroupedTable } from "../grouped-table-fHguJz9W.mjs";
2
2
  export { GroupedTable };
@@ -6,7 +6,7 @@ import { t as useControllableState } from "./use-controllable-state-Dna7u3r9.mjs
6
6
  import { Skeleton } from "./skeleton/index.mjs";
7
7
  import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table/index.mjs";
8
8
  import { n as EASE } from "./motion-DvGRWdsm.mjs";
9
- import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-DXrwZpgq.mjs";
9
+ import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-CJiQQ6TM.mjs";
10
10
  import { n as useDebouncedSearchInput } from "./use-debounced-search-input-yu_F0QBt.mjs";
11
11
  import { InputWithAddons } from "./input-with-addons/index.mjs";
12
12
  import { ChevronRight, Search, X } from "lucide-react";
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
2
  import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-SAPwee1h.mjs";
3
3
  import { Avatar, AvatarFallback, AvatarImage } from "./avatar/index.mjs";
4
- import { n as badgeVariants, t as Badge } from "./badge-BTNW_FEU.mjs";
4
+ import { n as badgeVariants, t as Badge } from "./badge-DOZUr5DV.mjs";
5
5
  import { a as BreadcrumbList, i as BreadcrumbLink, n as BreadcrumbEllipsis, o as BreadcrumbPage, r as BreadcrumbItem, s as BreadcrumbSeparator, t as Breadcrumb } from "./breadcrumb-DmdhC8bE.mjs";
6
6
  import { t as SpinnerIcon } from "./spinner.icon-BYUMQ8H-.mjs";
7
7
  import { n as buttonVariants, t as Button } from "./button-DoMqfQLu.mjs";
@@ -55,10 +55,10 @@ import { i as ClientOnly, n as useTheme, r as ThemeScript, t as ThemeProvider }
55
55
  import { a as formatJson, c as isValidYaml, d as CodeEditor, i as CodeEditorTabs, l as jsonToYaml, n as jsonSchema, o as formatYaml, r as yamlSchema, s as isValidJson, t as createCodeEditorSchema, u as yamlToJson } from "./types-agDpVXBG.mjs";
56
56
  import { t as toast } from "./toast-BUPlDkN3.mjs";
57
57
  import { n as Toaster, t as useToast } from "./use-toast-Bpq0yJIT.mjs";
58
- import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-DXrwZpgq.mjs";
58
+ import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-CJiQQ6TM.mjs";
59
59
  import { t as ActionRow } from "./action-row-DTvs91_3.mjs";
60
60
  import { n as useDebouncedSearchInput, t as DEFAULT_SEARCH_DEBOUNCE_MS } from "./use-debounced-search-input-yu_F0QBt.mjs";
61
- import { t as MultiSelect } from "./multi-select-CH8KERkt.mjs";
61
+ import { t as MultiSelect } from "./multi-select-CF0Yq5yf.mjs";
62
62
  import { a as formatRelativeDate, d as parseDate, i as formatCombinedDate, l as getTimestamp, n as DEFAULT_DATE_FORMAT, o as formatTimezoneDate, r as formatAbsoluteDate, s as formatUTCDate, t as DateTime, u as getTimezoneAbbreviation } from "./date-time-lZdefM4H.mjs";
63
63
  import { t as DateTimePicker } from "./date-time-picker-CricsXgF.mjs";
64
64
  import { a as DropdownMenuItem, c as DropdownMenuRadioGroup, d as DropdownMenuShortcut, f as DropdownMenuSub, h as DropdownMenuTrigger, i as DropdownMenuGroup, l as DropdownMenuRadioItem, m as DropdownMenuSubTrigger, n as DropdownMenuCheckboxItem, o as DropdownMenuLabel, p as DropdownMenuSubContent, r as DropdownMenuContent, s as DropdownMenuPortal, t as DropdownMenu, u as DropdownMenuSeparator } from "./dropdown-Xvduih96.mjs";
@@ -71,17 +71,18 @@ import { InputWithAddons } from "./input-with-addons/index.mjs";
71
71
  import { t as TimePicker } from "./time-picker-DQlc2gKW.mjs";
72
72
  import { t as Transfer } from "./transfer-DsshYgIk.mjs";
73
73
  import { a as getResponsiveValue, c as GRID_COLUMNS, d as RESPONSIVE_MAP, i as getGutter, l as GRID_PREFIX, n as Row, o as registerMediaQuery, r as RowContext, s as GRID_BREAKPOINTS, t as Col, u as RESPONSIVE_ARRAY } from "./col-BYGWTB4j.mjs";
74
- import { t as GroupedTable } from "./grouped-table-DewB9KoB.mjs";
74
+ import { t as GroupedTable } from "./grouped-table-fHguJz9W.mjs";
75
75
  import { t as InputNumber } from "./input-number-TXmW-X9o.mjs";
76
76
  import { a as logoStyles, i as LogoFlat, n as LogoStacked, r as LogoIcon, t as LogoText } from "./logo-text-Dpm5O6Kg.mjs";
77
77
  import { t as Logo } from "./logo-C3FxD7jb.mjs";
78
78
  import { t as MoreActions } from "./more-actions-C3DYWO0a.mjs";
79
79
  import { configureProgress, startProgress, stopProgress } from "./nprogress/index.mjs";
80
80
  import { t as PageTitle } from "./page-title-ipRWEOeM.mjs";
81
- import { n as useRichTextEditor, r as RichTextContent, t as RichTextEditor } from "./rich-text-editor-DVga5upM.mjs";
81
+ import { t as RichTextContent } from "./rich-text-content-DNonBD39.mjs";
82
+ import { n as useRichTextEditor, t as RichTextEditor } from "./rich-text-editor-DP5JnAoO.mjs";
82
83
  import { t as defineStepper } from "./stepper-dNvkipop.mjs";
83
- import { t as TagsInput } from "./tag-input-Ctxtcpwy.mjs";
84
- import { _ as RedisTaskStorage, a as TaskPanelItem, b as createProjectMetadata, c as TaskPanelHeader, d as useCurrentScope, f as useTasksWithLabels, g as detectStorage, h as TaskQueue, i as TaskPanel, l as getContextLabel, m as TaskQueueProvider, n as TaskSummaryDialog, o as TaskPanelCounter, p as useTaskQueue, r as TaskQueueTrigger, s as TaskPanelActions, t as TaskQueueDropdown, u as matchesCurrentScope, v as LocalTaskStorage, x as createUserMetadata, y as createOrgMetadata } from "./task-queue-dropdown-B7FCpfqr.mjs";
84
+ import { t as TagsInput } from "./tag-input-c9OvWTSe.mjs";
85
+ import { _ as RedisTaskStorage, a as TaskPanelItem, b as createProjectMetadata, c as TaskPanelHeader, d as useCurrentScope, f as useTasksWithLabels, g as detectStorage, h as TaskQueue, i as TaskPanel, l as getContextLabel, m as TaskQueueProvider, n as TaskSummaryDialog, o as TaskPanelCounter, p as useTaskQueue, r as TaskQueueTrigger, s as TaskPanelActions, t as TaskQueueDropdown, u as matchesCurrentScope, v as LocalTaskStorage, x as createUserMetadata, y as createOrgMetadata } from "./task-queue-dropdown-CHPP5WIm.mjs";
85
86
  import { _ as getShortTimezoneDisplay, a as TimeRangePicker, b as utcToLocalInputString, c as getPresetByKey, d as TimezoneSelector, f as QuickRangesPanel, g as getDefaultTimezoneOptions, h as formatUtcForDisplay, i as formatTimeRangeDisplay, l as getPresetByShortcut, m as createTimezoneOption, n as formatDateForInput, o as DEFAULT_PRESETS, p as CustomRangePanel, r as formatSingleTimeDisplay, s as getDefaultPreset, t as toApiTimeRange, u as getPresetRange, v as localInputStringToUtc, x as zonedDateToUtcString, y as utcStringToZonedDate } from "./to-api-format-QuJ81vQ9.mjs";
86
87
  import { t as useDebounce } from "./hooks-CwZ6hyI1.mjs";
87
88
  import { n as toUTCTimestampStartOfDay, t as toUTCTimestampEndOfDay } from "./utils-CA4QEIJ8.mjs";
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "../utils-Bu32wN-x.mjs";
2
- import { t as Badge } from "../badge-BTNW_FEU.mjs";
2
+ import { t as Badge } from "../badge-DOZUr5DV.mjs";
3
3
  import { t as Button } from "../button-DoMqfQLu.mjs";
4
4
  import { t as Icon } from "../icon-wrapper-DKfJlJd0.mjs";
5
5
  import { t as Checkbox } from "../checkbox-C6P12fio.mjs";
@@ -1,2 +1,2 @@
1
- import { t as MultiSelect } from "../multi-select-CH8KERkt.mjs";
1
+ import { t as MultiSelect } from "../multi-select-CF0Yq5yf.mjs";
2
2
  export { MultiSelect };
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
- import { t as Badge } from "./badge-BTNW_FEU.mjs";
2
+ import { t as Badge } from "./badge-DOZUr5DV.mjs";
3
3
  import { t as Separator } from "./separator-vbPD_WFQ.mjs";
4
4
  import { t as useControllableState } from "./use-controllable-state-Dna7u3r9.mjs";
5
5
  import { t as ResponsivePopover } from "./responsive-popover-8VcjBM2d.mjs";
@@ -0,0 +1,2 @@
1
+ import { t as RichTextContent } from "../rich-text-content-DNonBD39.mjs";
2
+ export { RichTextContent };
@@ -0,0 +1,63 @@
1
+ import { t as cn } from "./utils-Bu32wN-x.mjs";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import DOMPurify from "isomorphic-dompurify";
4
+ //#region src/components/features/rich-text-editor/rich-text-content.tsx
5
+ const SANITIZE_CONFIG = {
6
+ ALLOWED_TAGS: [
7
+ "strong",
8
+ "em",
9
+ "u",
10
+ "s",
11
+ "a",
12
+ "p",
13
+ "br"
14
+ ],
15
+ ALLOWED_ATTR: [
16
+ "href",
17
+ "target",
18
+ "rel"
19
+ ]
20
+ };
21
+ /**
22
+ * A bare domain (e.g. `example.com`) has no scheme and isn't a relative or
23
+ * fragment reference, so it needs an https:// prefix to resolve correctly.
24
+ * Anything already scheme-qualified (mailto:, tel:, http(s):, protocol-relative
25
+ * //) or relative (/path, #anchor, ?query, ./, ../) must be left untouched.
26
+ */
27
+ function needsHttpsPrefix(href) {
28
+ if (!href) return false;
29
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return false;
30
+ if (/^[/#?.]/.test(href)) return false;
31
+ return true;
32
+ }
33
+ /**
34
+ * Post-sanitization: normalizes link hrefs and hardens external links.
35
+ * Applied AFTER DOMPurify so we only touch already-clean HTML.
36
+ *
37
+ * Only bare domains get an https:// prefix, and only http(s) links get
38
+ * target/rel hardening — relative, fragment, tel:, and mailto: links are
39
+ * preserved as-authored so they keep working.
40
+ */
41
+ function normalizeLinks(html) {
42
+ return html.replace(/<a\s([^>]*)>/gi, (_match, attrs) => {
43
+ const href = attrs.match(/href="([^"]*)"/i)?.[1] ?? "";
44
+ const prefixed = needsHttpsPrefix(href);
45
+ let normalized = attrs;
46
+ if (prefixed) normalized = normalized.replace(/href="[^"]*"/i, `href="https://${href}"`);
47
+ if (prefixed || /^https?:\/\//i.test(href)) {
48
+ if (!/\btarget=/i.test(normalized)) normalized += " target=\"_blank\"";
49
+ if (!/\brel=/i.test(normalized)) normalized += " rel=\"noopener noreferrer\"";
50
+ }
51
+ return `<a ${normalized}>`;
52
+ });
53
+ }
54
+ function RichTextContent({ content, className }) {
55
+ if (!content) return null;
56
+ const withSafeLinks = normalizeLinks(DOMPurify.sanitize(content, SANITIZE_CONFIG));
57
+ return /* @__PURE__ */ jsx("div", {
58
+ className: cn("prose prose-sm dark:prose-invert max-w-none", "[&_a]:text-primary text-sm [&_a]:underline [&_p]:my-1 [&_p:empty]:min-h-[1em]", className),
59
+ dangerouslySetInnerHTML: { __html: withSafeLinks }
60
+ });
61
+ }
62
+ //#endregion
63
+ export { RichTextContent as t };
@@ -1,2 +1,3 @@
1
- import { n as useRichTextEditor, r as RichTextContent, t as RichTextEditor } from "../rich-text-editor-DVga5upM.mjs";
1
+ import { t as RichTextContent } from "../rich-text-content-DNonBD39.mjs";
2
+ import { n as useRichTextEditor, t as RichTextEditor } from "../rich-text-editor-DP5JnAoO.mjs";
2
3
  export { RichTextContent, RichTextEditor, useRichTextEditor };
@@ -7,72 +7,12 @@ import { t as Tooltip } from "./tooltip-DhbN1BDK.mjs";
7
7
  import { BoldIcon, ItalicIcon, LinkIcon, StrikethroughIcon, UnderlineIcon, UnlinkIcon } from "lucide-react";
8
8
  import { createContext, use, useCallback, useEffect, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
- import DOMPurify from "isomorphic-dompurify";
11
10
  import CharacterCount from "@tiptap/extension-character-count";
12
11
  import Link from "@tiptap/extension-link";
13
12
  import Placeholder from "@tiptap/extension-placeholder";
14
13
  import Underline from "@tiptap/extension-underline";
15
14
  import { EditorContent, useEditor } from "@tiptap/react";
16
15
  import StarterKit from "@tiptap/starter-kit";
17
- //#region src/components/features/rich-text-editor/rich-text-content.tsx
18
- const SANITIZE_CONFIG = {
19
- ALLOWED_TAGS: [
20
- "strong",
21
- "em",
22
- "u",
23
- "s",
24
- "a",
25
- "p",
26
- "br"
27
- ],
28
- ALLOWED_ATTR: [
29
- "href",
30
- "target",
31
- "rel"
32
- ]
33
- };
34
- /**
35
- * A bare domain (e.g. `example.com`) has no scheme and isn't a relative or
36
- * fragment reference, so it needs an https:// prefix to resolve correctly.
37
- * Anything already scheme-qualified (mailto:, tel:, http(s):, protocol-relative
38
- * //) or relative (/path, #anchor, ?query, ./, ../) must be left untouched.
39
- */
40
- function needsHttpsPrefix(href) {
41
- if (!href) return false;
42
- if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return false;
43
- if (/^[/#?.]/.test(href)) return false;
44
- return true;
45
- }
46
- /**
47
- * Post-sanitization: normalizes link hrefs and hardens external links.
48
- * Applied AFTER DOMPurify so we only touch already-clean HTML.
49
- *
50
- * Only bare domains get an https:// prefix, and only http(s) links get
51
- * target/rel hardening — relative, fragment, tel:, and mailto: links are
52
- * preserved as-authored so they keep working.
53
- */
54
- function normalizeLinks(html) {
55
- return html.replace(/<a\s([^>]*)>/gi, (_match, attrs) => {
56
- const href = attrs.match(/href="([^"]*)"/i)?.[1] ?? "";
57
- const prefixed = needsHttpsPrefix(href);
58
- let normalized = attrs;
59
- if (prefixed) normalized = normalized.replace(/href="[^"]*"/i, `href="https://${href}"`);
60
- if (prefixed || /^https?:\/\//i.test(href)) {
61
- if (!/\btarget=/i.test(normalized)) normalized += " target=\"_blank\"";
62
- if (!/\brel=/i.test(normalized)) normalized += " rel=\"noopener noreferrer\"";
63
- }
64
- return `<a ${normalized}>`;
65
- });
66
- }
67
- function RichTextContent({ content, className }) {
68
- if (!content) return null;
69
- const withSafeLinks = normalizeLinks(DOMPurify.sanitize(content, SANITIZE_CONFIG));
70
- return /* @__PURE__ */ jsx("div", {
71
- className: cn("prose prose-sm dark:prose-invert max-w-none", "[&_a]:text-primary text-sm [&_a]:underline [&_p]:my-1 [&_p:empty]:min-h-[1em]", className),
72
- dangerouslySetInnerHTML: { __html: withSafeLinks }
73
- });
74
- }
75
- //#endregion
76
16
  //#region src/components/features/rich-text-editor/toolbar/toolbar-button.tsx
77
17
  function ToolbarButton({ ref, active, disabled, onClick, tooltip, children, className }) {
78
18
  const button = /* @__PURE__ */ jsx("button", {
@@ -331,4 +271,4 @@ const RichTextEditor = Object.assign(RichTextEditorRoot, {
331
271
  Separator: ToolbarSeparator
332
272
  });
333
273
  //#endregion
334
- export { useRichTextEditor as n, RichTextContent as r, RichTextEditor as t };
274
+ export { useRichTextEditor as n, RichTextEditor as t };
@@ -1,2 +1,2 @@
1
- import { t as TagsInput } from "../tag-input-Ctxtcpwy.mjs";
1
+ import { t as TagsInput } from "../tag-input-c9OvWTSe.mjs";
2
2
  export { TagsInput };
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
- import { t as Badge } from "./badge-BTNW_FEU.mjs";
2
+ import { t as Badge } from "./badge-DOZUr5DV.mjs";
3
3
  import { t as Icon } from "./icon-wrapper-DKfJlJd0.mjs";
4
4
  import { t as Input } from "./input-j4tbmYYz.mjs";
5
5
  import { X } from "lucide-react";
@@ -1,2 +1,2 @@
1
- import { _ as RedisTaskStorage, a as TaskPanelItem, b as createProjectMetadata, c as TaskPanelHeader, d as useCurrentScope, f as useTasksWithLabels, g as detectStorage, h as TaskQueue, i as TaskPanel, l as getContextLabel, m as TaskQueueProvider, n as TaskSummaryDialog, o as TaskPanelCounter, p as useTaskQueue, r as TaskQueueTrigger, s as TaskPanelActions, t as TaskQueueDropdown, u as matchesCurrentScope, v as LocalTaskStorage, x as createUserMetadata, y as createOrgMetadata } from "../task-queue-dropdown-B7FCpfqr.mjs";
1
+ import { _ as RedisTaskStorage, a as TaskPanelItem, b as createProjectMetadata, c as TaskPanelHeader, d as useCurrentScope, f as useTasksWithLabels, g as detectStorage, h as TaskQueue, i as TaskPanel, l as getContextLabel, m as TaskQueueProvider, n as TaskSummaryDialog, o as TaskPanelCounter, p as useTaskQueue, r as TaskQueueTrigger, s as TaskPanelActions, t as TaskQueueDropdown, u as matchesCurrentScope, v as LocalTaskStorage, x as createUserMetadata, y as createOrgMetadata } from "../task-queue-dropdown-CHPP5WIm.mjs";
2
2
  export { LocalTaskStorage, RedisTaskStorage, TaskPanel, TaskPanelActions, TaskPanelCounter, TaskPanelHeader, TaskPanelItem, TaskQueue, TaskQueueDropdown, TaskQueueProvider, TaskQueueTrigger, TaskSummaryDialog, createOrgMetadata, createProjectMetadata, createUserMetadata, detectStorage, getContextLabel, matchesCurrentScope, useCurrentScope, useTaskQueue, useTasksWithLabels };
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
- import { t as Badge } from "./badge-BTNW_FEU.mjs";
2
+ import { t as Badge } from "./badge-DOZUr5DV.mjs";
3
3
  import { t as SpinnerIcon } from "./spinner.icon-BYUMQ8H-.mjs";
4
4
  import { t as Button } from "./button-DoMqfQLu.mjs";
5
5
  import { t as Icon } from "./icon-wrapper-DKfJlJd0.mjs";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datum-cloud/datum-ui",
3
3
  "type": "module",
4
- "version": "3.4.0",
4
+ "version": "3.5.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "url": "https://github.com/datum-cloud/datum-ui"
@@ -23,6 +23,9 @@
23
23
  "style": "./dist/styles/canela.css",
24
24
  "default": "./dist/styles/canela.css"
25
25
  },
26
+ "./fonts/*": {
27
+ "default": "./dist/styles/fonts/*"
28
+ },
26
29
  "./theme": {
27
30
  "source": "./src/components/themes/index.ts",
28
31
  "types": "./dist/components/themes/index.d.ts",
@@ -163,6 +166,11 @@
163
166
  "types": "./dist/components/features/rich-text-editor/index.d.ts",
164
167
  "default": "./dist/rich-text-editor/index.mjs"
165
168
  },
169
+ "./rich-text-content": {
170
+ "source": "./src/components/features/rich-text-editor/content.ts",
171
+ "types": "./dist/components/features/rich-text-editor/content.d.ts",
172
+ "default": "./dist/rich-text-content/index.mjs"
173
+ },
166
174
  "./select": {
167
175
  "source": "./src/components/base/select/index.ts",
168
176
  "types": "./dist/components/base/select/index.d.ts",