rift-ds 1.14.0 → 1.16.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 (284) hide show
  1. package/README.md +4 -4
  2. package/behaviors/LinkProvider.d.ts +3 -0
  3. package/behaviors/LocaleProvider.d.ts +27 -0
  4. package/behaviors/LocaleProvider.js +16 -0
  5. package/behaviors/direction.css +19 -0
  6. package/behaviors/direction.d.ts +40 -0
  7. package/behaviors/direction.js +56 -0
  8. package/behaviors/focusable.d.ts +2 -1
  9. package/behaviors/locale.d.ts +47 -0
  10. package/behaviors/locale.js +87 -0
  11. package/behaviors/shortcut-keys.d.ts +49 -0
  12. package/behaviors/shortcut-keys.js +129 -0
  13. package/behaviors/shortcuts.d.ts +75 -0
  14. package/behaviors/shortcuts.js +160 -0
  15. package/behaviors/useAnchoredPosition.d.ts +17 -4
  16. package/behaviors/useAnchoredPosition.js +8 -3
  17. package/behaviors/useFocusScope.d.ts +6 -0
  18. package/behaviors/useFocusScope.js +2 -0
  19. package/behaviors/useLayer.d.ts +9 -0
  20. package/behaviors/useLayer.js +4 -0
  21. package/behaviors/useScrollLock.d.ts +4 -0
  22. package/bin/rift-ds.mjs +3 -0
  23. package/chat/ChatWidget.css +24 -24
  24. package/chat/ChatWidget.js +4 -2
  25. package/chat/suggestions.d.ts +12 -0
  26. package/components/Accordion/Accordion.css +1 -1
  27. package/components/AgentPlan/AgentPlan.css +2 -2
  28. package/components/AgentRail/AgentRail.css +4 -4
  29. package/components/AgentRail/AgentRail.js +1 -1
  30. package/components/AgentStatus/AgentStatus.css +1 -1
  31. package/components/AiButton/AiButton.css +4 -4
  32. package/components/AiButton/AiButton.js +2 -1
  33. package/components/AlertDialog/AlertDialog.css +1 -1
  34. package/components/AnchorNav/AnchorNav.css +6 -6
  35. package/components/AnchorNav/AnchorNav.js +4 -2
  36. package/components/AnimatedNumber/AnimatedNumber.d.ts +6 -0
  37. package/components/AnimatedNumber/AnimatedNumber.js +12 -5
  38. package/components/AppLayout/AppLayout.css +3 -3
  39. package/components/AppSidebar/AppSidebar.css +20 -20
  40. package/components/AppSidebar/AppSidebar.js +6 -4
  41. package/components/ArtifactCard/ArtifactCard.css +1 -1
  42. package/components/ArtifactCard/ArtifactCard.js +2 -1
  43. package/components/ArtifactPanel/ArtifactPanel.js +2 -1
  44. package/components/Attachment/Attachment.css +4 -4
  45. package/components/Attachment/AttachmentDropZone.js +2 -1
  46. package/components/Attachment/AttachmentGroup.d.ts +2 -0
  47. package/components/Attachment/AttachmentGroup.js +4 -1
  48. package/components/Attachment/AttachmentViewer.d.ts +2 -0
  49. package/components/Attachment/AttachmentViewer.js +8 -13
  50. package/components/Attachment/fileTypes.d.ts +5 -3
  51. package/components/Attachment/fileTypes.js +8 -3
  52. package/components/Avatar/Avatar.css +1 -1
  53. package/components/AvatarGroup/AvatarGroup.css +1 -1
  54. package/components/BentoTile/BentoTile.css +15 -3
  55. package/components/BottomNav/BottomNav.css +6 -6
  56. package/components/BottomNav/BottomNav.js +3 -1
  57. package/components/Breadcrumb/Breadcrumb.js +2 -2
  58. package/components/Button/Button.css +1 -1
  59. package/components/Button/Button.js +4 -2
  60. package/components/Card/Card.css +3 -3
  61. package/components/CardStack/CardStack.js +4 -2
  62. package/components/Carousel/Carousel.css +5 -2
  63. package/components/Carousel/Carousel.js +11 -7
  64. package/components/Chart/AreaChart.d.ts +2 -0
  65. package/components/Chart/AreaChart.js +15 -6
  66. package/components/Chart/BarChart.d.ts +2 -0
  67. package/components/Chart/BarChart.js +15 -5
  68. package/components/Chart/Chart.css +6 -6
  69. package/components/Chart/ComboChart.d.ts +2 -0
  70. package/components/Chart/ComboChart.js +16 -6
  71. package/components/Chart/LineChart.d.ts +2 -0
  72. package/components/Chart/LineChart.js +15 -6
  73. package/components/Chart/PieChart.d.ts +2 -0
  74. package/components/Chart/PieChart.js +9 -5
  75. package/components/Chart/RadarChart.d.ts +2 -0
  76. package/components/Chart/RadarChart.js +15 -6
  77. package/components/Chart/RadialChart.d.ts +2 -0
  78. package/components/Chart/RadialChart.js +9 -5
  79. package/components/Chart/ScatterChart.d.ts +2 -0
  80. package/components/Chart/ScatterChart.js +17 -7
  81. package/components/Chart/StackedBarChart.d.ts +2 -0
  82. package/components/Chart/StackedBarChart.js +15 -7
  83. package/components/Chart/Treemap.d.ts +2 -0
  84. package/components/Chart/Treemap.js +9 -5
  85. package/components/ChatMarker/ChatMarker.js +2 -1
  86. package/components/ChatMessage/ChatMessage.css +2 -2
  87. package/components/ChatThread/ChatThread.css +1 -1
  88. package/components/Checkbox/Checkbox.css +4 -2
  89. package/components/CircularButton/CircularButton.d.ts +2 -2
  90. package/components/CircularButton/CircularButton.js +2 -1
  91. package/components/CodeDiff/CodeDiff.css +2 -2
  92. package/components/ColorPicker/ColorPicker.css +5 -4
  93. package/components/ColourSwatch/ColourSwatch.css +1 -1
  94. package/components/CommandPalette/CommandPalette.d.ts +13 -1
  95. package/components/CommandPalette/CommandPalette.js +12 -13
  96. package/components/CommandPaletteTrigger/CommandPaletteTrigger.css +1 -1
  97. package/components/CommandPaletteTrigger/CommandPaletteTrigger.d.ts +11 -1
  98. package/components/CommandPaletteTrigger/CommandPaletteTrigger.js +14 -5
  99. package/components/Composer/Composer.css +4 -4
  100. package/components/Composer/Composer.d.ts +2 -0
  101. package/components/Composer/Composer.js +6 -2
  102. package/components/ContactCard/ContactCard.css +1 -1
  103. package/components/ContactCard/ContactCard.js +4 -3
  104. package/components/ContextMenu/ContextMenu.css +10 -7
  105. package/components/ContextMenu/ContextMenu.d.ts +3 -0
  106. package/components/ContextMenu/ContextMenu.js +15 -9
  107. package/components/ContributionGraph/ContributionGraph.css +2 -2
  108. package/components/ContributionGraph/ContributionGraph.d.ts +7 -1
  109. package/components/ContributionGraph/ContributionGraph.js +6 -4
  110. package/components/DataTable/DataTable.css +1 -1
  111. package/components/DataTable/DataTable.d.ts +9 -3
  112. package/components/DataTable/DataTable.js +39 -29
  113. package/components/DataTable/columnTypes.d.ts +2 -2
  114. package/components/DataTable/columnTypes.js +9 -9
  115. package/components/DateInput/DateInput.css +4 -4
  116. package/components/DatePicker/DatePicker.d.ts +23 -7
  117. package/components/DatePicker/DatePicker.js +41 -34
  118. package/components/Dialog/Dialog.css +1 -1
  119. package/components/DocumentChip/DocumentChip.css +1 -1
  120. package/components/DocumentChip/DocumentChip.js +2 -1
  121. package/components/Drawer/Drawer.css +46 -12
  122. package/components/Drawer/Drawer.d.ts +12 -2
  123. package/components/Drawer/Drawer.js +12 -1
  124. package/components/Dropdown/ChosenMark.css +1 -1
  125. package/components/Dropdown/Dropdown.css +1 -1
  126. package/components/Dropdown/Dropdown.js +4 -3
  127. package/components/DropdownMenu/DropdownMenu.css +8 -5
  128. package/components/DropdownMenu/DropdownMenu.d.ts +6 -1
  129. package/components/DropdownMenu/DropdownMenu.js +15 -9
  130. package/components/DropdownMenu/ShortcutHint.d.ts +12 -0
  131. package/components/DropdownMenu/ShortcutHint.js +17 -0
  132. package/components/EmptyState/EmptyState.js +2 -1
  133. package/components/EntityCard/EntityCard.js +2 -1
  134. package/components/EventCalendar/EventCalendar.css +1 -1
  135. package/components/EventCalendar/EventCalendar.d.ts +21 -6
  136. package/components/EventCalendar/EventCalendar.js +33 -31
  137. package/components/FileInput/FileInput.d.ts +2 -0
  138. package/components/FileInput/FileInput.js +4 -1
  139. package/components/FilterBar/FilterBar.js +2 -1
  140. package/components/FunnelChart/FunnelChart.d.ts +2 -0
  141. package/components/FunnelChart/FunnelChart.js +11 -7
  142. package/components/GanttChart/GanttChart.css +1 -0
  143. package/components/GanttChart/GanttChart.js +2 -1
  144. package/components/Gauge/Gauge.css +1 -1
  145. package/components/Gauge/Gauge.d.ts +2 -0
  146. package/components/Gauge/Gauge.js +3 -1
  147. package/components/Globe/Globe.css +1 -0
  148. package/components/HoverCard/HoverCard.css +1 -0
  149. package/components/HoverCard/HoverCard.d.ts +2 -2
  150. package/components/ImageCompare/ImageCompare.css +1 -0
  151. package/components/Input/Input.css +8 -8
  152. package/components/Input/Input.js +3 -2
  153. package/components/Inspector/Inspector.css +12 -11
  154. package/components/Inspector/InspectorSlider.d.ts +2 -0
  155. package/components/Inspector/InspectorSlider.js +8 -1
  156. package/components/Instructions/Instructions.css +1 -1
  157. package/components/Instructions/Instructions.js +2 -1
  158. package/components/InterruptCard/InterruptCard.css +1 -1
  159. package/components/InterruptCard/InterruptCard.js +2 -1
  160. package/components/Kbd/Kbd.css +21 -0
  161. package/components/Kbd/Kbd.d.ts +14 -4
  162. package/components/Kbd/Kbd.js +10 -1
  163. package/components/LegendTile/LegendTile.d.ts +6 -1
  164. package/components/LegendTile/LegendTile.js +3 -2
  165. package/components/Lightbox/Lightbox.css +3 -3
  166. package/components/Lightbox/Lightbox.js +6 -14
  167. package/components/LinkList/LinkList.js +3 -2
  168. package/components/MapCallout/MapCallout.css +1 -0
  169. package/components/MegaMenu/MegaMenu.css +271 -0
  170. package/components/MegaMenu/MegaMenu.d.ts +72 -0
  171. package/components/MegaMenu/MegaMenu.js +71 -0
  172. package/components/Mention/Mention.js +2 -1
  173. package/components/MessageActions/MessageActions.js +2 -1
  174. package/components/MessageCard/MessageCard.css +1 -1
  175. package/components/MessageCard/MessageCard.js +2 -1
  176. package/components/Meter/Meter.css +1 -1
  177. package/components/Meter/Meter.d.ts +2 -0
  178. package/components/Meter/Meter.js +3 -1
  179. package/components/MotionSwatch/MotionSwatch.css +3 -3
  180. package/components/Nav/Nav.d.ts +7 -2
  181. package/components/NavList/NavList.css +3 -3
  182. package/components/NotificationCenter/NotificationCenter.d.ts +2 -0
  183. package/components/NotificationCenter/NotificationCenter.js +7 -2
  184. package/components/Pagination/Pagination.d.ts +2 -0
  185. package/components/Pagination/Pagination.js +6 -2
  186. package/components/PinInput/PinInput.js +3 -2
  187. package/components/Popover/Popover.d.ts +2 -2
  188. package/components/ProgressBar/ProgressBar.css +1 -1
  189. package/components/ProgressBar/ProgressBar.d.ts +2 -0
  190. package/components/ProgressBar/ProgressBar.js +3 -5
  191. package/components/ProgressiveBlur/ProgressiveBlur.css +102 -0
  192. package/components/ProgressiveBlur/ProgressiveBlur.d.ts +30 -0
  193. package/components/ProgressiveBlur/ProgressiveBlur.js +27 -0
  194. package/components/PromptSuggestions/PromptSuggestions.css +11 -4
  195. package/components/PromptSuggestions/PromptSuggestions.js +3 -1
  196. package/components/Prose/Prose.css +3 -0
  197. package/components/QuestionCard/QuestionCard.css +4 -3
  198. package/components/QuestionCard/QuestionCard.js +2 -8
  199. package/components/Quote/Quote.css +4 -4
  200. package/components/RadioButton/RadioButton.css +5 -3
  201. package/components/Rating/Rating.js +4 -3
  202. package/components/Reasoning/Reasoning.css +8 -8
  203. package/components/Reasoning/Reasoning.js +1 -0
  204. package/components/RichDropdown/RichDropdown.css +4 -3
  205. package/components/SegmentedControl/SegmentedControl.css +1 -1
  206. package/components/SegmentedControl/SegmentedControl.js +5 -3
  207. package/components/SidebarPanel/SidebarPanel.css +7 -7
  208. package/components/SidebarPanel/SidebarPanel.js +2 -2
  209. package/components/SiteHeader/SiteHeader.css +305 -0
  210. package/components/SiteHeader/SiteHeader.d.ts +117 -0
  211. package/components/SiteHeader/SiteHeader.js +329 -0
  212. package/components/Skeleton/Skeleton.css +2 -2
  213. package/components/Skeleton/Skeleton.js +1 -0
  214. package/components/Slider/Slider.css +16 -1
  215. package/components/Slider/Slider.js +1 -1
  216. package/components/SourceChip/SourceChip.d.ts +2 -0
  217. package/components/SourceChip/SourceChip.js +6 -2
  218. package/components/SourceTrail/SourceTrail.css +1 -1
  219. package/components/SpacingSwatch/SpacingSwatch.css +3 -3
  220. package/components/SplitButton/SplitButton.css +2 -2
  221. package/components/SplitButton/SplitButton.js +1 -0
  222. package/components/SplitPane/SplitPane.js +3 -2
  223. package/components/Stat/Stat.css +1 -1
  224. package/components/Stat/Stat.d.ts +2 -0
  225. package/components/Stat/Stat.js +3 -1
  226. package/components/Stepper/Stepper.css +5 -5
  227. package/components/Stepper/Stepper.d.ts +2 -0
  228. package/components/Stepper/Stepper.js +5 -1
  229. package/components/StreamingText/StreamingText.css +1 -1
  230. package/components/Table/Table.css +2 -2
  231. package/components/Table/Table.d.ts +2 -2
  232. package/components/Table/Table.js +4 -2
  233. package/components/TableToolbar/TableToolbar.js +2 -1
  234. package/components/Tabs/Tabs.js +3 -2
  235. package/components/ThreadPanel/ThreadPanel.css +19 -5
  236. package/components/ThreadPanel/ThreadPanel.d.ts +6 -1
  237. package/components/ThreadPanel/ThreadPanel.js +11 -7
  238. package/components/ThreadTabs/ThreadTabs.css +8 -1
  239. package/components/ThreadTabs/ThreadTabs.js +4 -2
  240. package/components/TimePicker/TimePicker.css +1 -1
  241. package/components/TimePicker/TimePicker.d.ts +17 -4
  242. package/components/TimePicker/TimePicker.js +21 -3
  243. package/components/Timeline/Timeline.css +8 -8
  244. package/components/Timeline/Timeline.d.ts +2 -0
  245. package/components/Timeline/Timeline.js +5 -2
  246. package/components/Toast/Toast.css +5 -5
  247. package/components/Toast/Toast.js +3 -2
  248. package/components/ToggleGroup/ToggleGroup.css +1 -1
  249. package/components/ToggleSwitch/ToggleSwitch.css +5 -5
  250. package/components/ToggleSwitch/ToggleSwitch.js +1 -0
  251. package/components/ToolCall/ToolCall.css +3 -3
  252. package/components/Toolbar/Toolbar.js +4 -2
  253. package/components/Tooltip/Tooltip.css +1 -0
  254. package/components/Tooltip/Tooltip.d.ts +2 -2
  255. package/components/TopAppBar/TopAppBar.css +2 -2
  256. package/components/TopAppBar/TopAppBar.js +3 -1
  257. package/components/TreeView/TreeView.css +1 -1
  258. package/components/TreeView/TreeView.js +4 -3
  259. package/components/UsageCard/UsageCard.d.ts +2 -0
  260. package/components/UsageCard/UsageCard.js +3 -0
  261. package/components/Widget/Widget.css +1 -1
  262. package/components/Widget/Widget.d.ts +2 -0
  263. package/components/Widget/Widget.js +3 -2
  264. package/components/WorldMap/WorldMap.css +1 -0
  265. package/components/registry.json +26 -2
  266. package/components/registry.json.d.ts +26 -2
  267. package/components/registry.json.js +1 -1
  268. package/fonts/material-symbols.css +13 -1
  269. package/fonts/presets/anton-400-latin-ext.woff2 +0 -0
  270. package/fonts/presets/anton-400-latin.woff2 +0 -0
  271. package/fonts/presets/work-sans-300-700-latin-ext.woff2 +0 -0
  272. package/fonts/presets/work-sans-300-700-latin.woff2 +0 -0
  273. package/index.d.ts +4 -0
  274. package/index.js +9 -0
  275. package/package.json +9 -1
  276. package/tokens/motion.d.ts +2 -0
  277. package/tokens/motion.js +2 -0
  278. package/tokens/presets/marquee.css +299 -0
  279. package/tokens/presets/presets.css +1 -0
  280. package/tokens/registry.json +6 -0
  281. package/tokens/registry.json.d.ts +6 -0
  282. package/tokens/registry.json.js +1 -1
  283. package/tokens/tokens-dark.css +18 -0
  284. package/tokens/tokens-light.css +18 -0
