@oneuptime/common 11.4.0 → 11.4.1

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 (178) hide show
  1. package/Server/API/AIAgentTaskAPI.ts +10 -24
  2. package/Server/API/TelemetryAPI.ts +4 -3
  3. package/Server/Services/AIAgentTaskService.ts +62 -0
  4. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  5. package/Server/Services/LogAggregationService.ts +29 -11
  6. package/Server/Services/MetricService.ts +603 -13
  7. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +1 -1
  8. package/Server/Utils/AI/Sentinel/README.md +10 -0
  9. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  10. package/Server/Utils/Monitor/MonitorMetricUtil.ts +108 -2
  11. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  12. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  13. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  14. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  15. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  16. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  17. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  18. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  19. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  20. package/Tests/UI/Components/Modal.test.tsx +144 -2
  21. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  22. package/Tests/UI/Utils/Theme.test.ts +161 -0
  23. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  24. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  25. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  26. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +19 -0
  27. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  28. package/Types/Monitor/CriteriaFilter.ts +4 -0
  29. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  30. package/Types/Monitor/MonitorMetricType.ts +17 -0
  31. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  32. package/Types/Probe/ProbeMonitorResponse.ts +14 -0
  33. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  34. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  35. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  36. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  37. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  38. package/UI/Components/Divider/Divider.tsx +1 -1
  39. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  40. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  41. package/UI/Components/ErrorBoundary.tsx +2 -1
  42. package/UI/Components/EventItem/EventItem.tsx +2 -2
  43. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  44. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  45. package/UI/Components/Header/Header.tsx +7 -0
  46. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  47. package/UI/Components/Label/Label.tsx +2 -2
  48. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  49. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  50. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  51. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  52. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  53. package/UI/Components/Modal/Modal.tsx +213 -121
  54. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  55. package/UI/Components/ModelTable/TableView.tsx +1 -1
  56. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  57. package/UI/Components/Pill/Pill.tsx +21 -13
  58. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  59. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  60. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  61. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  62. package/UI/Components/Workflow/Component.tsx +53 -33
  63. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  64. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  65. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  66. package/UI/Components/Workflow/Workflow.tsx +13 -11
  67. package/UI/Utils/Theme.ts +140 -0
  68. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +104 -0
  69. package/Utils/Dashboard/Components/Index.ts +7 -0
  70. package/Utils/Monitor/MonitorMetricType.ts +70 -2
  71. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  72. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  73. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  74. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  75. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  76. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  77. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  78. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  79. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  80. package/build/dist/Server/Services/MetricService.js +449 -7
  81. package/build/dist/Server/Services/MetricService.js.map +1 -1
  82. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +1 -1
  83. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  84. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  85. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +86 -2
  86. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  87. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  88. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  89. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  90. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  91. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  92. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  93. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  94. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  95. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  96. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  97. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  98. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  99. package/build/dist/Types/Monitor/MonitorMetricType.js +15 -0
  100. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  101. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  102. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  103. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  104. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  105. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  106. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  107. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  108. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  109. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  110. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  111. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  112. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  113. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  114. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  115. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  116. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  117. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  118. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  119. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  120. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  121. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  122. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  123. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  124. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  125. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  126. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  127. package/build/dist/UI/Components/Header/Header.js +1 -0
  128. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  129. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  130. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  131. package/build/dist/UI/Components/Label/Label.js +2 -2
  132. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  133. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  134. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  135. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  136. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  137. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  138. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  139. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  140. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  141. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  142. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  143. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  144. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  145. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  146. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  147. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  148. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  149. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  150. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  151. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  152. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  153. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  154. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  155. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  156. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  157. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  158. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  159. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  160. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  161. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  162. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  163. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  164. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  165. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  166. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  167. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  168. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  169. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  170. package/build/dist/UI/Utils/Theme.js +106 -0
  171. package/build/dist/UI/Utils/Theme.js.map +1 -0
  172. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +92 -0
  173. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  174. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  175. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  176. package/build/dist/Utils/Monitor/MonitorMetricType.js +70 -3
  177. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  178. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Packet-level statistics captured by the Ping/IP monitor. Populated when
