@datum-cloud/datum-ui 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/autocomplete/index.mjs +1 -1
  2. package/dist/{autocomplete-D0zAcT78.mjs → autocomplete-CrCbO2VS.mjs} +10 -4
  3. package/dist/autosearch/index.mjs +1 -1
  4. package/dist/button-group/index.mjs +1 -1
  5. package/dist/{button-group-C3GKo-0p.mjs → button-group-DJ6X1SSj.mjs} +2 -2
  6. package/dist/{calendar-date-picker-CknZM6a2.mjs → calendar-date-picker-BRKs56gR.mjs} +1 -1
  7. package/dist/components/features/logs/hooks/use-logs-url-state.d.ts +59 -0
  8. package/dist/components/features/logs/index.d.ts +4 -1
  9. package/dist/components/features/logs/utils/facet-counts.d.ts +50 -0
  10. package/dist/components/features/logs/utils/facets.d.ts +2 -0
  11. package/dist/components/features/logs/utils/index.d.ts +4 -0
  12. package/dist/components/features/logs/utils/url-state.d.ts +61 -0
  13. package/dist/data-table/index.mjs +1 -1
  14. package/dist/{data-table-CpBwO8Ea.mjs → data-table-DFUnUydN.mjs} +5 -5
  15. package/dist/date-picker/index.mjs +3 -3
  16. package/dist/date-time-picker/index.mjs +1 -1
  17. package/dist/{date-time-picker-Bc41Ixrs.mjs → date-time-picker-BRMm2QSz.mjs} +1 -1
  18. package/dist/{date-time-range-picker-yy1Acntn.mjs → date-time-range-picker-CqGGJTLS.mjs} +18 -7
  19. package/dist/field-styles-D-FJatHt.mjs +31 -0
  20. package/dist/form/index.mjs +1 -1
  21. package/dist/{form-DTaVgDSV.mjs → form-VdbSqDjL.mjs} +5 -5
  22. package/dist/grouped-table/index.mjs +1 -1
  23. package/dist/{grouped-table-Dl6DclbV.mjs → grouped-table-B4HzLXq6.mjs} +1 -1
  24. package/dist/hooks/index.mjs +1 -1
  25. package/dist/hooks/use-breakpoint.d.ts +7 -2
  26. package/dist/index.mjs +19 -19
  27. package/dist/{layers-DLUbkJtt.mjs → layers-BC1Ny49w.mjs} +1 -1
  28. package/dist/logs/index.mjs +374 -80
  29. package/dist/map/index.mjs +1 -1
  30. package/dist/more-actions/index.mjs +1 -1
  31. package/dist/{more-actions-BlSOVwa8.mjs → more-actions-DXQiV-bN.mjs} +1 -1
  32. package/dist/multi-select/index.mjs +1 -1
  33. package/dist/{multi-select-C2cBrqXl.mjs → multi-select-DwcsJdHt.mjs} +32 -24
  34. package/dist/picker/index.mjs +2 -2
  35. package/dist/{picker-DC3GErYg.mjs → picker-BHPtncpv.mjs} +2 -2
  36. package/dist/responsive-dropdown/index.mjs +1 -1
  37. package/dist/{responsive-dropdown-DsWgpE_i.mjs → responsive-dropdown-B9cDUeHt.mjs} +1 -1
  38. package/dist/responsive-popover/index.mjs +1 -1
  39. package/dist/{responsive-popover-DJYd8lOK.mjs → responsive-popover-B_pUxrZ1.mjs} +1 -1
  40. package/dist/rich-text-editor/index.mjs +1 -1
  41. package/dist/{rich-text-editor-BUz06s-b.mjs → rich-text-editor-CPd_6vdZ.mjs} +1 -1
  42. package/dist/select/index.mjs +1 -1
  43. package/dist/{select-DemYhwnG.mjs → select-B0Xlz-wD.mjs} +3 -2
  44. package/dist/{select-jgK6wyXj.mjs → select-CGsHf70u.mjs} +7 -2
  45. package/dist/task-queue/index.mjs +1 -1
  46. package/dist/{task-queue-dropdown-DuCAAybs.mjs → task-queue-dropdown-BflEFiMi.mjs} +1 -1
  47. package/dist/time-picker/index.mjs +1 -1
  48. package/dist/{time-picker-B_am4eAK.mjs → time-picker-BFwH-Vd3.mjs} +1 -1
  49. package/dist/{to-api-format-DyImCzCn.mjs → to-api-format-BiWOTFha.mjs} +3 -3
  50. package/dist/{use-breakpoint-zY_JYINK.mjs → use-breakpoint-Ce5tS4cU.mjs} +11 -5
  51. package/dist/utils/field-styles.d.ts +21 -0
  52. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import { t as SpinnerIcon } from "./spinner.icon-BiUnMAxw.mjs";
7
7
  import { n as buttonVariants, t as Button } from "./button-Cn4KagBO.mjs";
8
8
  import { t as LinkButton } from "./button-IK-Lnu_9.mjs";
9
9
  import { t as Separator } from "./separator-DNiiN2BN.mjs";
10
- import { i as buttonGroupVariants, n as ButtonGroupSeparator, r as ButtonGroupText, t as ButtonGroup } from "./button-group-C3GKo-0p.mjs";
10
+ import { i as buttonGroupVariants, n as ButtonGroupSeparator, r as ButtonGroupText, t as ButtonGroup } from "./button-group-DJ6X1SSj.mjs";
11
11
  import { t as Icon } from "./icon-wrapper-CIk2th6R.mjs";
12
12
  import { Calendar, CalendarDayButton } from "./calendar/index.mjs";
13
13
  import { a as CardField, c as CardFooter, d as CardTitle, i as CardContent, l as CardHeader, n as Card, o as CardFieldLabel, r as CardAction, s as CardFieldValue, t as CardSaveBar, u as CardDescription } from "./card-save-bar-Te8jf-ie.mjs";
@@ -23,16 +23,16 @@ import { n as HoverCardContent, r as HoverCardTrigger, t as HoverCard } from "./
23
23
  import { t as Input } from "./input-DsT5qkJn.mjs";
24
24
  import { a as InputGroupText, i as InputGroupInput, n as InputGroupAddon, o as InputGroupTextarea, r as InputGroupButton, t as InputGroup } from "./input-group-CCzvJIZM.mjs";
25
25
  import { t as Label } from "./label-BR1V3Imy.mjs";