@@ -0,0 +1,160 @@
1
+ "use client";
2
+ import { useSyncExternalStore, useRef, useEffect } from "react";
3
+ import { MOTION_SHORTCUT_SEQUENCE_MS } from "../tokens/motion.js";
4
+ import { warnOnce } from "./devWarning.js";
5
+ import { activeModalPanel } from "./useFocusScope.js";
6
+ import { hasOpenLayer, openLayerContains } from "./useLayer.js";
7
+ import { detectPlatform, parseShortcut, KEY_ALIASES } from "./shortcut-keys.js";
8
+ import { formatShortcut, shortcutAria, shortcutKeys } from "./shortcut-keys.js";
9
+ const noSubscription = () => () => {
10
+ };
11
+ function usePlatform() {
12
+ return useSyncExternalStore(noSubscription, detectPlatform, () => "other");
13
+ }
14
+ const eventKey = (event) => {
15
+ const key = event.key.toLowerCase();
16
+ return KEY_ALIASES[key] ?? key;
17
+ };
18
+ const codeKey = (event) => {
19
+ const match = /^(?:Key([A-Z])|Digit(\d))$/.exec(event.code ?? "");
20
+ return match ? (match[1] ?? match[2]).toLowerCase() : null;
21
+ };
22
+ const matchesStep = (step, event, platform) => {
23
+ const ctrl = step.ctrl || step.mod && platform === "other";
24
+ const meta = step.meta || step.mod && platform === "apple";
25
+ if (event.ctrlKey !== ctrl || event.metaKey !== meta || event.altKey !== step.alt) return false;
26
+ const symbol = step.key.length === 1 && !/[a-z0-9]/.test(step.key);
27
+ if (!symbol && event.shiftKey !== step.shift) return false;
28
+ return eventKey(event) === step.key || codeKey(event) === step.key;
29
+ };
30
+ const isTextField = (target) => {
31
+ if (!(target instanceof HTMLElement)) return false;
32
+ if (target.isContentEditable) return true;
33
+ if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) return true;
34
+ if (target instanceof HTMLInputElement) {
35
+ return !["checkbox", "radio", "button", "submit", "reset", "range", "color", "file", "image"].includes(target.type);
36
+ }
37
+ return false;
38
+ };
39
+ const registrations = [];
40
+ let version = 0;
41
+ const listeners = /* @__PURE__ */ new Set();
42
+ const notify = () => {
43
+ version += 1;
44
+ for (const listener of listeners) listener();
45
+ };
46
+ let recent = [];
47
+ const eligible = (registration, target) => {
48
+ const options = registration.optionsRef.current;
49
+ if (options.enabled === false) return false;
50
+ if (isTextField(target) && !options.allowInInput) return false;
51
+ if (options.global) return true;
52
+ const modal = activeModalPanel();
53
+ const node = target instanceof Node ? target : null;
54
+ const unfocused = !node || node === document.body || node === document.documentElement;
55
+ const scope = options.scope?.current;
56
+ if (scope) {
57
+ if (!unfocused) return scope.contains(node);
58
+ return modal ? modal.contains(scope) : !openLayerContains(scope);
59
+ }
60
+ if (modal) return false;
61
+ return unfocused || !openLayerContains(node);
62
+ };
63
+ const handleKeyDown = (event) => {
64
+ if (event.isComposing || event.defaultPrevented) return;
65
+ if (event.key === "Escape" && hasOpenLayer()) return;
66
+ if (["Shift", "Control", "Alt", "Meta"].includes(event.key)) return;
67
+ const now = event.timeStamp || performance.now();
68
+ const last = recent[recent.length - 1];
69
+ if (last && now - last.at > MOTION_SHORTCUT_SEQUENCE_MS) recent = [];
70
+ recent = [...recent.slice(-3), { event, at: now }];
71
+ const platform = detectPlatform();
72
+ let best = null;
73
+ registrations.forEach((registration2, index) => {
74
+ if (!eligible(registration2, event.target)) return;
75
+ for (const steps of registration2.steps) {
76
+ if (steps.length > recent.length) continue;
77
+ const tail = recent.slice(recent.length - steps.length);
78
+ if (!steps.every((step, i) => matchesStep(step, tail[i].event, platform))) continue;
79
+ if (!best || steps.length > best.length || steps.length === best.length && index > best.index) {
80
+ best = { registration: registration2, length: steps.length, index };
81
+ }
82
+ }
83
+ });
84
+ if (!best) return;
85
+ const { registration } = best;
86
+ event.preventDefault();
87
+ recent = [];
88
+ registration.handlerRef.current(event);
89
+ };
90
+ const register = (registration) => {
91
+ for (const shortcut of registration.shortcuts) {
92
+ const scope = registration.optionsRef.current.scope;
93
+ const clash = registrations.find(
94
+ (other) => other.optionsRef.current.enabled !== false && other.optionsRef.current.scope === scope && other.shortcuts.includes(shortcut)
95
+ );
96
+ if (clash && registration.optionsRef.current.enabled !== false) {
97
+ warnOnce(`The shortcut "${shortcut}" is registered twice in one scope; the one registered last wins.`);
98
+ }
99
+ }
100
+ registrations.push(registration);
101
+ if (registrations.length === 1) document.addEventListener("keydown", handleKeyDown);
102
+ notify();
103
+ return () => {
104
+ const index = registrations.indexOf(registration);
105
+ if (index !== -1) registrations.splice(index, 1);
106
+ if (registrations.length === 0) {
107
+ document.removeEventListener("keydown", handleKeyDown);
108
+ recent = [];
109
+ }
110
+ notify();
111
+ };
112
+ };
113
+ function useShortcut(shortcut, handler, options = {}) {
114
+ const handlerRef = useRef(handler);
115
+ const optionsRef = useRef(options);
116
+ useEffect(() => {
117
+ handlerRef.current = handler;
118
+ optionsRef.current = options;
119
+ });
120
+ const shortcuts = Array.isArray(shortcut) ? shortcut : [shortcut];
121
+ const key = shortcuts.join("\0");
122
+ const enabled = options.enabled !== false;
123
+ useEffect(() => {
124
+ if (!enabled) return;
125
+ const list = key.split("\0");
126
+ return register({ shortcuts: list, steps: list.map(parseShortcut), handlerRef, optionsRef });
127
+ }, [key, enabled]);
128
+ }
129
+ const subscribe = (listener) => {
130
+ listeners.add(listener);
131
+ return () => listeners.delete(listener);
132
+ };
133
+ let snapshot = { version: -1, list: [] };
134
+ const readRegistered = () => {
135
+ if (snapshot.version !== version) {
136
+ snapshot = {
137
+ version,
138
+ list: registrations.filter((r) => r.optionsRef.current.enabled !== false && r.optionsRef.current.description).map((r) => ({
139
+ shortcuts: r.shortcuts,
140
+ description: r.optionsRef.current.description ?? "",
141
+ group: r.optionsRef.current.group
142
+ }))
143
+ };
144
+ }
145
+ return snapshot.list;
146
+ };
147
+ const EMPTY = [];
148
+ function useRegisteredShortcuts() {
149
+ return useSyncExternalStore(subscribe, readRegistered, () => EMPTY);
150
+ }
151
+ export {
152
+ detectPlatform,
153
+ formatShortcut,
154
+ parseShortcut,
155
+ shortcutAria,
156
+ shortcutKeys,
157
+ usePlatform,
158
+ useRegisteredShortcuts,
159
+ useShortcut
160
+ };
@@ -46,6 +46,8 @@
46
46
  * overflow does the room narrow to its box.