3
+ * the probe sends multiple ICMP echo requests per check. All RTT values
4
+ * are in milliseconds. Absent on older probes (single-echo pings) and when
5
+ * ICMP is blocked and the probe falls back to TCP port checks, which keeps
6
+ * the pipeline backwards-compatible.
7
+ */
8
+ export default interface PingMonitorResponse {
9
+ packetsSent: number;
10
+ packetsReceived: number;
11
+ packetLossPercent: number;
12
+ minRoundTripTimeInMs?: number | undefined;
13
+ maxRoundTripTimeInMs?: number | undefined;
14
+ avgRoundTripTimeInMs?: number | undefined;
15
+ /*
16
+ * Jitter is the standard deviation of round-trip times across the
17
+ * packets sent in this check.
18
+ */
19
+ jitterInMs?: number | undefined;
20
+ }
@@ -8,10 +8,13 @@ import SslMonitorResponse from "../Monitor/SSLMonitor/SslMonitorResponse";
8
8
  import SyntheticMonitorResponse from "../Monitor/SyntheticMonitors/SyntheticMonitorResponse";
9
9
  import SnmpMonitorResponse from "../Monitor/SnmpMonitor/SnmpMonitorResponse";
10
10
  import DnsMonitorResponse from "../Monitor/DnsMonitor/DnsMonitorResponse";
11
+ import PingMonitorResponse from "../Monitor/PingMonitor/PingMonitorResponse";
11
12
  import DomainMonitorResponse from "../Monitor/DomainMonitor/DomainMonitorResponse";
12
13
  import DnssecMonitorResponse from "../Monitor/DnssecMonitor/DnssecMonitorResponse";
13
14
  import ExternalStatusPageMonitorResponse from "../Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
15
+ import HttpPhaseTimings from "../Monitor/HttpPhaseTimings";
14
16
  import MonitorEvaluationSummary from "../Monitor/MonitorEvaluationSummary";
17
+ import NetworkPathTrace from "../Monitor/NetworkMonitor/NetworkPathTrace";
15
18
  import ObjectID from "../ObjectID";
16
19
  import Port from "../Port";
17
20
  import ProbeAttempt from "./ProbeAttempt";
@@ -35,6 +38,17 @@ export default interface ProbeMonitorResponse {
35
38
  syntheticMonitorResponse?: Array<SyntheticMonitorResponse> | undefined;
36
39
  customCodeMonitorResponse?: CustomCodeMonitorResponse | undefined;
37
40
  snmpResponse?: SnmpMonitorResponse | undefined;
41
+ pingResponse?: PingMonitorResponse | undefined;
42
+ /*
43
+ * Traceroute + DNS lookup captured by the probe when a Ping/IP/Port check
44
+ * fails — path evidence from the moment of failure.
45
+ */
46
+ networkPathTrace?: NetworkPathTrace | undefined;
47
+ /*
48
+ * DNS / TCP / TLS / TTFB / download phase breakdown for Website and API
49
+ * checks. Absent when the request went through a proxy.
50
+ */
51
+ httpTimings?: HttpPhaseTimings | undefined;
38
52
  dnsResponse?: DnsMonitorResponse | undefined;
39
53
  domainResponse?: DomainMonitorResponse | undefined;
40
54
  dnssecResponse?: DnssecMonitorResponse | undefined;
@@ -815,7 +815,10 @@ const AreaChart: React.ForwardRefExoticComponent<
815
815
  wrapperStyle={{ outline: "none" }}
816
816
  isAnimationActive={true}
817
817
  animationDuration={100}
818
- cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
818
+ cursor={{
819
+ stroke: "var(--ou-chart-cursor, #d1d5db)",
820
+ strokeWidth: 1,
821
+ }}
819
822
  offset={20}
820
823
  position={{ y: 0 }}
821
824
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -903,11 +906,11 @@ const AreaChart: React.ForwardRefExoticComponent<
903
906
  }
904
907
  return [low, high];
905
908
  }}
906
- stroke="#94a3b8"
909
+ stroke="var(--ou-chart-tick, #94a3b8)"
907
910
  strokeOpacity={0.3}
908
911
  strokeWidth={1}
909
912
  strokeDasharray="3 3"
910
- fill="#94a3b8"
913
+ fill="var(--ou-chart-tick, #94a3b8)"
911
914
  fillOpacity={0.12}
912
915
  isAnimationActive={false}
913
916
  connectNulls={false}