26
- import { A as useLeaflet, C as MapMarkerClusterGroup, D as MapRectangle, E as MapPopup, O as MapTooltip, S as MapMarker, T as MapPolyline, _ as MapLocateControl, a as MapTileLayer, b as MapCircle, c as MapDrawDelete, d as MapDrawPolygon, f as MapDrawPolyline, g as MapFullscreenControl, h as MapControlContainer, i as MapLayersControl, j as PlaceAutocomplete, k as Map, l as MapDrawEdit, m as MapDrawUndo, n as MapLayerGroup, o as MapDrawCircle, p as MapDrawRectangle, r as MapLayers, s as MapDrawControl, t as MapFeatureGroup, u as MapDrawMarker, v as MapSearchControl, w as MapPolygon, x as MapCircleMarker, y as MapZoomControl } from "./layers-DLUbkJtt.mjs";
26
+ import { A as useLeaflet, C as MapMarkerClusterGroup, D as MapRectangle, E as MapPopup, O as MapTooltip, S as MapMarker, T as MapPolyline, _ as MapLocateControl, a as MapTileLayer, b as MapCircle, c as MapDrawDelete, d as MapDrawPolygon, f as MapDrawPolyline, g as MapFullscreenControl, h as MapControlContainer, i as MapLayersControl, j as PlaceAutocomplete, k as Map, l as MapDrawEdit, m as MapDrawUndo, n as MapLayerGroup, o as MapDrawCircle, p as MapDrawRectangle, r as MapLayers, s as MapDrawControl, t as MapFeatureGroup, u as MapDrawMarker, v as MapSearchControl, w as MapPolygon, x as MapCircleMarker, y as MapZoomControl } from "./layers-BC1Ny49w.mjs";
27
27
  import { i as useInSheet, n as MobileSheet, r as InSheetContext, t as ResponsiveSheetTrigger } from "./mobile-sheet-1995HYY_.mjs";
28
28
  import { a as SheetHeader, i as SheetFooter, o as SheetTitle, r as SheetDescription, s as SheetTrigger } from "./sheet-DYPtVIb2.mjs";
29
29
  import { i as SheetOverlay, n as SheetClose, r as SheetContent, t as Sheet } from "./sheet-DEpEJ_Rk.mjs";
30
30
  import { i as PopoverTrigger, n as PopoverAnchor, r as PopoverContent, t as Popover } from "./popover-7YxyP_W0.mjs";
31
31
  import { n as RadioGroupItem, t as RadioGroup } from "./radio-group-Boxi2hm0.mjs";
32
- import { t as useBreakpoint } from "./use-breakpoint-zY_JYINK.mjs";
33
- import { t as ResponsiveDropdown } from "./responsive-dropdown-DsWgpE_i.mjs";
34
- import { t as ResponsivePopover } from "./responsive-popover-DJYd8lOK.mjs";
35
- import { a as SelectLabel, c as SelectSeparator, i as SelectItem, l as SelectTrigger, n as SelectContent, o as SelectScrollDownButton, r as SelectGroup, s as SelectScrollUpButton, t as Select, u as SelectValue } from "./select-DemYhwnG.mjs";
32
+ import { t as useBreakpoint } from "./use-breakpoint-Ce5tS4cU.mjs";
33
+ import { t as ResponsiveDropdown } from "./responsive-dropdown-B9cDUeHt.mjs";
34
+ import { t as ResponsivePopover } from "./responsive-popover-B_pUxrZ1.mjs";
35
+ import { a as SelectLabel, c as SelectSeparator, i as SelectItem, l as SelectTrigger, n as SelectContent, o as SelectScrollDownButton, r as SelectGroup, s as SelectScrollUpButton, t as Select, u as SelectValue } from "./select-B0Xlz-wD.mjs";
36
36
  import { n as SettingsNavItem, r as SettingsNavLabel, t as SettingsNav } from "./settings-nav-CCZdPD-x.mjs";
37
37
  import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "./sidebar-DWnxQWQ8.mjs";
38
38
  import { t as Tooltip } from "./tooltip-OIySjJWW.mjs";
@@ -46,44 +46,44 @@ import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragrap
46
46
  import { t as VisuallyHidden } from "./visuallyhidden-Mg4OsZ-l.mjs";
47
47
  import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "./app-navigation-DyNZwlSG.mjs";
48
48
  import { t as LoaderOverlay } from "./loader-overlay-DP0pvHuG.mjs";
49
- import { t as Autocomplete } from "./autocomplete-D0zAcT78.mjs";
49
+ import { t as Autocomplete } from "./autocomplete-CrCbO2VS.mjs";
50
50
  import { Autosearch, defaultAutosearchValue } from "./autosearch/index.mjs";
51
51
  import { n as avatarStackVariants, t as AvatarStack } from "./avatar-stack-2ow672bR.mjs";
52
- import { M as getBrowserTimezone, N as getTimezoneOffset, j as formatTimezoneLabel } from "./date-time-range-picker-yy1Acntn.mjs";
53
- import { t as CalendarDatePicker } from "./calendar-date-picker-CknZM6a2.mjs";
52
+ import { M as getBrowserTimezone, N as getTimezoneOffset, j as formatTimezoneLabel } from "./date-time-range-picker-CqGGJTLS.mjs";
53
+ import { t as CalendarDatePicker } from "./calendar-date-picker-BRKs56gR.mjs";
54
54
  import { i as ClientOnly, n as useTheme, r as ThemeScript, t as ThemeProvider } from "./themes-BoxnQbbu.mjs";
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-ChINrR8H.mjs";
56
56
  import { t as toast } from "./toast-CizxCEcK.mjs";
57
57
  import { n as Toaster, t as useToast } from "./use-toast-CZAZeS95.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-CpBwO8Ea.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-DFUnUydN.mjs";
59
59
  import { t as ActionRow } from "./action-row-BEzE9x37.mjs";
60
60
  import { n as useDebouncedSearchInput, t as DEFAULT_SEARCH_DEBOUNCE_MS } from "./use-debounced-search-input-B4u28iLx.mjs";
61
- import { t as MultiSelect } from "./multi-select-C2cBrqXl.mjs";
61
+ import { t as MultiSelect } from "./multi-select-DwcsJdHt.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-BBRVM9qb.mjs";
63
- import { t as DateTimePicker } from "./date-time-picker-Bc41Ixrs.mjs";
63
+ import { t as DateTimePicker } from "./date-time-picker-BRMm2QSz.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-DR9j3Ni_.mjs";
65
65
  import { i as FileInputButton, n as DropzoneContent, r as DropzoneEmptyState, t as Dropzone } from "./dropzone-qKYfSe7o.mjs";
66
66
  import { t as EmptyContent } from "./empty-content-CTMHak36.mjs";