47
47
  */
48
48
  export type AnchoredSide = 'top' | 'bottom' | 'left' | 'right';
49
+ /** A side a caller can ask for: a screen side, or `start` / `end` of the reading direction. */
50
+ export type AnchoredSideRequest = AnchoredSide | 'start' | 'end';
49
51
  export type AnchoredAlign = 'start' | 'center' | 'end';
50
52
  export type AnchoredPlacement = {
51
53
  /** The side the panel took after flipping. */
@@ -71,9 +73,20 @@ export type AnchoredPositionOptions = {
71
73
  anchorRef: React.RefObject<HTMLElement | null>;
72
74
  /** The panel being placed. */
73
75
  panelRef: React.RefObject<HTMLElement | null>;
74
- /** The preferred side. Default `bottom`. */
75
- side?: AnchoredSide;
76
- /** Where along that side the panel lines up with the anchor. Default `start`. */
76
+ /**
77
+ * The preferred side. Default `bottom`. `start` and `end` resolve against
78
+ * the anchor's reading direction at placement time (`start` is the left
79
+ * in English, the right in Arabic); the placement reports the screen side
80
+ * it took.
81
+ */
82
+ side?: AnchoredSideRequest;
83
+ /**
84
+ * Where along that side the panel lines up with the anchor. Default
85
+ * `start`. On the top and bottom sides `start` and `end` follow the
86
+ * anchor's reading direction, so a menu lines up with its trigger's
87
+ * leading edge in either. The panel's `data-align` reports the screen
88
+ * alignment it took (`start` is the left edge), like `data-side`.
89
+ */
77
90
  align?: AnchoredAlign;
78
91
  /**
79
92
  * The custom property that holds the gap between anchor and panel, read
@@ -108,4 +121,4 @@ export type AnchoredPositionOptions = {
108
121
  */
109
122
  export declare function fixedContainingBlock(element: HTMLElement): HTMLElement | null;
110
123
  /** Places `panelRef` against `anchorRef` while `open`. See the module comment. */
111
- export declare function useAnchoredPosition({ open, anchorRef, panelRef, side, align, gapProperty, edgeProperty, flip, fitHeight, matchWidth, onPlace, target, }: AnchoredPositionOptions): void;
124
+ export declare function useAnchoredPosition({ open, anchorRef, panelRef, side: sideRequest, align: alignRequest, gapProperty, edgeProperty, flip, fitHeight, matchWidth, onPlace, target, }: AnchoredPositionOptions): void;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useRef, useLayoutEffect } from "react";
3
+ import { isRtl } from "./direction.js";
3
4
  function fixedContainingBlock(element) {
4
5
  for (let node = element.parentElement; node; node = node.parentElement) {
5
6
  const style = getComputedStyle(node);
@@ -21,8 +22,8 @@ function useAnchoredPosition({
21
22
  open,
22
23
  anchorRef,
23
24
  panelRef,
24
- side = "bottom",
25
- align = "start",
25
+ side: sideRequest = "bottom",
26
+ align: alignRequest = "start",
26
27
  gapProperty = "--gap-200",
27
28
  edgeProperty = "--gap-200",
28
29
  flip = true,
@@ -64,6 +65,10 @@ function useAnchoredPosition({
64
65
  bounds.bottom = Math.min(bounds.bottom, origin.top + block.clientHeight);
65
66
  }
66
67
  }
68
+ const rtl = isRtl(anchor);
69
+ const side = sideRequest === "start" ? rtl ? "right" : "left" : sideRequest === "end" ? rtl ? "left" : "right" : sideRequest;
70
+ const flipsAlign = rtl && (side === "top" || side === "bottom") && alignRequest !== "center";
71
+ const align = flipsAlign ? alignRequest === "start" ? "end" : "start" : alignRequest;
67
72
  const a = anchor.getBoundingClientRect();
68
73
  const panelStyle = getComputedStyle(panel);
69
74
  const gap = parseFloat(panelStyle.getPropertyValue(gapProperty)) || 0;
@@ -145,7 +150,7 @@ function useAnchoredPosition({
145
150
  panel.style.removeProperty("visibility");
146
151
  }
147
152
  };
148
- }, [open, target, anchorRef, panelRef, side, align, gapProperty, edgeProperty, flip, fitHeight, matchWidth]);
153
+ }, [open, target, anchorRef, panelRef, sideRequest, alignRequest, gapProperty, edgeProperty, flip, fitHeight, matchWidth]);
149
154
  }