@@ -1064,7 +1067,7 @@ const AreaChart: React.ForwardRefExoticComponent<
1064
1067
  y={exemplar.y}
1065
1068
  r={5}
1066
1069
  fill="#7c3aed"
1067
- stroke="#ffffff"
1070
+ stroke="var(--ou-chart-marker-ring, #ffffff)"
1068
1071
  strokeWidth={2}
1069
1072
  style={{ cursor: "pointer" }}
1070
1073
  onClick={() => {
@@ -1074,7 +1077,7 @@ const AreaChart: React.ForwardRefExoticComponent<
1074
1077
  <Label
1075
1078
  value="E"
1076
1079
  position="center"
1077
- fill="#ffffff"
1080
+ fill="var(--ou-chart-marker-ring, #ffffff)"
1078
1081
  fontSize={8}
1079
1082
  fontWeight={700}
1080
1083
  />
@@ -925,7 +925,10 @@ const BarChart: React.ForwardRefExoticComponent<
925
925
  wrapperStyle={{ outline: "none" }}
926
926
  isAnimationActive={true}
927
927
  animationDuration={100}
928
- cursor={{ fill: "#d1d5db", opacity: "0.15" }}
928
+ cursor={{
929
+ fill: "var(--ou-chart-cursor, #d1d5db)",
930
+ opacity: "0.15",
931
+ }}
929
932
  offset={20}
930
933
  {...(layout === "horizontal"
931
934
  ? { position: { y: 0 } }
@@ -813,7 +813,10 @@ const LineChart: React.ForwardRefExoticComponent<
813
813
  wrapperStyle={{ outline: "none" }}
814
814
  isAnimationActive={true}
815
815
  animationDuration={100}
816
- cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
816
+ cursor={{
817
+ stroke: "var(--ou-chart-cursor, #d1d5db)",
818
+ strokeWidth: 1,
819
+ }}
817
820
  offset={20}
818
821
  position={{ y: 0 }}
819
822
  content={({ active, payload, label }: any) => {
@@ -1052,7 +1055,7 @@ const LineChart: React.ForwardRefExoticComponent<
1052
1055
  y={exemplar.y}
1053
1056
  r={5}
1054
1057
  fill="#7c3aed"
1055
- stroke="#ffffff"
1058
+ stroke="var(--ou-chart-marker-ring, #ffffff)"
1056
1059
  strokeWidth={2}
1057
1060
  style={{ cursor: "pointer" }}
1058
1061
  onClick={() => {
@@ -1062,7 +1065,7 @@ const LineChart: React.ForwardRefExoticComponent<
1062
1065
  <Label
1063
1066
  value="E"
1064
1067
  position="center"
1065
- fill="#ffffff"
1068
+ fill="var(--ou-chart-marker-ring, #ffffff)"
1066
1069
  fontSize={8}
1067
1070
  fontWeight={700}
1068
1071
  />
@@ -36,8 +36,9 @@ const CodeBlock: FunctionComponent<ComponentProps> = (
36
36
  {showCopyButton && typeof props.code === "string" && (
37
37
  <button
38
38
  onClick={handleCopy}
39
- className="absolute top-2 right-2 p-2 rounded-md bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white transition-all opacity-0 group-hover:opacity-100 z-10"
39
+ className="absolute top-2 right-2 p-2 rounded-md bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white transition-all opacity-70 hover:opacity-100 focus-visible:opacity-100 group-hover:opacity-100 z-10"
40
40
  title={copied ? "Copied!" : "Copy to clipboard"}
41
+ aria-label={copied ? "Copied" : "Copy to clipboard"}
41
42
  type="button"
42
43
  >
43
44
  <Icon
@@ -1,6 +1,7 @@
1
1
  import Editor from "@monaco-editor/react";
2
2
  import CodeType from "../../../Types/Code/CodeType";
3
3
  import MarkdownUtil from "../../Utils/Markdown";
4
+ import { Theme, useTheme } from "../../Utils/Theme";
4
5
  import React, {
5
6
  FunctionComponent,
6
7
  ReactElement,
@@ -36,6 +37,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
36
37
  const [placeholder, setPlaceholder] = useState<string>("");
37
38
  const [helpText, setHelpText] = useState<string | ReactElement>("");
38
39
  const editorRef: React.MutableRefObject<any> = useRef<any>(null);
40
+ const theme: Theme = useTheme();
39
41
 
40
42
  useEffect(() => {
41
43
  let value: string | undefined = props.value;
@@ -138,6 +140,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
138
140
  )}
139
141
 
140
142
  <Editor
143
+ theme={theme === Theme.Dark ? "vs-dark" : "light"}
141
144
  defaultLanguage={props.type}
142
145
  height="30vh"
143
146
  value={value}
@@ -4,7 +4,7 @@ const Divider: () => JSX.Element = (): ReactElement => {
4
4
  return (
5
5
  <div
6
6
  className="w-full border-t border-gray-100"
7
- style={{ borderColor: "#f9fafb" }}
7
+ style={{ borderColor: "var(--ou-border-subtle, #f9fafb)" }}
8
8
  ></div>
9
9
  );
10
10
  };
@@ -41,6 +41,12 @@ export interface DropdownOptionGroup {
41
41
  options: Array<DropdownOption>;
42
42
  }
43
43
 
44
+ /*
45
+ * Modal surfaces use z-50. The portalled menu must sit above that stacking
46
+ * context so options are not painted underneath an anchored modal footer.
47
+ */
48
+ export const DROPDOWN_MENU_Z_INDEX: number = 60;
49
+
44
50
  export interface ComponentProps {
45
51
  options: Array<DropdownOption | DropdownOptionGroup>;
46
52
  initialValue?: undefined | DropdownOption | Array<DropdownOption>;
@@ -334,8 +340,8 @@ const Dropdown: FunctionComponent<ComponentProps> = (
334
340
  } = (color?: string): { backgroundColor: string; color: string } => {
335
341
  if (!color) {
336
342
  return {
337
- backgroundColor: "#e5e7eb", // gray-200
338
- color: "#374151", // gray-700
343
+ backgroundColor: "var(--ou-surface-tertiary, #e5e7eb)",
344
+ color: "var(--ou-text-secondary, #374151)",
339
345
  };
340
346
  }
341
347
 
@@ -511,6 +517,9 @@ const Dropdown: FunctionComponent<ComponentProps> = (
511
517
  setValue(value);
512
518
  }, [props.value]);
513
519
 
520
+ const menuPortalTarget: HTMLElement | null =
521
+ typeof document === "undefined" ? null : document.body;
522
+
514
523
  return (
515
524
  <div
516
525
  id={props.id}
@@ -710,11 +719,11 @@ const Dropdown: FunctionComponent<ComponentProps> = (
710
719
  menuPortal: (base: CSSObjectWithLabel): CSSObjectWithLabel => {
711
720
  return {
712
721
  ...base,
713
- zIndex: 50,
722
+ zIndex: DROPDOWN_MENU_Z_INDEX,
714
723
  };
715
724
  },
716
725
  }}
717
- menuPortalTarget={document.body}
726
+ menuPortalTarget={menuPortalTarget}
718
727
  menuPosition="fixed"
719
728
  isClearable={true}
720
729
  isSearchable={true}
@@ -10,6 +10,7 @@ import ObjectID from "../../../Types/ObjectID";
10
10
  import ModelAPI, { ListResult } from "../../Utils/ModelAPI/ModelAPI";
11
11
  import Icon from "../Icon/Icon";
12
12
  import {
13
+ DROPDOWN_MENU_Z_INDEX,
13
14
  DropdownOption,
14
15
  DropdownOptionGroup,
15
16
  DropdownOptionLabel,
@@ -20,6 +21,7 @@ import React, {
20
21
  ReactElement,
21
22
  useCallback,
22
23
  useEffect,
24
+ useLayoutEffect,
23
25
  useMemo,
24
26
  useRef,
25
27
  useState,
@@ -107,6 +109,18 @@ export interface EntityDropdownProps {
107
109
  const SEARCH_DEBOUNCE_MS: number = 250;
108
110
  const SEARCH_PAGE_SIZE: number = 50;
109
111
  const LABEL_PREVIEW_LIMIT: number = 50;
112
+ const MENU_GAP_PX: number = 4;
113
+ const MENU_MAX_HEIGHT_PX: number = 384;
114
+ const MENU_MIN_USEFUL_HEIGHT_PX: number = 160;
115
+ const MENU_VIEWPORT_PADDING_PX: number = 8;
116
+
117
+ interface EntityDropdownMenuPosition {
118
+ bottom?: number | undefined;
119
+ left: number;
120
+ maxHeight: number;
121
+ top?: number | undefined;
122
+ width: number;
123
+ }
110
124
 
111
125
  const flattenOptions: (
112
126
  options: Array<DropdownOption | DropdownOptionGroup> | undefined,
@@ -441,12 +455,91 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
441
455
 
442
456
  const containerRef: React.MutableRefObject<HTMLDivElement | null> =
443
457
  useRef<HTMLDivElement | null>(null);
458
+ const controlRef: React.MutableRefObject<HTMLDivElement | null> =
459
+ useRef<HTMLDivElement | null>(null);
444
460
  const inputRef: React.MutableRefObject<HTMLInputElement | null> =
445
461
  useRef<HTMLInputElement | null>(null);
446
462
  const debounceRef: React.MutableRefObject<number | null> = useRef<
447
463
  number | null
448
464
  >(null);
449
465
  const searchSeqRef: React.MutableRefObject<number> = useRef<number>(0);
466
+ const [menuPosition, setMenuPosition] =
467
+ useState<EntityDropdownMenuPosition | null>(null);
468
+
469
+ const updateMenuPosition: () => void = useCallback((): void => {
470
+ if (!controlRef.current || typeof window === "undefined") {
471
+ return;
472
+ }
473
+
474
+ const controlRect: DOMRect = controlRef.current.getBoundingClientRect();
475
+ const availableWidth: number = Math.max(
476
+ 0,
477
+ window.innerWidth - MENU_VIEWPORT_PADDING_PX * 2,
478
+ );
479
+ const width: number = Math.min(controlRect.width, availableWidth);
480
+ const maximumLeft: number = Math.max(
481
+ MENU_VIEWPORT_PADDING_PX,
482
+ window.innerWidth - MENU_VIEWPORT_PADDING_PX - width,
483
+ );
484
+ const left: number = Math.min(
485
+ Math.max(controlRect.left, MENU_VIEWPORT_PADDING_PX),
486
+ maximumLeft,
487
+ );
488
+ const spaceBelow: number = Math.max(
489
+ 0,
490
+ window.innerHeight -
491
+ controlRect.bottom -
492
+ MENU_GAP_PX -
493
+ MENU_VIEWPORT_PADDING_PX,
494
+ );
495
+ const spaceAbove: number = Math.max(
496
+ 0,
497
+ controlRect.top - MENU_GAP_PX - MENU_VIEWPORT_PADDING_PX,
498
+ );
499
+ const shouldOpenAbove: boolean =
500
+ spaceBelow < MENU_MIN_USEFUL_HEIGHT_PX && spaceAbove > spaceBelow;
501
+ const availableHeight: number = shouldOpenAbove ? spaceAbove : spaceBelow;
502
+
503
+ setMenuPosition({
504
+ bottom: shouldOpenAbove
505
+ ? window.innerHeight - controlRect.top + MENU_GAP_PX
506
+ : undefined,
507
+ left,
508
+ maxHeight: Math.min(MENU_MAX_HEIGHT_PX, availableHeight),
509
+ top: shouldOpenAbove ? undefined : controlRect.bottom + MENU_GAP_PX,
510
+ width,
511
+ });
512
+ }, []);
513
+
514
+ useLayoutEffect(() => {
515
+ if (!isOpen) {
516
+ setMenuPosition(null);
517
+ return;
518
+ }
519
+
520
+ let animationFrame: number | null = null;
521
+ const schedulePositionUpdate: () => void = (): void => {
522
+ if (animationFrame !== null) {
523
+ return;
524
+ }
525
+ animationFrame = window.requestAnimationFrame((): void => {
526
+ animationFrame = null;
527
+ updateMenuPosition();
528
+ });
529
+ };
530
+
531
+ updateMenuPosition();
532
+ window.addEventListener("resize", schedulePositionUpdate);
533
+ document.addEventListener("scroll", schedulePositionUpdate, true);
534
+
535
+ return () => {
536
+ window.removeEventListener("resize", schedulePositionUpdate);
537
+ document.removeEventListener("scroll", schedulePositionUpdate, true);
538
+ if (animationFrame !== null) {
539
+ window.cancelAnimationFrame(animationFrame);
540
+ }
541
+ };
542
+ }, [isOpen, selectedKeys, updateMenuPosition]);
450
543
 
451
544
  // Click-outside closes the popover.
452
545
  useEffect(() => {
@@ -1106,7 +1199,7 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
1106
1199
  </div>
1107
1200
  )}
1108
1201
 
1109
- <div className="relative">
1202
+ <div ref={controlRef} className="relative">
1110
1203
  {/*
1111
1204
  * Single-select shows the resolved label in-place when closed so the
1112
1205
  * chrome looks like a static value field. Click anywhere on the
@@ -1281,6 +1374,11 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
1281
1374
  return;
1282
1375
  }
1283
1376
  if (event.key === "Escape") {
1377
+ /*
1378
+ * The modal also listens for Escape. Consume it here so the
1379
+ * first press closes the menu without dismissing the form.
1380
+ */
1381
+ event.preventDefault();
1284
1382
  setIsOpen(false);
1285
1383
  setHighlightedIndex(-1);
1286
1384
  return;
@@ -1329,8 +1427,18 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
1329
1427
 
1330
1428
  {isOpen && !props.disabled && (
1331
1429
  <div
1332
- className="absolute z-20 mt-1 flex max-h-96 w-full flex-col overflow-hidden rounded-md border border-gray-200 bg-white text-sm shadow-lg"
1430
+ className="fixed flex flex-col overflow-hidden rounded-md border border-gray-200 bg-white text-sm shadow-lg"
1431
+ data-testid="entity-dropdown-menu"
1333
1432
  role="listbox"
1433
+ style={{
1434
+ bottom: menuPosition?.bottom,
1435
+ left: menuPosition?.left ?? 0,
1436
+ maxHeight: menuPosition?.maxHeight,
1437
+ top: menuPosition?.top,
1438
+ visibility: menuPosition ? "visible" : "hidden",
1439
+ width: menuPosition?.width ?? 0,
1440
+ zIndex: DROPDOWN_MENU_Z_INDEX,
1441
+ }}
1334
1442
  >
1335
1443
  {labelsTabEnabled && (
1336
1444
  <div className="flex flex-shrink-0 items-center gap-1 border-b border-gray-100 bg-gray-50 px-1.5 py-1">
@@ -15,7 +15,8 @@ const Fallback: FunctionComponent = () => {
15
15
  bottom: "0",
16
16
  left: "0",
17
17
  right: "0",
18
- backgroundColor: "#fdfdfd",
18
+ backgroundColor: "var(--ou-background-primary, #fdfdfd)",
19
+ color: "var(--ou-text-primary, #111827)",
19
20
  zIndex: "999",
20
21
  display: "flex",
21
22
  justifyContent: "center",
@@ -168,8 +168,8 @@ const EventItem: FunctionComponent<ComponentProps> = (
168
168
  text={item}
169
169
  color={VeryLightGray}
170
170
  style={{
171
- backgroundColor: "#f3f4f6",
172
- color: "#9ca3af",
171
+ backgroundColor: "var(--ou-surface-tertiary, #f3f4f6)",
172
+ color: "var(--ou-text-subtle, #9ca3af)",
173
173
  }}
174
174
  />
175
175
  );
@@ -137,7 +137,7 @@ const Bar: FunctionComponent<ComponentProps> = (
137
137
  marginLeft: `${barWidth + 15}px`,
138
138
  opacity: barOpacity,
139
139
  fontWeight: props.isHighlighted ? 600 : undefined,
140
- color: props.isHighlighted ? "#4338ca" : undefined,
140
+ color: props.isHighlighted ? "var(--ou-link, #4338ca)" : undefined,
141
141
  }}
142
142
  >
143
143
  <BarLabel label={props.bar.label} />
@@ -72,7 +72,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
72
72
  gap: "8px",
73
73
  paddingBottom: "10px",
74
74
  marginBottom: "10px",
75
- borderBottom: "1px solid #f0f0f0",
75
+ borderBottom: "1px solid var(--ou-border-subtle, #f0f0f0)",
76
76
  }}
77
77
  >
78
78
  <svg
@@ -80,7 +80,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
80
80
  height="14"
81
81
  viewBox="0 0 24 24"
82
82
  fill="none"
83
- stroke="#9ca3af"
83
+ stroke="var(--ou-text-subtle, #9ca3af)"
84
84
  strokeWidth="2"
85
85
  strokeLinecap="round"
86
86
  strokeLinejoin="round"
@@ -95,7 +95,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
95
95
  style={{
96
96
  fontWeight: 600,
97
97
  fontSize: "13px",
98
- color: "#111827",
98
+ color: "var(--ou-text-primary, #111827)",
99
99
  letterSpacing: "-0.01em",
100
100
  }}
101
101
  >
@@ -121,7 +121,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
121
121
  <span
122
122
  style={{
123
123
  fontSize: "11px",
124
- color: "#6b7280",
124
+ color: "var(--ou-text-muted, #6b7280)",
125
125
  fontWeight: 500,
126
126
  }}
127
127
  >
@@ -165,7 +165,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
165
165
  overflow: "hidden",
166
166
  display: "flex",
167
167
  gap: "1px",
168
- backgroundColor: "#e5e7eb",
168
+ backgroundColor: "var(--ou-border-default, #e5e7eb)",
169
169
  }}
170
170
  >
171
171
  {sortedDurations.map((status: StatusDuration, index: number) => {
@@ -191,7 +191,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
191
191
  style={{
192
192
  width: "100%",
193
193
  height: "4px",
194
- backgroundColor: "#e5e7eb",
194
+ backgroundColor: "var(--ou-border-default, #e5e7eb)",
195
195
  borderRadius: "100px",
196
196
  overflow: "hidden",
197
197
  }}
@@ -213,7 +213,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
213
213
  {!props.hasEvents && (
214
214
  <div
215
215
  style={{
216
- backgroundColor: "#f9fafb",
216
+ backgroundColor: "var(--ou-surface-secondary, #f9fafb)",
217
217
  borderRadius: "8px",
218
218
  padding: "16px",
219
219
  textAlign: "center",
@@ -224,7 +224,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
224
224
  height="18"
225
225
  viewBox="0 0 24 24"
226
226
  fill="none"
227
- stroke="#d1d5db"
227
+ stroke="var(--ou-border-strong, #d1d5db)"
228
228
  strokeWidth="1.5"
229
229
  strokeLinecap="round"
230
230
  strokeLinejoin="round"
@@ -237,7 +237,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
237
237
  <div
238
238
  style={{
239
239
  fontSize: "12px",
240
- color: "#9ca3af",
240
+ color: "var(--ou-text-subtle, #9ca3af)",
241
241
  fontWeight: 500,
242
242
  }}
243
243
  >
@@ -252,7 +252,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
252
252
  style={{
253
253
  paddingBottom: hasIncidents ? "10px" : "0",
254
254
  marginBottom: hasIncidents ? "10px" : "0",
255
- borderBottom: hasIncidents ? "1px solid #f0f0f0" : "none",
255
+ borderBottom: hasIncidents
256
+ ? "1px solid var(--ou-border-subtle, #f0f0f0)"
257
+ : "none",
256
258
  }}
257
259
  >
258
260
  {sortedDurations.map((status: StatusDuration, index: number) => {
@@ -291,7 +293,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
291
293
  <span
292
294
  style={{
293
295
  fontSize: "12px",
294
- color: "#374151",
296
+ color: "var(--ou-text-secondary, #374151)",
295
297
  fontWeight: 500,
296
298
  whiteSpace: "nowrap",
297
299
  overflow: "hidden",
@@ -306,7 +308,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
306
308
  style={{
307
309
  flex: 1,
308
310
  height: "4px",
309
- backgroundColor: "#f3f4f6",
311
+ backgroundColor: "var(--ou-surface-tertiary, #f3f4f6)",
310
312
  borderRadius: "100px",
311
313
  overflow: "hidden",
312
314
  }}
@@ -325,7 +327,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
325
327
  <span
326
328
  style={{
327
329
  fontSize: "11px",
328
- color: status.isDowntime ? "#dc2626" : "#6b7280",
330
+ color: status.isDowntime
331
+ ? "var(--ou-danger-text, #b91c1c)"
332
+ : "var(--ou-text-muted, #6b7280)",
329
333
  fontWeight: status.isDowntime ? 600 : 400,
330
334
  fontVariantNumeric: "tabular-nums",
331
335
  whiteSpace: "nowrap",
@@ -356,7 +360,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
356
360
  <span
357
361
  style={{
358
362
  fontSize: "11px",
359
- color: "#6b7280",
363
+ color: "var(--ou-text-muted, #6b7280)",
360
364
  fontWeight: 500,
361
365
  flex: 1,
362
366
  }}
@@ -367,8 +371,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
367
371
  style={{
368
372
  fontSize: "10px",
369
373
  fontWeight: 600,
370
- color: "#dc2626",
371
- backgroundColor: "#fef2f2",
374
+ color: "var(--ou-danger-text, #b91c1c)",
375
+ backgroundColor:
376
+ "color-mix(in srgb, #ef4444 14%, var(--ou-surface-primary, #ffffff))",
372
377
  padding: "1px 7px",
373
378
  borderRadius: "9999px",
374
379
  lineHeight: "1.6",
@@ -400,21 +405,23 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
400
405
  marginBottom: "6px",
401
406
  cursor: isClickable ? "pointer" : "default",
402
407
  transition: "background-color 0.12s ease",
403
- backgroundColor: "#fafafa",
408
+ backgroundColor: "var(--ou-surface-secondary, #fafafa)",
404
409
  borderRadius: "0 6px 6px 0",
405
410
  }}
406
411
  onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {
407
412
  if (isClickable) {
408
413
  (
409
414
  e.currentTarget as HTMLDivElement
410
- ).style.backgroundColor = "#f3f4f6";
415
+ ).style.backgroundColor =
416
+ "var(--ou-surface-tertiary, #f3f4f6)";
411
417
  }
412
418
  }}
413
419
  onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
414
420
  if (isClickable) {
415
421
  (
416
422
  e.currentTarget as HTMLDivElement
417
- ).style.backgroundColor = "#fafafa";
423
+ ).style.backgroundColor =
424
+ "var(--ou-surface-secondary, #fafafa)";
418
425
  }
419
426
  }}
420
427
  >
@@ -431,7 +438,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
431
438
  flex: 1,
432
439
  minWidth: 0,
433
440
  fontSize: "12px",
434
- color: "#111827",
441
+ color: "var(--ou-text-primary, #111827)",
435
442
  fontWeight: 600,
436
443
  lineHeight: "1.3",
437
444
  overflow: "hidden",
@@ -451,7 +458,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
451
458
  >
452
459
  <path
453
460
  d="M6 3l5 5-5 5"
454
- stroke="#6b7280"
461
+ stroke="var(--ou-text-muted, #6b7280)"
455
462
  strokeWidth="1.5"
456
463
  strokeLinecap="round"
457
464
  strokeLinejoin="round"
@@ -509,7 +516,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
509
516
  <span
510
517
  style={{
511
518
  fontSize: "10px",
512
- color: "#b0b0b0",
519
+ color: "var(--ou-text-subtle, #b0b0b0)",
513
520
  marginLeft: "auto",
514
521
  whiteSpace: "nowrap",
515
522
  }}
@@ -528,7 +535,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
528
535
  <div
529
536
  style={{
530
537
  fontSize: "11px",
531
- color: "#9ca3af",
538
+ color: "var(--ou-text-subtle, #9ca3af)",
532
539
  textAlign: "center",
533
540
  padding: "4px 0 0",
534
541
  fontWeight: 500,
@@ -3,6 +3,7 @@ import React, { FunctionComponent, ReactElement } from "react";
3
3
  export interface ComponentProps {
4
4
  leftComponents?: undefined | Array<ReactElement> | ReactElement;
5
5
  rightComponents?: undefined | Array<ReactElement> | ReactElement;
6
+ mobileRightComponents?: undefined | Array<ReactElement> | ReactElement;
6
7
  centerComponents?: undefined | Array<ReactElement> | ReactElement;
7
8
  className?: string | undefined;
8
9
  }
@@ -28,6 +29,12 @@ const Header: FunctionComponent<ComponentProps> = (
28
29
  </div>
29
30
  )}
30
31
 
32
+ {props.mobileRightComponents && (
33
+ <div className="relative z-20 flex items-center lg:hidden">
34
+ {props.mobileRightComponents}
35
+ </div>
36
+ )}
37
+
31
38
  <div className="hidden lg:relative lg:z-10 lg:ml-4 lg:flex lg:items-center lg:gap-2">
32
39
  {props.rightComponents}
33
40
  </div>