67
- import { A as FormCheckbox, C as FormDialog, D as FormCustom, E as FormDatePicker, M as FormAutosearch, N as FormAutocomplete, O as FormCopyBox, S as FormError, T as FormDateTimePicker, _ as FormRadioGroup, a as useField, b as FormFieldArray, c as useWatchAll, d as FormTextarea, f as FormSwitch, g as FormRoot, h as FormSelectItem, i as useFieldContext, j as FormButton, k as FormCombobox, l as FormTransfer, m as FormSelect, n as useFormState, o as FormWhen, p as FormSubmit, r as useFormContext, s as useWatch, t as Form, u as FormTimePicker, v as FormRadioItem, w as FormDescription, x as FormField, y as FormInput } from "./form-DTaVgDSV.mjs";
67
+ import { A as FormCheckbox, C as FormDialog, D as FormCustom, E as FormDatePicker, M as FormAutosearch, N as FormAutocomplete, O as FormCopyBox, S as FormError, T as FormDateTimePicker, _ as FormRadioGroup, a as useField, b as FormFieldArray, c as useWatchAll, d as FormTextarea, f as FormSwitch, g as FormRoot, h as FormSelectItem, i as useFieldContext, j as FormButton, k as FormCombobox, l as FormTransfer, m as FormSelect, n as useFormState, o as FormWhen, p as FormSubmit, r as useFormContext, s as useWatch, t as Form, u as FormTimePicker, v as FormRadioItem, w as FormDescription, x as FormField, y as FormInput } from "./form-VdbSqDjL.mjs";
68
68
  import { t as useCopyToClipboard } from "./use-copy-to-clipboard-CWKWHLxI.mjs";
69
69
  import { n as useAdapter, t as FormAdapterProvider } from "./adapter-context-XT749tyT.mjs";
70
70
  import { InputWithAddons } from "./input-with-addons/index.mjs";
71
- import { t as TimePicker } from "./time-picker-B_am4eAK.mjs";
71
+ import { t as TimePicker } from "./time-picker-BFwH-Vd3.mjs";
72
72
  import { t as Transfer } from "./transfer-DGUeAQUL.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-CYHIfPJq.mjs";
74
- import { t as GroupedTable } from "./grouped-table-Dl6DclbV.mjs";
74
+ import { t as GroupedTable } from "./grouped-table-B4HzLXq6.mjs";
75
75
  import { t as InputNumber } from "./input-number-DF-bfIo8.mjs";
76
76
  import { a as logoStyles, i as LogoFlat, n as LogoStacked, r as LogoIcon, t as LogoText } from "./logo-text-B54YiEte.mjs";
77
77
  import { t as Logo } from "./logo-CkzKra6H.mjs";
78
- import { t as MoreActions } from "./more-actions-BlSOVwa8.mjs";
78
+ import { t as MoreActions } from "./more-actions-DXQiV-bN.mjs";
79
79
  import { configureProgress, startProgress, stopProgress } from "./nprogress/index.mjs";
80
80
  import { t as PageTitle } from "./page-title-Djl3LvPT.mjs";
81
81
  import { t as RichTextContent } from "./rich-text-content-BiIIowBU.mjs";
82
- import { n as useRichTextEditor, t as RichTextEditor } from "./rich-text-editor-BUz06s-b.mjs";
82
+ import { n as useRichTextEditor, t as RichTextEditor } from "./rich-text-editor-CPd_6vdZ.mjs";
83
83
  import { t as defineStepper } from "./stepper-ZipxpzWY.mjs";
84
84
  import { t as TagsInput } from "./tag-input-a3ktkGrH.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-DuCAAybs.mjs";
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-DyImCzCn.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-BflEFiMi.mjs";
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-BiWOTFha.mjs";
87
87
  import { t as useDebounce } from "./hooks-WVJbyhCz.mjs";
88
88
  import { n as toUTCTimestampStartOfDay, t as toUTCTimestampEndOfDay } from "./utils-CPJ5MCfd.mjs";
89
89
  export { CustomRangePanel as AbsoluteRangePanel, CustomRangePanel, ActionRow, Alert, AlertDescription, AlertTitle, AppNavigation, Autocomplete, Autosearch, Avatar, AvatarFallback, AvatarImage, AvatarStack, Badge, Blockquote, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, CalendarDatePicker, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardField, CardFieldLabel, CardFieldValue, CardFooter, CardHeader, CardSaveBar, CardTitle, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, ClientOnly, CloseIcon, Code, CodeEditor, CodeEditorTabs, Col, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, DEFAULT_DATE_FORMAT, DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DEFAULT_PRESETS, DEFAULT_SEARCH_DEBOUNCE_MS, DURATION, DataTable, DataTableColumnHeader, DataTableRowActions, DateTime, DateTimePicker, Dialog, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneContent, DropzoneEmptyState, EASE, EmptyContent, FileInputButton, Form, FormAdapterProvider, FormAutocomplete, FormAutosearch, FormButton, FormCheckbox, FormCombobox, FormCopyBox, FormCustom, FormDatePicker, FormDateTimePicker, FormDescription, FormDialog, FormError, FormField, FormFieldArray, FormInput, FormRadioGroup, FormRadioItem, FormRoot, FormSelect, FormSelectItem, FormSubmit, FormSwitch, FormTextarea, FormTimePicker, FormTransfer, FormWhen, GRID_BREAKPOINTS, GRID_COLUMNS, GRID_PREFIX, GroupedTable, HoverCard, HoverCardContent, HoverCardTrigger, Icon, InSheetContext, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumber, InputWithAddons, Label, Link, LinkButton, List, ListItem, LoaderOverlay, LocalTaskStorage, Logo, LogoFlat, LogoIcon, LogoStacked, LogoText, Map, MapCircle, MapCircleMarker, MapControlContainer, MapDrawCircle, MapDrawControl, MapDrawDelete, MapDrawEdit, MapDrawMarker, MapDrawPolygon, MapDrawPolyline, MapDrawRectangle, MapDrawUndo, MapFeatureGroup, MapFullscreenControl, MapLayerGroup, MapLayers, MapLayersControl, MapLocateControl, MapMarker, MapMarkerClusterGroup, MapPolygon, MapPolyline, MapPopup, MapRectangle, MapSearchControl, MapTileLayer, MapTooltip, MapZoomControl, MobileSheet, MoreActions, MultiSelect, NavMenu, NavMenuItem, PageTitle, Paragraph, PlaceAutocomplete, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, QuickRangesPanel, RESPONSIVE_ARRAY, RESPONSIVE_MAP, RadioGroup, RadioGroupItem, RedisTaskStorage, ResponsiveDropdown, ResponsivePopover, ResponsiveSheetTrigger, RichTextContent, RichTextEditor, Row, RowContext, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SettingsNav, SettingsNavItem, SettingsNavLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Spinner, SpinnerIcon, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsLinkTrigger, TabsList, TabsTrigger, TagsInput, TaskPanel, TaskPanelActions, TaskPanelCounter, TaskPanelHeader, TaskPanelItem, TaskQueue, TaskQueueDropdown, TaskQueueProvider, TaskQueueTrigger, TaskSummaryDialog, Text, Textarea, ThemeProvider, ThemeScript, TimePicker, TimeRangePicker, TimezoneSelector, Title, Toaster, Tooltip, Transfer, VisuallyHidden, avatarStackVariants, badgeVariants, buttonGroupVariants, buttonVariants, cn, configureProgress, createCodeEditorSchema, createDataTableStore, createOrgMetadata, createProjectMetadata, createSelectionColumn, createTimezoneOption, createUserMetadata, dataTableFeatures, defaultAutosearchValue, defineStepper, detectStorage, findItemByHref, formatAbsoluteDate, formatCombinedDate, formatDateForInput, formatJson, formatRelativeDate, formatSingleTimeDisplay, formatTimeRangeDisplay, formatTimezoneDate, formatTimezoneLabel, formatUTCDate, formatUtcForDisplay, formatYaml, getBrowserTimezone, getContextLabel, getDefaultPreset, getDefaultTimezoneOptions, getGutter, getPresetByKey, getPresetByShortcut, getPresetRange, getResponsiveValue, getShortTimezoneDisplay, getTimestamp, getTimezoneAbbreviation, getTimezoneOffset, hasActiveDescendant, isNavItemActive, isValidJson, isValidYaml, jsonSchema, jsonToYaml, localInputStringToUtc, logoStyles, matchesCurrentScope, normalizePath, paragraphVariants, parseDate, registerMediaQuery, resolvePath, rowMatchesSearch, startProgress, stopProgress, textVariants, titleVariants, toApiTimeRange, toUTCTimestampEndOfDay, toUTCTimestampStartOfDay, toast, useAdapter, useBreakpoint, useControllableState, useCopyToClipboard, useCurrentScope, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, useDebounce, useDebouncedSearchInput, useField, useFieldContext, useFormContext, useFormState, useInSheet, useLeaflet, useNuqsAdapter, useRichTextEditor, useSidebar, useTaskQueue, useTasksWithLabels, useTheme, useToast, useWatch, useWatchAll, utcStringToZonedDate, utcToLocalInputString, yamlSchema, yamlToJson, zonedDateToUtcString };
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-CTIuhCOs.mjs";
2
- import { t as ButtonGroup } from "./button-group-C3GKo-0p.mjs";
2
+ import { t as ButtonGroup } from "./button-group-DJ6X1SSj.mjs";
3
3
  import { t as Button } from "./button-C57mkFkS.mjs";