150
155
  export {
151
156
  fixedContainingBlock,
@@ -47,5 +47,11 @@ type FocusScopeOptions = {
47
47
  /** Make everything outside the scope inert while active. Default true. */
48
48
  inertOutside?: boolean;
49
49
  };
50
+ /**
51
+ * The panel of the topmost modal scope that isolates the page, or null when
52
+ * none is open. Keyboard shortcuts read it: while a modal is open, only the
53
+ * shortcuts that live inside it (or are global) may fire.
54
+ */
55
+ export declare const activeModalPanel: () => HTMLElement | null;
50
56
  export declare function useFocusScope(ref: React.RefObject<HTMLElement | null>, options: FocusScopeOptions): void;
51
57
  export {};
@@ -23,6 +23,7 @@ const applyInert = () => {
23
23
  inerted.clear();
24
24
  }
25
25
  };
26
+ const activeModalPanel = () => inertScopes[inertScopes.length - 1]?.panel ?? null;
26
27
  const isVisible = (el) => el.getClientRects().length > 0;
27
28
  const restoreTarget = (previous) => {
28
29
  if (!document.contains(previous)) return null;
@@ -92,5 +93,6 @@ function useFocusScope(ref, options) {
92
93
  }, [active, ref]);
93
94
  }
94
95
  export {
96
+ activeModalPanel,
95
97
  useFocusScope
96
98
  };
@@ -58,6 +58,15 @@ export type LayerHandle = {
58
58
  */
59
59
  ownsPress: () => boolean;
60
60
  };
61
+ /**
62
+ * Whether `node` sits inside an open layer that names its elements: a
63
+ * popover, menu or listbox holding focus, or a modal panel. Keyboard
64
+ * shortcuts read it, so a page shortcut never fires while focus is in an
65
+ * overlay that has its own keys.
66
+ */
67
+ export declare const openLayerContains: (node: Node) => boolean;
68
+ /** Whether any layer is open. Escape belongs to the stack while one is, so keyboard shortcuts leave it alone. */
69
+ export declare const hasOpenLayer: () => boolean;
61
70
  /**
62
71
  * Register an overlay on the dismissal stack while `open` is true. Options
63
72
  * are read at event time, so a `dismissOnEscape` toggle takes effect without
@@ -4,6 +4,8 @@ const layers = [];
4
4
  let listenersInstalled = false;
5
5
  let pressOwner = null;
6
6
  const contains = (layer, target) => (layer.optionsRef.current.getElements?.() ?? []).some((el) => el?.contains(target));
7
+ const openLayerContains = (node) => layers.some((layer) => contains(layer, node));
8
+ const hasOpenLayer = () => layers.length > 0;
7
9
  const handleKeyDown = (event) => {
8
10
  if (event.key !== "Escape" || event.defaultPrevented) return;
9
11
  const top = layers[layers.length - 1];
@@ -63,5 +65,7 @@ function useLayer(options) {
63
65
  return { ownsPress };
64
66
  }
65
67
  export {
68
+ hasOpenLayer,
69
+ openLayerContains,
66
70
  useLayer
67
71
  };
@@ -31,6 +31,10 @@
31
31
  * swapping the modals onto it would change their scrollbar rendering.
32
32
  * Instances are independent — overlapping cancel-events locks stack
33
33
  * harmlessly.
34
+ *
35
+ * Published as the `./behaviors/useScrollLock` subpath so host chrome can
36
+ * join the counter; with LinkProvider it is one of only two behaviors that
37
+ * are. The rest of src/behaviors stays internal.
34
38
  */