4
4
  import { i as CommandGroup, o as CommandItem, r as CommandEmpty, s as CommandList, t as Command } from "./command-B0be-zU5.mjs";
5
5
  import { i as InputGroupInput, n as InputGroupAddon, t as InputGroup } from "./input-group-CCzvJIZM.mjs";
@@ -1,7 +1,7 @@
1
1
  import { t as cn } from "../utils-CTIuhCOs.mjs";
2
2
  import { Skeleton } from "../skeleton/index.mjs";
3
3
  import { Badge } from "../badge/index.mjs";
4
- import { t as useBreakpoint } from "../use-breakpoint-zY_JYINK.mjs";
4
+ import { t as useBreakpoint } from "../use-breakpoint-Ce5tS4cU.mjs";
5
5
  import { t as useCopyToClipboard } from "../use-copy-to-clipboard-CWKWHLxI.mjs";
6
6
  import { t as Button } from "../button-Cn4KagBO.mjs";
7
7
  import { t as Sheet } from "../sheet-DEpEJ_Rk.mjs";
@@ -10,11 +10,11 @@ import { t as CopyStateIcon } from "../copy-state-icon-DMvXXsZm.mjs";
10
10
  import { c as getBrowserTimezone, t as DateTime, u as getTimezoneAbbreviation } from "../date-time-BBRVM9qb.mjs";
11
11
  import { t as Checkbox } from "../checkbox-BAo5sbHk.mjs";
12
12
  import { n as CollapsibleContent, r as CollapsibleTrigger, t as Collapsible } from "../collapsible-B-KP3JZ-.mjs";
13
- import { t as DateTimeRangePicker } from "../date-time-range-picker-yy1Acntn.mjs";
13
+ import { t as DateTimeRangePicker } from "../date-time-range-picker-CqGGJTLS.mjs";
14
14
  import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "../table/index.mjs";
15
15
  import { t as Input } from "../input-DsT5qkJn.mjs";
16
16
  import { Bot, ChevronDown, ChevronUp, Clock, Download, HelpCircle, Inbox, Monitor, RefreshCw, Search, SlidersHorizontal, Smartphone, Tablet, TriangleAlert, X } from "lucide-react";