35
39
  type ScrollLockOptions = {
36
40
  /** Which locking strategy to use. Default `'pin'`. */
package/bin/rift-ds.mjs CHANGED
@@ -42,6 +42,9 @@
42
42
  * the disk: a skill name is one kebab-case folder, a file is a plain
43
43
  * relative path, and nothing may climb out of the target folder.
44
44
  *
45
+ * A bin and never an exports subpath: nothing imports it, and build-package.mjs
46
+ * ships it as dist/bin/<BIN_NAME>.mjs.
47
+ *
45
48
  * Zero dependencies by design: global fetch plus node:fs, so npx never
46
49
  * installs anything beyond the package itself. SITE_ORIGIN, SKILL_NAME,
47
50
  * MCP_SERVER_NAME and PACKAGE_NAME are stamped by build-package.mjs from
@@ -189,7 +189,7 @@
189
189
  font-weight: var(--font-sub-display-weight);
190
190
  line-height: var(--font-sub-display-line-height);
191
191
  letter-spacing: var(--font-sub-display-letter-spacing);
192
- color: var(--color-text-primary);
192
+ color: var(--color-text-display);
193
193
  }
194
194
 
195
195
  /* The panel is a viewport of its own. tokens-typography.css collapses
@@ -562,7 +562,7 @@
562
562
  flex: none;
563
563
  display: flex;
564
564
  min-height: 0;
565
- border-right: var(--border-025) solid var(--color-bg-container-border);
565
+ border-inline-end: var(--border-025) solid var(--color-bg-container-border);
566
566
  }
567
567
 
568
568
  /* The sheet layer: hidden entirely (visibility) while closed, so nothing in
@@ -604,13 +604,13 @@
604
604
  position: absolute;
605
605
  top: 0;
606
606
  bottom: 0;
607
- left: 0;
607
+ inset-inline-start: 0;
608
608
  display: flex;
609
609
  background-color: var(--color-bg-glass);
610
610
  backdrop-filter: blur(24px);
611
- border-right: var(--border-025) solid var(--color-bg-container-border);
611
+ border-inline-end: var(--border-025) solid var(--color-bg-container-border);
612
612
  box-shadow: var(--shadow-floating);
613
- transform: translateX(-100%);
613
+ transform: translateX(calc(-100% * var(--ds-inline-sign, 1)));
614
614
  transition: transform var(--motion-duration-slow) var(--motion-ease-emphasized);
615
615
  }
616
616
 
@@ -655,7 +655,7 @@
655
655
  /* Before the first measurement there is no number to glide to, so the
656
656
  rail simply takes its content's width. */
657
657
  width: var(--ds-chat-aside-width, max-content);
658
- margin-right: var(--padding-500);
658
+ margin-inline-end: var(--padding-500);
659
659
  opacity: 1;
660
660
  visibility: visible;
661
661
  transition:
@@ -728,7 +728,7 @@
728
728
 
729
729
  .ds-chat-widget__artifact-rail--open {
730
730
  flex-grow: 1.2;
731
- margin-right: var(--padding-500);
731
+ margin-inline-end: var(--padding-500);
732
732
  opacity: 1;
733
733
  visibility: visible;
734
734
  transition:
@@ -798,7 +798,7 @@
798
798
  position: absolute;
799
799
  top: 0;
800
800
  bottom: 0;
801
- left: -10px;
801
+ inset-inline-start: -10px;
802
802
  width: 20px;
803
803
  }
804
804
 
@@ -806,7 +806,7 @@
806
806
  content: "";
807
807
  position: absolute;
808
808
  top: calc(50% - 24px);
809
- left: -2px;
809
+ inset-inline-start: -2px;
810
810
  width: 4px;
811
811
  height: 48px;
812
812
  border-radius: var(--radius-pill);
@@ -833,7 +833,7 @@
833
833
 
834
834
  .ds-chat-widget__rails[data-artifact-expanded] > .ds-chat-widget__artifact-rail--open {
835
835
  flex: 1 1 0;
836
- margin-left: var(--padding-500);
836
+ margin-inline-start: var(--padding-500);
837
837
  }
838
838
 
839
839
  /* The sheet runs nearly the widget's height: a preview has no natural
@@ -859,7 +859,7 @@
859
859
  .ds-chat-widget__rail-actions {
860
860
  position: absolute;
861
861
  top: calc(var(--padding-200) + var(--padding-150));
862
- right: var(--padding-500);
862
+ inset-inline-end: var(--padding-500);
863
863
  z-index: 6;
864
864
  display: flex;
865
865
  align-items: center;
@@ -925,7 +925,7 @@
925
925
  top: var(--ds-chat-inset);
926
926
  /* Placed from the left on both docks, so one property carries every move
927
927
  the card makes and they all glide. */
928
- left: calc(100% - var(--ds-chat-width) - var(--ds-chat-inset));
928
+ left: calc(100% - var(--ds-chat-width) - var(--ds-chat-inset)); /* ds-allow(physical): the panel is placed on a dock the visitor picks by screen edge (src/chat/placement.ts), not by reading direction */
929
929
  height: calc(100dvh - var(--ds-chat-inset) * 2);
930
930
  width: var(--ds-chat-width);
931
931
  max-width: calc(100vw - var(--ds-chat-inset) * 2);
@@ -992,7 +992,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
992
992
 
993
993
  /* The left dock: the same card on the other edge. */
994
994
  .ds-chat-launcher__panel--left {
995
- left: var(--ds-chat-inset);
995
+ left: var(--ds-chat-inset); /* ds-allow(physical): the left dock is the screen's left edge */
996
996
  }
997
997
 
998
998
  /* Fullscreen view: a true viewport takeover. The stronger blur keeps the
@@ -1000,7 +1000,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1000
1000
  border fades rather than vanishing, so the geometry never snaps mid-glide. */
1001
1001
  .ds-chat-launcher__panel--full {
1002
1002
  top: 0;
1003
- left: 0;
1003
+ left: 0; /* ds-allow(physical): fullscreen covers the viewport from its corner */
1004
1004
  height: 100dvh;
1005
1005
  width: 100vw;
1006
1006
  max-width: 100vw;
@@ -1032,7 +1032,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1032
1032
  position: fixed;
1033
1033
  top: var(--ds-chat-inset);
1034
1034
  height: calc(100dvh - var(--ds-chat-inset) * 2);
1035
- right: calc(var(--ds-chat-inset) + var(--ds-chat-width) - 10px);
1035
+ right: calc(var(--ds-chat-inset) + var(--ds-chat-width) - 10px); /* ds-allow(physical): the dock grip tracks the docked card's screen edge */
1036
1036
  width: 20px;
1037
1037
  display: flex;
1038
1038
  align-items: center;
@@ -1044,7 +1044,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1044
1044
 
1045
1045
  .ds-chat-launcher__dock-handle--left {
1046
1046
  right: auto;
1047
- left: calc(var(--ds-chat-inset) + var(--ds-chat-width) - 10px);
1047
+ left: calc(var(--ds-chat-inset) + var(--ds-chat-width) - 10px); /* ds-allow(physical): the dock grip tracks the docked card's screen edge */
1048
1048
  }
1049
1049
 
1050
1050
  .ds-chat-launcher__dock-handle::after {
@@ -1089,7 +1089,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1089
1089
  calc(100dvh - var(--ds-chat-inset) * 2)
1090
1090
  );
1091
1091
  top: var(--ds-chat-float-y, calc(100dvh - var(--ds-chat-float-h) - var(--ds-chat-inset)));
1092
- left: var(--ds-chat-float-x, calc(100% - var(--ds-chat-width) - var(--ds-chat-inset)));
1092
+ left: var(--ds-chat-float-x, calc(100% - var(--ds-chat-width) - var(--ds-chat-inset))); /* ds-allow(physical): the floating card sits where the visitor dragged it, in screen pixels */
1093
1093
  width: var(--ds-chat-float-w, var(--ds-chat-width));
1094
1094
  height: var(--ds-chat-float-h);
1095
1095
  max-height: 100dvh;
@@ -1125,8 +1125,8 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1125
1125
 
1126
1126
  .ds-chat-launcher__float-grip[data-dir="n"] { top: 0; }
1127
1127
  .ds-chat-launcher__float-grip[data-dir="s"] { bottom: 0; }
1128
- .ds-chat-launcher__float-grip[data-dir="e"] { right: 0; }
1129
- .ds-chat-launcher__float-grip[data-dir="w"] { left: 0; }
1128
+ .ds-chat-launcher__float-grip[data-dir="e"] { right: 0; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen edge */
1129
+ .ds-chat-launcher__float-grip[data-dir="w"] { left: 0; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen edge */
1130
1130
 
1131
1131
  .ds-chat-launcher__float-grip[data-dir="ne"],
1132
1132
  .ds-chat-launcher__float-grip[data-dir="nw"],
@@ -1136,10 +1136,10 @@ html[data-chat-resizing] .ds-chat-launcher__panel {
1136
1136
  height: 16px;
1137
1137
  }
1138
1138
 
1139
- .ds-chat-launcher__float-grip[data-dir="ne"] { top: 0; right: 0; cursor: nesw-resize; }
1140
- .ds-chat-launcher__float-grip[data-dir="sw"] { bottom: 0; left: 0; cursor: nesw-resize; }
1141
- .ds-chat-launcher__float-grip[data-dir="nw"] { top: 0; left: 0; cursor: nwse-resize; }
1142
- .ds-chat-launcher__float-grip[data-dir="se"] { bottom: 0; right: 0; cursor: nwse-resize; }
1139
+ .ds-chat-launcher__float-grip[data-dir="ne"] { top: 0; right: 0; cursor: nesw-resize; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen corner */
1140
+ .ds-chat-launcher__float-grip[data-dir="sw"] { bottom: 0; left: 0; cursor: nesw-resize; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen corner */
1141
+ .ds-chat-launcher__float-grip[data-dir="nw"] { top: 0; left: 0; cursor: nwse-resize; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen corner */
1142
+ .ds-chat-launcher__float-grip[data-dir="se"] { bottom: 0; right: 0; cursor: nwse-resize; } /* ds-allow(physical): a compass grip resizes the floating card toward that screen corner */
1143
1143
 
1144
1144
  .ds-chat-launcher__float-grip[data-dir="n"]::after,
1145
1145
  .ds-chat-launcher__float-grip[data-dir="s"]::after,
@@ -1209,7 +1209,7 @@ html[data-chat-resizing] .ds-chat-launcher__panel--floating .ds-chat-header {
1209
1209
 
1210
1210
  .ds-chat-launcher__fab {
1211
1211
  position: fixed;
1212
- right: var(--ds-chat-inset);
1212
+ right: var(--ds-chat-inset); /* ds-allow(physical): the launcher sits in the corner the default dock opens from, a screen corner like the docks */
1213
1213
  bottom: var(--ds-chat-inset);
1214
1214
  z-index: var(--layer-chat-launcher);
1215
1215
  }
@@ -32,6 +32,8 @@ import { ReplyBody } from "./TurnQuestion.js";
32
32
  import { ResponseActionsLabelsContext } from "./ResponseActions.js";
33
33
  import { FollowupSuggestionsLabelContext } from "./FollowupSuggestions.js";
34
34
  import "./ChatWidget.css";
35
+ import "../behaviors/direction.css";
36
+ import { inlineSign } from "../behaviors/direction.js";
35
37
  const DEFAULT_MAX_ATTACHMENTS = 10;
36
38
  const DEFAULT_MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
37
39
  const LONG_PASTE_CHARS = 1200;
@@ -287,14 +289,14 @@ function ChatWidget({
287
289
  if (!rail || !column) return;
288
290
  const width = rail.getBoundingClientRect().width;
289
291
  const spare = column.getBoundingClientRect().width - ARTIFACT_MIN_COLUMN_WIDTH;
290
- artifactDragRef.current = { startX: e.clientX, width, max: width + Math.max(0, spare), next: width };
292
+ artifactDragRef.current = { startX: e.clientX, width, max: width + Math.max(0, spare), next: width, sign: inlineSign(rail) };
291
293
  e.currentTarget.setPointerCapture(e.pointerId);
292
294
  setArtifactResizing(true);
293
295
  };
294
296
  const moveArtifactResize = (e) => {
295
297
  const drag = artifactDragRef.current;
296
298
  if (!drag) return;
297
- const grown = drag.width - (e.clientX - drag.startX);
299
+ const grown = drag.width - (e.clientX - drag.startX) * drag.sign;
298
300
  drag.next = Math.round(Math.min(drag.max, Math.max(ARTIFACT_MIN_COLUMN_WIDTH, grown)));
299
301
  railsHostRef.current?.style.setProperty("--ds-chat-artifact-width", `${drag.next}px`);
300
302
  };
@@ -16,6 +16,18 @@
16
16
  *
17
17
  * Every producer of a suggestion checks against it. A suggestion that will
18
18
  * not fit is dropped, never truncated: half a question is not a question.
19
+ *
20
+ * The FAB summary panel's chips render through the same row, so they share
21
+ * the budget too. It lives in the package rather than the site so every
22
+ * consumer's chat holds the same line. The enforcement points: the
23
+ * follow-ups model is told the number in its prompt (server.ts), the
24
+ * follow-ups route drops a long one rather than clipping it, and the
25
+ * options of a question the agent asks are held to it as well (the
26
+ * `ask_user` input schema tells the model the number, and
27
+ * `sanitizeQuestionSet` in questions.ts drops a question whose option
28
+ * outruns it). Written copy is a build check: validate-chat-starters.mjs
29
+ * holds the starters, follow-ups and the demo script's question options,
30
+ * and validate-page-summaries.mjs the summary panel's chips.
19
31
  */
20
32
  export declare const SUGGESTION_MAX_CHARS = 40;
21
33
  /** Whether a suggestion label is one the chip row can actually show. */
@@ -43,7 +43,7 @@
43
43
  background: none;
44
44
  border: none;
45
45
  cursor: pointer;
46
- text-align: left;
46
+ text-align: start;
47
47
  transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
48
48
  }
49
49
 
@@ -34,7 +34,7 @@
34
34
  padding: var(--padding-200) var(--padding-500);
35
35
  background: none;
36
36
  border: none;
37
- text-align: left;
37
+ text-align: start;
38
38
  cursor: pointer;
39
39
  transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
40
40
  }
@@ -141,7 +141,7 @@
141
141
  position: absolute;
142
142
  top: calc(var(--padding-100) + var(--icon-size-500));
143
143
  bottom: calc(-1 * var(--padding-100));
144
- left: calc(var(--icon-size-500) / 2);
144
+ inset-inline-start: calc(var(--icon-size-500) / 2);
145
145
  width: var(--border-025);
146
146
  background-color: var(--color-divider);
147
147
  }
@@ -53,7 +53,7 @@
53
53
  `--ds-agent-rail-collapse-top` is the seat, which a host overrides to
54
54
  line the chevron up with its own header row. */
55
55
  top: var(--ds-agent-rail-collapse-top, var(--padding-300));
56
- left: var(--padding-300);
56
+ inset-inline-start: var(--padding-300);
57
57
  z-index: 1;
58
58
  display: inline-flex;
59
59
  align-items: center;
@@ -77,8 +77,8 @@
77
77
  way out usually sits. The host that asks for it has decided its controls
78
78
  and this one do not collide. */
79
79
  .ds-agent-rail__collapse--close {
80
- left: auto;
81
- right: var(--padding-300);
80
+ inset-inline-start: auto;
81
+ inset-inline-end: var(--padding-300);
82
82
  }
83
83
 
84
84
  .ds-agent-rail__collapse .material-symbols-rounded {
@@ -146,7 +146,7 @@
146
146
  annotate. The offset puts the circle's centre roughly on the portrait's
147
147
  45-degree point, so it straddles the edge the way a badge should and the
148
148
  glyph underneath stays whole. */
149
- right: -6px;
149
+ inset-inline-end: -6px;
150
150
  bottom: -6px;
151
151
  display: inline-flex;
152
152
  align-items: center;
@@ -37,7 +37,7 @@ const AgentRail = React.forwardRef(
37
37
  className: `${baseClass}__collapse${collapseVariant === "close" ? ` ${baseClass}__collapse--close` : ""}`,
38
38
  "aria-label": collapseLabel,
39
39
  onClick: onCollapse,
40
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: collapseVariant === "close" ? "close" : "chevron_right" })
40
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded ds-mirror-rtl", "aria-hidden": "true", children: collapseVariant === "close" ? "close" : "chevron_right" })
41
41
  }
42
42
  ),
43
43
  profile && /* @__PURE__ */ jsxs("header", { className: `${baseClass}__header`, children: [
@@ -97,7 +97,7 @@
97
97
 
98
98
  .ds-agent-status__label-text--out {
99
99
  position: absolute;
100
- left: 0;
100
+ inset-inline-start: 0;
101
101
  top: 0;
102
102
  white-space: nowrap;
103
103
  animation: ds-agent-label-out var(--motion-duration-slow) var(--motion-ease-standard) both;
@@ -398,7 +398,7 @@
398
398
  .ds-ai-button__request {
399
399
  position: fixed;
400
400
  top: 0;
401
- left: 0;
401
+ left: 0; /* ds-allow(physical): closed, the panel rests at the viewport corner the anchored behaviour then places it from, in screen pixels */
402
402
  z-index: 1;
403
403
  /* Component-local lengths, chosen by eye like the button's glow: a
404
404
  readable measure, capped so a phone viewport keeps its margins. */
@@ -620,7 +620,7 @@
620
620
  .ds-ai-button__summary-dismiss {
621
621
  position: absolute;
622
622
  top: var(--gap-200);
623
- right: var(--gap-200);
623
+ inset-inline-end: var(--gap-200);
624
624
  display: inline-flex;
625
625
  align-items: center;
626
626
  justify-content: center;
@@ -649,7 +649,7 @@
649
649
  align-items: baseline;
650
650
  gap: var(--gap-200);
651
651
  /* Keeps the row clear of the dismiss button in the corner. */
652
- padding-right: var(--gap-1000);
652
+ padding-inline-end: var(--gap-1000);
653
653
  }
654
654
 
655
655
  .ds-ai-button__summary-overline {
@@ -672,7 +672,7 @@
672
672
 
673
673
  .ds-ai-button__summary-title {
674
674
  /* Long titles wrap short of the dismiss corner. */
675
- padding-right: var(--gap-500);
675
+ padding-inline-end: var(--gap-500);
676
676
  font-family: var(--font-family-primary);
677
677
  font-size: var(--font-title-body-size);
678
678
  font-weight: var(--font-title-body-weight);