17
- import { createContext, memo, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
17
+ import { createContext, memo, use, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
18
18
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
19
19
  import { AnimatePresence, motion, useReducedMotion } from "motion/react";
20
20
  import { format, isSameDay } from "date-fns";
@@ -320,6 +320,322 @@ function useLogs() {
320
320
  if (!context) throw new Error("Logs components must be used within <Logs.Root>");
321
321
  return context;
322
322
  }
323
+ // src/components/features/logs/utils/time-range.ts
324
+ const MINUTE_MS$1 = 6e4;
325
+ const HOUR_MS = 60 * MINUTE_MS$1;
326
+ const DAY_MS$1 = 24 * HOUR_MS;
327
+ function relativePreset(key, label, durationMs, shortcut) {
328
+ return {
329
+ key,
330
+ label,
331
+ shortcut,
332
+ getRange: () => {
333
+ const now = /* @__PURE__ */ new Date();
334
+ return {
335
+ from: new Date(now.getTime() - durationMs),
336
+ to: now
337
+ };
338
+ }
339
+ };
340
+ }
341
+ /**
342
+ * Relative "Last N" windows offered by `Logs.Filters`. Keys are persisted on
343
+ * `LogTimeRange.preset` so hosts can slide the window forward with
344
+ * `resolveLogTimeRange` on refresh or while live tailing.
345
+ *
346
+ * Shortcuts follow the `picker` datetime presets (`1` / `2` / `H` / `W`) that
347
+ * the logs picker is built on. Keys that mean something else in the pickers
348
+ * (`3` is "Last 30 minutes" in `time-range-picker`, `D` is "Today") are not
349
+ * reused, so muscle memory carries over between the two.
350
+ */
351
+ const LOG_TIME_PRESETS = [
352
+ relativePreset("last-15m", "Last 15 minutes", 15 * MINUTE_MS$1, "1"),
353
+ relativePreset("last-30m", "Last 30 minutes", 30 * MINUTE_MS$1, "2"),
354
+ relativePreset("last-1h", "Last hour", HOUR_MS, "H"),
355
+ relativePreset("last-3h", "Last 3 hours", 3 * HOUR_MS),
356
+ relativePreset("last-6h", "Last 6 hours", 6 * HOUR_MS, "6"),
357
+ relativePreset("last-12h", "Last 12 hours", 12 * HOUR_MS),
358
+ relativePreset("last-24h", "Last 24 hours", DAY_MS$1),
359
+ relativePreset("last-7d", "Last 7 days", 7 * DAY_MS$1, "W")
360
+ ];
361
+ function lastThirtyMinutes(now = /* @__PURE__ */ new Date()) {
362
+ return {
363
+ from: (/* @__PURE__ */ new Date(now.getTime() - 30 * MINUTE_MS$1)).toISOString(),
364
+ to: now.toISOString(),
365
+ preset: "last-30m"
366
+ };
367
+ }
368
+ /**
369
+ * Re-anchor a preset range to `now`. Absolute ranges (no `preset`, or an
370
+ * unknown key) are returned unchanged.
371
+ */
372
+ function resolveLogTimeRange(range, now = /* @__PURE__ */ new Date()) {
373
+ if (!range.preset) return range;
374
+ const preset = LOG_TIME_PRESETS.find((item) => item.key === range.preset);
375
+ if (!preset) return range;
376
+ const { from, to } = preset.getRange("UTC");
377
+ const shift = now.getTime() - to.getTime();
378
+ return {
379
+ from: new Date(from.getTime() + shift).toISOString(),
380
+ to: now.toISOString(),
381
+ preset: range.preset
382
+ };
383
+ }
384
+ /** Human label for the time-range trigger: preset name, or a compact absolute range. */
385
+ function logTimeRangeLabel(range) {
386
+ const preset = range.preset ? LOG_TIME_PRESETS.find((item) => item.key === range.preset) : void 0;
387
+ if (preset) return preset.label;
388
+ const from = new Date(range.from);
389
+ const to = new Date(range.to);
390
+ if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) return "Select range";
391
+ if (isSameDay(from, to)) return `${format(from, "MMM d, HH:mm")} – ${format(to, "HH:mm")}`;
392
+ return `${format(from, "MMM d, HH:mm")} – ${format(to, "MMM d, HH:mm")}`;
393
+ }
394
+ function filtersAreActive(filters) {
395
+ return Object.values(filters).some((values) => values.length > 0);
396
+ }
397
+ // src/components/features/logs/utils/url-state.ts
398
+ /**
399
+ * Query-string keys, before `prefix`. A preset range is written as
400
+ * `range=last-1h`; a hand-picked one as `from` and `to` ISO timestamps. Each
401
+ * selected facet value is its own `filter=<label>:<value>` entry.
402
+ */
403
+ const LOGS_URL_KEYS = {
404
+ search: "q",
405
+ range: "range",
406
+ from: "from",
407
+ to: "to",
408
+ filter: "filter",
409
+ live: "live",
410
+ selected: "log"
411
+ };
412
+ /** Same rule as `buildLogQL`: anything else can't be a Loki label. */
413
+ const LABEL_NAME$1 = /^[a-z_]\w*$/i;
414
+ function keysFor(prefix = "") {
415
+ return {
416
+ search: `${prefix}${LOGS_URL_KEYS.search}`,
417
+ range: `${prefix}${LOGS_URL_KEYS.range}`,
418
+ from: `${prefix}${LOGS_URL_KEYS.from}`,
419
+ to: `${prefix}${LOGS_URL_KEYS.to}`,
420
+ filter: `${prefix}${LOGS_URL_KEYS.filter}`,
421
+ live: `${prefix}${LOGS_URL_KEYS.live}`,
422
+ selected: `${prefix}${LOGS_URL_KEYS.selected}`
423
+ };
424
+ }
425
+ function toParams(input) {
426
+ return typeof input === "string" ? new URLSearchParams(input) : input;
427
+ }
428
+ function isValidDate(value) {
429
+ return value !== null && value !== "" && !Number.isNaN(new Date(value).getTime());
430
+ }
431
+ function isKnownPreset(key) {
432
+ return LOG_TIME_PRESETS.some((preset) => preset.key === key);
433
+ }
434
+ function sameRange(a, b) {
435
+ if (a.preset || b.preset) return a.preset === b.preset;
436
+ return a.from === b.from && a.to === b.to;
437
+ }
438
+ function parseTimeRange(params, keys, fallback, now) {
439
+ const preset = params.get(keys.range);
440
+ if (preset && isKnownPreset(preset)) return resolveLogTimeRange({
441
+ from: "",
442
+ to: "",
443
+ preset
444
+ }, now);
445
+ const from = params.get(keys.from);
446
+ const to = params.get(keys.to);
447
+ if (isValidDate(from) && isValidDate(to) && new Date(from) < new Date(to)) return {
448
+ from: new Date(from).toISOString(),
449
+ to: new Date(to).toISOString()
450
+ };
451
+ return fallback.preset ? resolveLogTimeRange(fallback, now) : fallback;
452
+ }
453
+ function parseFilters(params, key) {
454
+ const filters = {};
455
+ for (const entry of params.getAll(key)) {
456
+ const split = entry.indexOf(":");
457
+ if (split <= 0) continue;
458
+ const name = entry.slice(0, split);
459
+ const value = entry.slice(split + 1);
460
+ if (!LABEL_NAME$1.test(name) || value === "") continue;
461
+ const values = filters[name] ?? (filters[name] = []);
462
+ if (!values.includes(value)) values.push(value);
463
+ }
464
+ return filters;
465
+ }
466
+ /**
467
+ * Read logs state from a query string. Missing or malformed values fall back
468
+ * to the defaults (`Logs.Root`'s own defaults), so a hand-edited or stale link
469
+ * still opens. A preset range is resolved against `now`.
470
+ */
471
+ function parseLogsUrlState(input, options = {}) {
472
+ const params = toParams(input);
473
+ const keys = keysFor(options.prefix);
474
+ const now = options.now ?? /* @__PURE__ */ new Date();
475
+ const fallback = options.defaultTimeRange ?? lastThirtyMinutes(now);
476
+ const live = params.get(keys.live);
477
+ return {
478
+ timeRange: parseTimeRange(params, keys, fallback, now),
479
+ filters: parseFilters(params, keys.filter),
480
+ search: params.get(keys.search) ?? "",
481
+ live: live === "1" || live === "true",
482
+ selectedId: params.get(keys.selected) || null
483
+ };
484
+ }
485
+ /**
486
+ * Return a copy of `input` with the given parts of logs state written in.
487
+ * Only the parts present in `state` are touched, and params the app owns are
488
+ * kept. Defaults (no search, no filters, not live, the default range) are
489
+ * left out so a fresh explorer has a clean URL.
490
+ */
491
+ function writeLogsUrlState(input, state, options = {}) {
492
+ const params = new URLSearchParams(toParams(input));
493
+ const keys = keysFor(options.prefix);
494
+ if (state.timeRange !== void 0) {
495
+ params.delete(keys.range);
496
+ params.delete(keys.from);
497
+ params.delete(keys.to);
498
+ const range = state.timeRange;
499
+ if (!sameRange(range, options.defaultTimeRange ?? {
500
+ from: "",
501
+ to: "",
502
+ preset: "last-30m"
503
+ })) {
504
+ if (range.preset && isKnownPreset(range.preset)) params.set(keys.range, range.preset);
505
+ else if (isValidDate(range.from) && isValidDate(range.to)) {
506
+ params.set(keys.from, range.from);
507
+ params.set(keys.to, range.to);
508
+ }
509
+ }
510
+ }
511
+ if (state.filters !== void 0) {
512
+ params.delete(keys.filter);
513
+ for (const [name, values] of Object.entries(state.filters)) {
514
+ if (!LABEL_NAME$1.test(name)) continue;
515
+ for (const value of values) if (value !== "") params.append(keys.filter, `${name}:${value}`);
516
+ }
517
+ }
518
+ if (state.search !== void 0) {
519
+ if (state.search === "") params.delete(keys.search);
520
+ else params.set(keys.search, state.search);
521
+ }
522
+ if (state.live !== void 0) {
523
+ if (state.live) params.set(keys.live, "1");
524
+ else params.delete(keys.live);
525
+ }
526
+ if (state.selectedId !== void 0) {
527
+ if (state.selectedId) params.set(keys.selected, state.selectedId);
528
+ else params.delete(keys.selected);
529
+ }
530
+ return params;
531
+ }
532
+ /**
533
+ * Just this explorer's params, as a query string in a fixed key order. It
534
+ * parses to the same state as the full query string, and only changes when
535
+ * one of these params does, so it works as a memo key: an unrelated app param
536
+ * changing won't re-resolve a preset range (which would hand the host a new
537
+ * `timeRange` and trigger a refetch).
538
+ */
539
+ function logsUrlStateKey(input, options = {}) {
540
+ const params = toParams(input);
541
+ const own = new URLSearchParams();
542
+ for (const key of Object.values(keysFor(options.prefix))) for (const value of params.getAll(key)) own.append(key, value);
543
+ return own.toString();
544
+ }
545
+ // src/components/features/logs/hooks/use-logs-url-state.ts
546
+ /** pushState/replaceState don't fire `popstate`, so writes announce themselves. */
547
+ const LOCATION_CHANGE_EVENT = "datum-ui:logs-url-change";
548
+ function subscribeToLocation(onChange) {
549
+ window.addEventListener("popstate", onChange);
550
+ window.addEventListener(LOCATION_CHANGE_EVENT, onChange);
551
+ return () => {
552
+ window.removeEventListener("popstate", onChange);
553
+ window.removeEventListener(LOCATION_CHANGE_EVENT, onChange);
554
+ };
555
+ }
556
+ const getLocationSearch = () => window.location.search;
557
+ const getServerSearch = () => "";
558
+ const setWindowSearchParams = (update, options) => {
559
+ const next = update(new URLSearchParams(window.location.search)).toString();
560
+ const url = `${window.location.pathname}${next ? `?${next}` : ""}${window.location.hash}`;
561
+ if (options?.replace === false) window.history.pushState(window.history.state, "", url);
562
+ else window.history.replaceState(window.history.state, "", url);
563
+ window.dispatchEvent(new Event(LOCATION_CHANGE_EVENT));
564
+ };
565
+ /**
566
+ * Keep `Logs.Root`'s time range, filters, search, live toggle and open row in
567
+ * the address bar, so a refresh or a shared link reopens the same view.
568
+ *
569
+ * ```tsx
570
+ * const logsState = useLogsUrlState()
571
+ * const query = buildLogQL({ matchers: logsState.filters, lineContains: logsState.search })
572
+ * // fetch with query + resolveLogTimeRange(logsState.timeRange)
573
+ *
574
+ * <Logs.Root {...logsState} entries={entries} onRefresh={refetch}>
575
+ * <Logs.Explorer />
576
+ * </Logs.Root>
577
+ * ```
578
+ *
579
+ * With React Router, hand it the router's search params instead:
580
+ *
581
+ * ```tsx
582
+ * const [searchParams, setSearchParams] = useSearchParams()
583
+ * const logsState = useLogsUrlState({ searchParams, setSearchParams })
584
+ * ```
585
+ */
586
+ function useLogsUrlState(options = {}) {
587
+ const { searchParams, setSearchParams, prefix = "", defaultTimeRange, history = "replace" } = options;
588
+ const locationSearch = useSyncExternalStore(subscribeToLocation, getLocationSearch, getServerSearch);
589
+ const write = setSearchParams ?? setWindowSearchParams;
590
+ const replace = history !== "push";
591
+ const hasDefault = defaultTimeRange !== void 0;
592
+ const defaultFrom = defaultTimeRange?.from;
593
+ const defaultTo = defaultTimeRange?.to;
594
+ const defaultPreset = defaultTimeRange?.preset;
595
+ const urlOptions = useMemo(() => ({
596
+ prefix,
597
+ defaultTimeRange: hasDefault ? {
598
+ from: defaultFrom ?? "",
599
+ to: defaultTo ?? "",
600
+ preset: defaultPreset
601
+ } : void 0
602
+ }), [
603
+ prefix,
604
+ hasDefault,
605
+ defaultFrom,
606
+ defaultTo,
607
+ defaultPreset
608
+ ]);
609
+ const ownQuery = logsUrlStateKey(searchParams ?? locationSearch, { prefix });
610
+ const state = useMemo(() => parseLogsUrlState(ownQuery, urlOptions), [ownQuery, urlOptions]);
611
+ const update = useCallback((patch) => {
612
+ write((prev) => writeLogsUrlState(prev, patch, urlOptions), { replace });
613
+ }, [
614
+ write,
615
+ urlOptions,
616
+ replace
617
+ ]);
618
+ const onTimeRangeChange = useCallback((timeRange) => update({ timeRange }), [update]);
619
+ const onFiltersChange = useCallback((filters) => update({ filters }), [update]);
620
+ const onSearchChange = useCallback((search) => update({ search }), [update]);
621
+ const onLiveChange = useCallback((live) => update({ live }), [update]);
622
+ const onSelectedIdChange = useCallback((selectedId) => update({ selectedId }), [update]);
623
+ return useMemo(() => ({
624
+ ...state,
625
+ onTimeRangeChange,
626
+ onFiltersChange,
627
+ onSearchChange,
628
+ onLiveChange,
629
+ onSelectedIdChange
630
+ }), [
631
+ state,
632
+ onTimeRangeChange,
633
+ onFiltersChange,
634
+ onSearchChange,
635
+ onLiveChange,
636
+ onSelectedIdChange
637
+ ]);
638
+ }
323
639
  function clampLogsDetailWidth(width, maxWidth) {
324
640
  return Math.min(Math.max(Math.round(width), 400), maxWidth > 400 ? maxWidth : 400);
325
641
  }
@@ -1030,84 +1346,15 @@ function LogsDetail({ className }) {
1030
1346
  }), /* @__PURE__ */ jsx(DetailBody, {})]
1031
1347
  }) });
1032
1348
  }
1033
- // src/components/features/logs/utils/time-range.ts
1034
- const MINUTE_MS$1 = 6e4;
1035
- const HOUR_MS = 60 * MINUTE_MS$1;
1036
- const DAY_MS$1 = 24 * HOUR_MS;
1037
- function relativePreset(key, label, durationMs, shortcut) {
1038
- return {
1039
- key,
1040
- label,
1041
- shortcut,
1042
- getRange: () => {
1043
- const now = /* @__PURE__ */ new Date();
1044
- return {
1045
- from: new Date(now.getTime() - durationMs),
1046
- to: now
1047
- };
1048
- }
1049
- };
1050
- }
1051
- /**
1052
- * Relative "Last N" windows offered by `Logs.Filters`. Keys are persisted on
1053
- * `LogTimeRange.preset` so hosts can slide the window forward with
1054
- * `resolveLogTimeRange` on refresh or while live tailing.
1055
- *
1056
- * Shortcuts follow the `picker` datetime presets (`1` / `2` / `H` / `W`) that
1057
- * the logs picker is built on. Keys that mean something else in the pickers
1058
- * (`3` is "Last 30 minutes" in `time-range-picker`, `D` is "Today") are not
1059
- * reused, so muscle memory carries over between the two.
1060
- */
1061
- const LOG_TIME_PRESETS = [
1062
- relativePreset("last-15m", "Last 15 minutes", 15 * MINUTE_MS$1, "1"),
1063
- relativePreset("last-30m", "Last 30 minutes", 30 * MINUTE_MS$1, "2"),
1064
- relativePreset("last-1h", "Last hour", HOUR_MS, "H"),
1065
- relativePreset("last-3h", "Last 3 hours", 3 * HOUR_MS),
1066
- relativePreset("last-6h", "Last 6 hours", 6 * HOUR_MS, "6"),
1067
- relativePreset("last-12h", "Last 12 hours", 12 * HOUR_MS),
1068
- relativePreset("last-24h", "Last 24 hours", DAY_MS$1),
1069
- relativePreset("last-7d", "Last 7 days", 7 * DAY_MS$1, "W")
1070
- ];
1071
- function lastThirtyMinutes(now = /* @__PURE__ */ new Date()) {
1072
- return {
1073
- from: (/* @__PURE__ */ new Date(now.getTime() - 30 * MINUTE_MS$1)).toISOString(),
1074
- to: now.toISOString(),
1075
- preset: "last-30m"
1076
- };
1077
- }
1078
- /**
1079
- * Re-anchor a preset range to `now`. Absolute ranges (no `preset`, or an
1080
- * unknown key) are returned unchanged.
1081
- */
1082
- function resolveLogTimeRange(range, now = /* @__PURE__ */ new Date()) {
1083
- if (!range.preset) return range;
1084
- const preset = LOG_TIME_PRESETS.find((item) => item.key === range.preset);
1085
- if (!preset) return range;
1086
- const { from, to } = preset.getRange("UTC");
1087
- const shift = now.getTime() - to.getTime();
1088
- return {
1089
- from: new Date(from.getTime() + shift).toISOString(),
1090
- to: now.toISOString(),
1091
- preset: range.preset
1092
- };
1093
- }
1094
- /** Human label for the time-range trigger: preset name, or a compact absolute range. */
1095
- function logTimeRangeLabel(range) {
1096
- const preset = range.preset ? LOG_TIME_PRESETS.find((item) => item.key === range.preset) : void 0;
1097
- if (preset) return preset.label;
1098
- const from = new Date(range.from);
1099
- const to = new Date(range.to);
1100
- if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) return "Select range";
1101
- if (isSameDay(from, to)) return `${format(from, "MMM d, HH:mm")} – ${format(to, "HH:mm")}`;
1102
- return `${format(from, "MMM d, HH:mm")} – ${format(to, "MMM d, HH:mm")}`;
1103
- }
1104
- function filtersAreActive(filters) {
1105
- return Object.values(filters).some((values) => values.length > 0);
1106
- }
1107
1349
  // src/components/features/logs/components/filters.tsx
1108
1350
  function LogsFilterGroup({ facet, defaultOpen }) {
1109
1351
  const { filters, toggleFilterValue } = useLogs();
1110
1352
  const selected = filters[facet.name] ?? [];
1353
+ const known = new Set(facet.options.map((option) => option.value));
1354
+ const options = [...facet.options, ...selected.filter((value) => !known.has(value)).map((value) => ({
1355
+ value,
1356
+ count: void 0
1357
+ }))];
1111
1358
  return /* @__PURE__ */ jsxs(Collapsible, {
1112
1359
  defaultOpen,
1113
1360
  className: "border-b border-border",
@@ -1119,7 +1366,7 @@ function LogsFilterGroup({ facet, defaultOpen }) {
1119
1366
  })]
1120
1367
  }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("ul", {
1121
1368
  className: "flex flex-col gap-1 px-3 pb-3",
1122
- children: facet.options.map((option) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("label", {
1369
+ children: options.map((option) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("label", {
1123
1370
  className: "hover:bg-muted/40 flex cursor-pointer items-center gap-2 rounded-md px-1 py-1 text-sm",
1124
1371
  children: [
1125
1372
  /* @__PURE__ */ jsx(Checkbox, {
@@ -1596,7 +1843,7 @@ function LogsSearch({ placeholder = "Search logs...", className }) {
1596
1843
  onChange: (event) => setValue(event.target.value),
1597
1844
  placeholder,
1598
1845
  "aria-label": placeholder,
1599
- className: "pl-9",
1846
+ className: "h-9 pl-9",
1600
1847
  "data-slot": "logs-search"
1601
1848
  })]
1602
1849
  });
@@ -1984,6 +2231,53 @@ function buildLogQL({ matchers, lineContains } = {}) {
1984
2231
  if (!text) return selector;
1985
2232
  return `${selector} |= "${escapeQuoted(text)}"`;
1986
2233
  }
2234
+ // src/components/features/logs/utils/facet-counts.ts
2235
+ /** Same rule as `buildLogQL`: anything else can't be a Loki label. */
2236
+ const LABEL_NAME = /^[a-z_]\w*$/i;
2237
+ /** Whole seconds between `from` and `to`, at least 1. */
2238
+ function rangeSeconds({ from, to }) {
2239
+ const ms = new Date(to).getTime() - new Date(from).getTime();
2240
+ return Number.isFinite(ms) ? Math.max(1, Math.round(ms / 1e3)) : 1;
2241
+ }
2242
+ /**
2243
+ * LogQL that counts log lines per value of one label across a time range:
2244
+ *
2245
+ * ```
2246
+ * sum by (response_code) (count_over_time({route_name="my-alb"}[1800s]))
2247
+ * ```
2248
+ *
2249
+ * Run it as an instant query (`/loki/api/v1/query`) with `time` set to the
2250
+ * range's `to`, then pass the response to `facetFromLokiCounts`. Returns
2251
+ * `null` for a label name LogQL can't express. Works for indexed stream labels
2252
+ * and for structured metadata, which Loki can also group by.
2253
+ */
2254
+ function buildLogFacetCountQuery({ label, matchers, timeRange }) {
2255
+ if (!LABEL_NAME.test(label)) return null;
2256
+ return `sum by (${label}) (count_over_time(${buildLogQL({ matchers })}[${rangeSeconds(timeRange)}s]))`;
2257
+ }
2258
+ /**
2259
+ * Turn the response to a `buildLogFacetCountQuery` query into a facet for
2260
+ * `Logs.Root`. Series without the label (lines that don't carry it) and zero
2261
+ * counts are skipped. Options are ordered the same way as `facetsFromEntries`.
2262
+ * Pass `label` to override the display name.
2263
+ */
2264
+ function facetFromLokiCounts(name, response, options = {}) {
2265
+ const counts = /* @__PURE__ */ new Map();
2266
+ if (response?.status === "success" && response.data?.resultType === "vector") for (const sample of response.data.result) {
2267
+ const value = sample.metric[name];
2268
+ const count = Number(sample.value?.[1]);
2269
+ if (!value || !Number.isFinite(count) || count <= 0) continue;
2270
+ counts.set(value, (counts.get(value) ?? 0) + Math.round(count));
2271
+ }
2272
+ return {
2273
+ name,
2274
+ label: options.label ?? facetLabel(name),
2275
+ options: [...counts.entries()].sort(([a], [b]) => compareFacetValues(name, a, b)).map(([value, count]) => ({
2276
+ value,
2277
+ count
2278
+ }))
2279
+ };
2280
+ }
1987
2281
  // src/components/features/logs/utils/flatten-loki.ts
1988
2282
  function nsToDate(timestampNs) {
1989
2283
  try {
@@ -2027,4 +2321,4 @@ function flattenLokiStreams(response) {
2027
2321
  });
2028
2322
  return entries;
2029
2323
  }
2030
- export { LOG_TIME_PRESETS, Logs, buildLogQL, facetsFromEntries, filterEntries, flattenLokiStreams, formatHttpLogLine, histogramFromEntries, httpStatusBadgeType, httpStatusTextClass, lastThirtyMinutes, logLineDisplay, logRequestHost, logTimeRangeLabel, logUserAgent, nsToDate, parseLogLine, parseUserAgent, resolveLogColumns, resolveLogTimeRange, searchableLogText, severityBadgeType, splitPathQuery, useLogs };
2324
+ export { LOGS_URL_KEYS, LOG_TIME_PRESETS, Logs, buildLogFacetCountQuery, buildLogQL, facetFromLokiCounts, facetsFromEntries, filterEntries, flattenLokiStreams, formatHttpLogLine, histogramFromEntries, httpStatusBadgeType, httpStatusTextClass, lastThirtyMinutes, logLineDisplay, logRequestHost, logTimeRangeLabel, logUserAgent, nsToDate, parseLogLine, parseLogsUrlState, parseUserAgent, resolveLogColumns, resolveLogTimeRange, searchableLogText, severityBadgeType, splitPathQuery, useLogs, useLogsUrlState, writeLogsUrlState };
@@ -1 +1 @@
1
- export { k as Map, b as MapCircle, x as MapCircleMarker, h as MapControlContainer, o as MapDrawCircle, s as MapDrawControl, c as MapDrawDelete, l as MapDrawEdit, u as MapDrawMarker, d as MapDrawPolygon, f as MapDrawPolyline, p as MapDrawRectangle, m as MapDrawUndo, t as MapFeatureGroup, g as MapFullscreenControl, n as MapLayerGroup, r as MapLayers, i as MapLayersControl, _ as MapLocateControl, S as MapMarker, C as MapMarkerClusterGroup, w as MapPolygon, T as MapPolyline, E as MapPopup, D as MapRectangle, v as MapSearchControl, a as MapTileLayer, O as MapTooltip, y as MapZoomControl, j as PlaceAutocomplete, A as useLeaflet } from "../layers-DLUbkJtt.mjs";
1
+ export { k as Map, b as MapCircle, x as MapCircleMarker, h as MapControlContainer, o as MapDrawCircle, s as MapDrawControl, c as MapDrawDelete, l as MapDrawEdit, u as MapDrawMarker, d as MapDrawPolygon, f as MapDrawPolyline, p as MapDrawRectangle, m as MapDrawUndo, t as MapFeatureGroup, g as MapFullscreenControl, n as MapLayerGroup, r as MapLayers, i as MapLayersControl, _ as MapLocateControl, S as MapMarker, C as MapMarkerClusterGroup, w as MapPolygon, T as MapPolyline, E as MapPopup, D as MapRectangle, v as MapSearchControl, a as MapTileLayer, O as MapTooltip, y as MapZoomControl, j as PlaceAutocomplete, A as useLeaflet } from "../layers-BC1Ny49w.mjs";
@@ -1,3 +1,3 @@
1
1
  import { t as ActionRow } from "../action-row-BEzE9x37.mjs";
2
- import { t as MoreActions } from "../more-actions-BlSOVwa8.mjs";
2
+ import { t as MoreActions } from "../more-actions-DXQiV-bN.mjs";
3
3
  export { ActionRow, MoreActions };
@@ -1,7 +1,7 @@
1
1
  import { t as Button } from "./button-Cn4KagBO.mjs";
2
2
  import { t as Tooltip } from "./tooltip-OIySjJWW.mjs";
3
3
  import { t as cn } from "./utils-CTIuhCOs.mjs";
4
- import { t as ResponsiveDropdown } from "./responsive-dropdown-DsWgpE_i.mjs";
4
+ import { t as ResponsiveDropdown } from "./responsive-dropdown-B9cDUeHt.mjs";
5
5
  import { t as ActionRow } from "./action-row-BEzE9x37.mjs";
6
6
  import { Ellipsis } from "lucide-react";
7
7
  import { useState } from "react";
@@ -1 +1 @@
1
- export { t as MultiSelect } from "../multi-select-C2cBrqXl.mjs";
1
+ export { t as MultiSelect } from "../multi-select-DwcsJdHt.mjs";