@workbench-kit/react 0.0.2-prototype.0.2.4 → 0.0.2-prototype.0.2.41

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 (221) hide show
  1. package/README.md +172 -0
  2. package/package.json +38 -21
  3. package/src/index.ts +74 -34
  4. package/src/jdw/JsonCodeEditorPane.tsx +7 -6
  5. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +1 -3
  6. package/src/jdw/createBuiltinJdwRegistry.ts +1 -4
  7. package/src/jdw/cssRenderBackend.tsx +1 -4
  8. package/src/jdw/fixtures/jdw-fixtures.ts +0 -15
  9. package/src/json-config/JsonConfigWorkbench.tsx +1 -1
  10. package/src/layout/index.ts +11 -2
  11. package/src/layout/panel/Panel.tsx +0 -3
  12. package/src/layout/panel/index.ts +0 -2
  13. package/src/layout/sidebar/SideBarTree.tsx +301 -0
  14. package/src/layout/sidebar/SidebarActionIconBar.tsx +3 -3
  15. package/src/layout/sidebar/WorkbenchSidebarActions.tsx +1 -1
  16. package/src/layout/sidebar/index.ts +13 -0
  17. package/src/modal/Modal.tsx +55 -25
  18. package/src/modal/ModalTitlebar.tsx +8 -8
  19. package/src/modal/modalPosition.ts +3 -6
  20. package/src/modal/useModalFocusTrap.ts +142 -0
  21. package/src/modal/useModalWindowFrame.ts +2 -2
  22. package/src/overlay/ContextMenu.tsx +133 -21
  23. package/src/overlay/context-menu-density.css +22 -0
  24. package/src/overlay/context-menu-item.ts +17 -0
  25. package/src/overlay/context-menu.css +6 -2
  26. package/src/overlay/index.ts +6 -1
  27. package/src/overlay/measureAnchoredOverlayPanel.ts +4 -6
  28. package/src/overlay/pointerPassthroughRegion.ts +17 -1
  29. package/src/overlay/usePointerPassthroughRegion.ts +35 -6
  30. package/src/primitives/absolute-box/AbsoluteBox.tsx +4 -4
  31. package/src/primitives/app-icon/AppIcon.tsx +33 -0
  32. package/src/primitives/app-icon/app-icon.css +28 -0
  33. package/src/primitives/button/Button.tsx +4 -4
  34. package/src/primitives/catalog-browse-card/CatalogBrowseCard.tsx +6 -6
  35. package/src/primitives/catalog-browse-pane/CatalogBrowsePane.tsx +39 -32
  36. package/src/primitives/catalog-browse-pane/catalog-browse-pane.css +4 -0
  37. package/src/primitives/catalog-filter-overlay/CatalogFilterOverlay.tsx +2 -2
  38. package/src/primitives/checkbox/Checkbox.tsx +1 -1
  39. package/src/primitives/chip/Chip.tsx +3 -12
  40. package/src/primitives/chip/index.ts +2 -2
  41. package/src/primitives/codicon/Codicon.tsx +1 -1
  42. package/src/primitives/file-icon/FileIcon.tsx +1 -1
  43. package/src/primitives/icon-button/IconButton.tsx +1 -1
  44. package/src/primitives/index.ts +7 -2
  45. package/src/primitives/library-detail-layout/LibraryDetailLayout.tsx +123 -9
  46. package/src/primitives/library-detail-layout/library-detail-layout.css +57 -0
  47. package/src/primitives/library-detail-layout/resolve-hero-cover-media.ts +80 -0
  48. package/src/primitives/library-facet-filter-panel/LibraryFacetFilterPanel.tsx +12 -12
  49. package/src/primitives/library-facet-filter-strip/LibraryFacetFilterStrip.tsx +4 -4
  50. package/src/primitives/number-input/NumberInput.tsx +5 -5
  51. package/src/primitives/primitives.css +1 -0
  52. package/src/primitives/record-media-hero/RecordMediaHero.tsx +10 -6
  53. package/src/primitives/scroll-area/ScrollArea.tsx +5 -5
  54. package/src/primitives/scroll-area-infinite-load/useScrollAreaInfiniteLoad.ts +5 -5
  55. package/src/primitives/searchable-multi-select/SearchableMultiSelect.tsx +7 -7
  56. package/src/primitives/select/Select.tsx +1 -13
  57. package/src/primitives/text-area/TextArea.tsx +1 -1
  58. package/src/primitives/text-input/TextInput.tsx +1 -1
  59. package/src/primitives/workbench-media-slot/WorkbenchMediaPlaceholder.tsx +2 -2
  60. package/src/primitives/workbench-media-slot/WorkbenchMediaSlot.tsx +16 -10
  61. package/src/primitives/workbench-thumbnail/WorkbenchThumbnail.tsx +4 -4
  62. package/src/scrollbars.css +2 -6
  63. package/src/styles/core.css +16 -0
  64. package/src/styles/foundation.css +4 -0
  65. package/src/styles/overlay.css +2 -0
  66. package/src/styles.css +1 -15
  67. package/src/utils/clamp.ts +4 -0
  68. package/src/utils/normalizeKeyToken.ts +13 -0
  69. package/src/utils/readNumber.ts +4 -0
  70. package/src/widget-tree/WidgetSourceEditor.tsx +0 -14
  71. package/src/widget-tree/WidgetTreeCanvasPreview.tsx +3 -10
  72. package/src/widget-tree/WidgetTreeLab.tsx +2 -23
  73. package/src/widget-tree/WidgetTreeSidePanel.tsx +0 -5
  74. package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +2 -2
  75. package/src/widget-tree/index.ts +1 -5
  76. package/src/workbench/auth/WorkbenchLoginView.tsx +1 -2
  77. package/src/workbench/auth/auth-flows.css +14 -0
  78. package/src/workbench/chat/ChatCommandProposalCard.tsx +4 -4
  79. package/src/workbench/chat/ChatConversationBar.tsx +3 -3
  80. package/src/workbench/chat/ChatHistoryMenu.tsx +1 -1
  81. package/src/workbench/chat/ChatMessageCollapsible.tsx +4 -0
  82. package/src/workbench/chat/ChatMessageItem.tsx +68 -10
  83. package/src/workbench/chat/ChatMessageList.tsx +1 -1
  84. package/src/workbench/chat/ChatPanel.tsx +32 -23
  85. package/src/workbench/chat/ChatPhasedRunProgress.tsx +86 -13
  86. package/src/workbench/chat/ChatRenameDialog.tsx +1 -1
  87. package/src/workbench/chat/chat-conversation.css +2 -2
  88. package/src/workbench/chat/chat-ui.css +8 -0
  89. package/src/workbench/chat/chatConversation.ts +2 -2
  90. package/src/workbench/chat/chatMessageMeta.tsx +4 -4
  91. package/src/workbench/chat/index.ts +1 -0
  92. package/src/workbench/chat/types.ts +7 -7
  93. package/src/workbench/chat/useCancelRuntimeOnUnmount.ts +4 -4
  94. package/src/workbench/chat/useChatPrependPagination.ts +10 -10
  95. package/src/workbench/chat/useSlashCommandSuggest.ts +1 -1
  96. package/src/workbench/{WorkbenchModalPortal.tsx → chrome/WorkbenchModalPortal.tsx} +1 -1
  97. package/src/workbench/chrome/workbench-chrome.css +4 -0
  98. package/src/workbench/chrome/workbench-layout-regions.css +13 -0
  99. package/src/workbench/chrome/workbench-shell-titlebar.css +0 -8
  100. package/src/workbench/{CommandGroupShell.tsx → commands/CommandGroupShell.tsx} +2 -2
  101. package/src/workbench/{CommandList.tsx → commands/CommandList.tsx} +6 -5
  102. package/src/workbench/{CommandPalette.tsx → commands/CommandPalette.tsx} +16 -42
  103. package/src/workbench/{ConfirmationFlow.tsx → commands/ConfirmationFlow.tsx} +5 -5
  104. package/src/workbench/{ShortcutCommandBridge.tsx → commands/ShortcutCommandBridge.tsx} +17 -13
  105. package/src/workbench/commands/WorkbenchQuickOpen.tsx +462 -0
  106. package/src/workbench/{command-model.ts → commands/command-model.ts} +5 -1
  107. package/src/workbench/commands/command-ui.ts +3 -0
  108. package/src/workbench/{commands.ts → commands/commands.ts} +45 -1
  109. package/src/workbench/commands/createWorkspaceFilesQuickOpenProvider.ts +86 -0
  110. package/src/workbench/commands/quick-open-model.ts +66 -0
  111. package/src/workbench/{WorkbenchEditorTabs.tsx → editor/WorkbenchEditorTabs.tsx} +13 -3
  112. package/src/workbench/{editor-tabs-dnd.ts → editor/editor-tabs-dnd.ts} +1 -1
  113. package/src/workbench/{editorTabContextMenu.ts → editor/editorTabContextMenu.ts} +43 -4
  114. package/src/workbench/{useEditorTabsStripDnd.ts → editor/useEditorTabsStripDnd.ts} +1 -1
  115. package/src/workbench/{useWorkbenchEditorTabContextMenu.tsx → editor/useWorkbenchEditorTabContextMenu.tsx} +16 -2
  116. package/src/workbench/editor-tabs/index.ts +10 -9
  117. package/src/workbench/index.ts +114 -74
  118. package/src/workbench/management/CommandManagementSidebar.tsx +16 -30
  119. package/src/workbench/management/ExtensionManagementPanel.tsx +84 -46
  120. package/src/workbench/management/ExtensionManagementSidebar.tsx +79 -45
  121. package/src/workbench/management/IntegrationCommandActions.tsx +8 -1
  122. package/src/workbench/management/IntegrationSettingsSurface.tsx +2 -5
  123. package/src/workbench/management/IntegrationsShell.tsx +1 -1
  124. package/src/workbench/management/KeybindingCaptureField.tsx +1 -9
  125. package/src/workbench/management/WorkbenchDialogFrame.tsx +2 -2
  126. package/src/workbench/management/WorkbenchNotice.tsx +17 -12
  127. package/src/workbench/management/createWorkbenchNotify.tsx +88 -0
  128. package/src/workbench/management/extension-install-approval.ts +72 -0
  129. package/src/workbench/management/extension-management-filters.ts +38 -0
  130. package/src/workbench/management/index.ts +18 -3
  131. package/src/workbench/management/management.css +28 -0
  132. package/src/workbench/management/types.ts +23 -1
  133. package/src/workbench/management/useWorkbenchNotify.ts +10 -0
  134. package/src/workbench/{MarkdownPreview.tsx → markdown/MarkdownPreview.tsx} +22 -7
  135. package/src/workbench/markdown/markdownRemarkPlugins.ts +12 -0
  136. package/src/workbench/markdown/sanitizeMarkdownHref.ts +29 -0
  137. package/src/workbench/schema/workbenchDocumentRenderer.tsx +48 -121
  138. package/src/workbench/settings/StructuredDataForm.tsx +1 -4
  139. package/src/workbench/settings/StructuredDataSchemaPanel.tsx +39 -44
  140. package/src/workbench/settings/sectionedPanelScrollSpy.ts +0 -81
  141. package/src/workbench/settings/structuredDataFormModel.ts +2 -1
  142. package/src/workbench/settings/structuredDataSchema.ts +24 -13
  143. package/src/workbench/settings/structuredDataSchemaSection.ts +4 -0
  144. package/src/workbench/settings/structuredDataSchemaTable.ts +5 -2
  145. package/src/workbench/{ActivityBar.tsx → shell/ActivityBar.tsx} +8 -12
  146. package/src/workbench/{ArtifactShell.tsx → shell/ArtifactShell.tsx} +25 -25
  147. package/src/workbench/{SplitView.tsx → shell/SplitView.tsx} +3 -3
  148. package/src/workbench/{StatusBar.tsx → shell/StatusBar.tsx} +8 -8
  149. package/src/workbench/{StructuredArtifactEditor.tsx → shell/StructuredArtifactEditor.tsx} +9 -9
  150. package/src/workbench/{Timeline.tsx → shell/Timeline.tsx} +77 -20
  151. package/src/workbench/{WorkbenchCanvasShell.defaults.ts → shell/WorkbenchCanvasShell.defaults.ts} +1 -1
  152. package/src/workbench/{WorkbenchCanvasShell.model.ts → shell/WorkbenchCanvasShell.model.ts} +5 -5
  153. package/src/workbench/{WorkbenchCanvasShell.tsx → shell/WorkbenchCanvasShell.tsx} +6 -6
  154. package/src/workbench/{WorkbenchDesktopTitleBar.tsx → shell/WorkbenchDesktopTitleBar.tsx} +15 -15
  155. package/src/workbench/{WorkbenchShell.tsx → shell/WorkbenchShell.tsx} +45 -6
  156. package/src/workbench/{WorkbenchShellTitleBarLayoutControls.tsx → shell/WorkbenchShellTitleBarLayoutControls.tsx} +16 -19
  157. package/src/workbench/{WorkbenchStandaloneShell.tsx → shell/WorkbenchStandaloneShell.tsx} +15 -22
  158. package/src/workbench/{WorkbenchViewEditor.tsx → shell/WorkbenchViewEditor.tsx} +7 -7
  159. package/src/workbench/{WorkbenchViewSidebar.tsx → shell/WorkbenchViewSidebar.tsx} +7 -7
  160. package/src/workbench/{WorkbenchWindowChromeControls.tsx → shell/WorkbenchWindowChromeControls.tsx} +18 -15
  161. package/src/workbench/shell/activity-bar.css +47 -0
  162. package/src/workbench/{activityBarOrder.ts → shell/activityBarOrder.ts} +2 -1
  163. package/src/workbench/{shell.ts → shell/shell.ts} +20 -13
  164. package/src/workbench/{shellViewModel.ts → shell/shellViewModel.ts} +3 -3
  165. package/src/workbench/{standalone.ts → shell/standalone.ts} +14 -2
  166. package/src/workbench/{useDeferredWorkbenchMount.ts → shell/useDeferredWorkbenchMount.ts} +3 -3
  167. package/src/workbench/{useWorkbenchAppearanceDocumentSync.tsx → shell/useWorkbenchAppearanceDocumentSync.tsx} +1 -1
  168. package/src/workbench/{useWorkbenchSidebarActionBarDnd.ts → shell/useWorkbenchSidebarActionBarDnd.ts} +1 -1
  169. package/src/workbench/{useWorkbenchSidebarViewPlacementDropZone.ts → shell/useWorkbenchSidebarViewPlacementDropZone.ts} +2 -2
  170. package/src/workbench/{workbenchSidebarLayoutContext.ts → shell/workbenchSidebarLayoutContext.ts} +0 -6
  171. package/src/workbench/{workbenchViewRouteState.ts → shell/workbenchViewRouteState.ts} +4 -4
  172. package/src/workbench/{WorkbenchThemeProvider.tsx → theme/WorkbenchThemeProvider.tsx} +4 -4
  173. package/src/workbench/{theme.ts → theme/theme.ts} +1 -1
  174. package/src/workbench/{themePresets.ts → theme/themePresets.ts} +39 -64
  175. package/src/workbench/views/command-list-density.css +36 -0
  176. package/src/workbench/views/command-list.css +2 -0
  177. package/src/workbench/views/extensions-sidebar.css +2 -0
  178. package/src/workbench/views/view-hosts.css +3 -3
  179. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +1 -1
  180. package/src/workbench/workspace/WorkspaceExplorer.tsx +105 -4
  181. package/src/workbench/workspace/createVirtualWorkspaceExplorerPort.ts +1 -14
  182. package/src/workbench/workspace/workspaceExplorerKeyboard.ts +109 -0
  183. package/src/workbench/workspace/workspaceJsonDiagnostics.ts +6 -5
  184. package/src/workbench-menu-surfaces.css +3 -57
  185. package/src/brand/TilepaperAppIcon.tsx +0 -181
  186. package/src/brand/index.ts +0 -2
  187. package/src/brand/tilepaper-app-icon-geometry.ts +0 -201
  188. package/src/primitives/absolute-box/index.ts +0 -1
  189. package/src/primitives/catalog-browse-card/index.ts +0 -1
  190. package/src/primitives/catalog-browse-pane/index.ts +0 -3
  191. package/src/primitives/catalog-filter-overlay/index.ts +0 -1
  192. package/src/primitives/codicon/index.ts +0 -1
  193. package/src/primitives/external-link-button/index.ts +0 -1
  194. package/src/primitives/library-detail-layout/index.ts +0 -1
  195. package/src/primitives/library-facet-filter-panel/index.ts +0 -1
  196. package/src/primitives/library-facet-filter-strip/index.ts +0 -1
  197. package/src/primitives/panel-loading/index.ts +0 -1
  198. package/src/primitives/scroll-area-infinite-load/index.ts +0 -2
  199. package/src/primitives/workbench-thumbnail/index.ts +0 -1
  200. package/src/test-utils/workbenchMonacoMock.tsx +0 -37
  201. package/src/workbench/jdw/jdw-editors.css +0 -4
  202. package/src/workbench/jdw/jdw.css +0 -2
  203. package/src/workbench/management/integration-command-action.ts +0 -25
  204. package/src/workbench/markdownRemarkPlugins.ts +0 -4
  205. package/src/workbench/workbench-chrome.css +0 -4
  206. /package/src/workbench/{WorkbenchPlatformContext.tsx → chrome/WorkbenchPlatformContext.tsx} +0 -0
  207. /package/src/workbench/{workbenchModalViewState.ts → chrome/workbenchModalViewState.ts} +0 -0
  208. /package/src/workbench/{workbenchOverlaysContext.tsx → chrome/workbenchOverlaysContext.tsx} +0 -0
  209. /package/src/workbench/{workbenchPlatformChrome.ts → chrome/workbenchPlatformChrome.ts} +0 -0
  210. /package/src/workbench/{command-execution-policy.ts → commands/command-execution-policy.ts} +0 -0
  211. /package/src/workbench/{keyboard.ts → commands/keyboard.ts} +0 -0
  212. /package/src/workbench/{workbenchContextMenu.ts → commands/workbenchContextMenu.ts} +0 -0
  213. /package/src/workbench/{appearanceLabels.ts → shell/appearanceLabels.ts} +0 -0
  214. /package/src/workbench/{hardReset.ts → shell/hardReset.ts} +0 -0
  215. /package/src/workbench/{shellPresets.ts → shell/shellPresets.ts} +0 -0
  216. /package/src/workbench/{shellState.ts → shell/shellState.ts} +0 -0
  217. /package/src/workbench/{sidebarViewPlacementDnd.ts → shell/sidebarViewPlacementDnd.ts} +0 -0
  218. /package/src/workbench/{status.ts → shell/status.ts} +0 -0
  219. /package/src/workbench/{useWorkbenchSidebarLayout.tsx → shell/useWorkbenchSidebarLayout.tsx} +0 -0
  220. /package/src/workbench/{useWorkbenchStandaloneShellStateSync.tsx → shell/useWorkbenchStandaloneShellStateSync.tsx} +0 -0
  221. /package/src/workbench/{workbenchStandaloneShellReactContext.tsx → shell/workbenchStandaloneShellReactContext.tsx} +0 -0
@@ -1,13 +1,13 @@
1
1
  import { Badge } from '../../primitives/badge';
2
2
  import { Button } from '../../primitives/button';
3
3
  import { cx } from '../../utils/cx';
4
- import { getWorkbenchCommandExecutionPolicyLabel } from '../command-execution-policy';
5
- import type { WorkbenchCommandExecutionPolicy } from '../command-model';
4
+ import { getWorkbenchCommandExecutionPolicyLabel } from '../commands/command-execution-policy';
5
+ import type { WorkbenchCommandExecutionPolicy } from '../commands/command-model';
6
6
  import type { ChatCommandProposal, ChatCommandProposalStatus } from './types';
7
7
 
8
8
  export interface ChatCommandProposalCardProps {
9
- onAllow?: ((proposal: ChatCommandProposal) => void) | undefined;
10
- onDeny?: ((proposal: ChatCommandProposal) => void) | undefined;
9
+ onAllow?: (proposal: ChatCommandProposal) => void;
10
+ onDeny?: (proposal: ChatCommandProposal) => void;
11
11
  proposal: ChatCommandProposal;
12
12
  }
13
13
 
@@ -12,14 +12,14 @@ import { defaultWorkbenchChatConversationLabels } from './chatConversation';
12
12
  export interface ChatConversationBarProps {
13
13
  activeConversationId: string;
14
14
  conversations: readonly WorkbenchChatConversation[];
15
- labels?: Partial<WorkbenchChatConversationLabels> | undefined;
16
- moreMenuItems?: readonly ContextMenuItem[] | undefined;
15
+ labels?: Partial<WorkbenchChatConversationLabels>;
16
+ moreMenuItems?: readonly ContextMenuItem[];
17
17
  onActivate: (conversationId: string) => void;
18
18
  onCreate: () => void;
19
19
  onDelete: (conversationId: string) => void;
20
20
  onRename: (conversationId: string, title: string) => void;
21
21
  /** Optional slot for a second pill next to the active conversation control. */
22
- secondaryPill?: ReactNode | undefined;
22
+ secondaryPill?: ReactNode;
23
23
  }
24
24
 
25
25
  export function ChatConversationBar({
@@ -15,7 +15,7 @@ import { buildChatHistoryRowMenuItems } from './chatHistoryRowMenuItems';
15
15
  export interface ChatHistoryMenuProps {
16
16
  activeConversationId: string;
17
17
  conversations: readonly WorkbenchChatConversation[];
18
- labels?: Partial<WorkbenchChatConversationLabels> | undefined;
18
+ labels?: Partial<WorkbenchChatConversationLabels>;
19
19
  x: number;
20
20
  y: number;
21
21
  onActivate: (conversationId: string) => void;
@@ -5,6 +5,8 @@ import { cx } from '../../utils/cx';
5
5
  export const CHAT_MESSAGE_COLLAPSE_MAX_LINES = 8;
6
6
 
7
7
  export interface ChatMessageCollapsibleProps {
8
+ /** In-bubble region after message body (file chips, etc.). */
9
+ attachments?: ReactNode;
8
10
  children: ReactNode;
9
11
  className?: string;
10
12
  content: string;
@@ -26,6 +28,7 @@ function measureChatMessageNeedsCollapse(body: HTMLElement, maxLines: number): b
26
28
  }
27
29
 
28
30
  export function ChatMessageCollapsible({
31
+ attachments,
29
32
  children,
30
33
  className,
31
34
  content,
@@ -99,6 +102,7 @@ export function ChatMessageCollapsible({
99
102
  >
100
103
  {children}
101
104
  </div>
105
+ {attachments ? <div className="message__attachments">{attachments}</div> : null}
102
106
  {needsCollapse && !isStreaming ? (
103
107
  <Button
104
108
  aria-expanded={expanded}
@@ -1,7 +1,11 @@
1
1
  import Markdown from 'react-markdown';
2
2
  import type { ReactNode } from 'react';
3
3
  import { cx } from '../../utils/cx';
4
- import { workbenchMarkdownRemarkPlugins } from '../markdownRemarkPlugins';
4
+ import {
5
+ workbenchMarkdownRemarkPlugins,
6
+ workbenchMarkdownRehypePlugins,
7
+ } from '../markdown/markdownRemarkPlugins';
8
+ import { sanitizeMarkdownHref } from '../markdown/sanitizeMarkdownHref';
5
9
  import { ChatCommandProposalCard } from './ChatCommandProposalCard';
6
10
  import { ChatMessageCollapsible } from './ChatMessageCollapsible';
7
11
  import { ChatMessageTime, resolveChatMessageTimestamp } from './chatMessageMeta';
@@ -16,26 +20,38 @@ import type {
16
20
  export interface ChatMessageItemProps {
17
21
  /**
18
22
  * Rendered after the bubble (and before command proposals). Use for
19
- * attachments or host chrome that should stay outside the collapsible surface.
23
+ * host chrome that should stay outside the collapsible surface.
24
+ * Prefer `attachments` for in-bubble file chips.
20
25
  */
21
- afterMessage?: ReactNode | undefined;
26
+ afterMessage?: ReactNode;
27
+ /**
28
+ * In-bubble attachment region (file chips, etc.). Renders inside the bubble
29
+ * surface after the message body. Prefer this over `afterMessage` when chips
30
+ * must stay end-aligned with user bubbles.
31
+ */
32
+ attachments?: ReactNode;
22
33
  assistantLabel?: string;
23
34
  /** Overrides `message.contentMode` when set. */
24
- contentMode?: ChatMessageContentMode | undefined;
35
+ contentMode?: ChatMessageContentMode;
25
36
  /**
26
37
  * Forwarded into `ChatMessageCollapsible` in-bubble footer (progress, actions).
27
38
  */
28
- footer?: ReactNode | undefined;
39
+ footer?: ReactNode;
29
40
  isStreaming?: boolean;
41
+ /**
42
+ * Assistant-layout label icon. Defaults to sparkle; error/warning tones use
43
+ * status icons. Pass a custom node to override, or `false` to hide.
44
+ */
45
+ labelIcon?: ReactNode | false;
30
46
  layout?: ChatMessageLayout;
31
47
  message: ChatMessage;
32
- onCommandProposalAllow?: ((messageId: string, proposal: ChatCommandProposal) => void) | undefined;
33
- onCommandProposalDeny?: ((messageId: string, proposal: ChatCommandProposal) => void) | undefined;
48
+ onCommandProposalAllow?: (messageId: string, proposal: ChatCommandProposal) => void;
49
+ onCommandProposalDeny?: (messageId: string, proposal: ChatCommandProposal) => void;
34
50
  showSenderLabel?: boolean;
35
51
  /** When true, keeps the inline timestamp visible without hover. */
36
52
  showTimestamp?: boolean;
37
53
  /** Overrides `message.tone` when set. */
38
- tone?: ChatMessageTone | undefined;
54
+ tone?: ChatMessageTone;
39
55
  userLabel?: string;
40
56
  }
41
57
 
@@ -151,6 +167,25 @@ function resolveChatMessageToneClass(
151
167
  return undefined;
152
168
  }
153
169
 
170
+ function resolveAssistantLabelIcon(
171
+ labelIcon: ReactNode | false | undefined,
172
+ tone: ChatMessageTone | undefined,
173
+ ): ReactNode | null {
174
+ if (labelIcon === false) {
175
+ return null;
176
+ }
177
+ if (labelIcon !== undefined) {
178
+ return labelIcon;
179
+ }
180
+ if (tone === 'error') {
181
+ return <i className="codicon codicon-error message__label-icon" />;
182
+ }
183
+ if (tone === 'warning') {
184
+ return <i className="codicon codicon-warning message__label-icon" />;
185
+ }
186
+ return <i className="codicon codicon-sparkle message__label-icon" />;
187
+ }
188
+
154
189
  function ChatMessageBody({
155
190
  content,
156
191
  contentMode,
@@ -165,7 +200,23 @@ function ChatMessageBody({
165
200
  <div className="md-content">
166
201
  <Markdown
167
202
  remarkPlugins={workbenchMarkdownRemarkPlugins}
203
+ rehypePlugins={workbenchMarkdownRehypePlugins}
168
204
  components={{
205
+ a: ({ children, href }) => {
206
+ const safeHref = sanitizeMarkdownHref(href);
207
+ if (!safeHref) {
208
+ return <span>{children}</span>;
209
+ }
210
+ return (
211
+ <a
212
+ href={safeHref}
213
+ rel="noreferrer"
214
+ target={safeHref.startsWith('#') ? undefined : '_blank'}
215
+ >
216
+ {children}
217
+ </a>
218
+ );
219
+ },
169
220
  code: ({ children, className }) => (
170
221
  <code className={cx('ui-workbench-scrollbar', className)}>{children}</code>
171
222
  ),
@@ -188,10 +239,12 @@ function ChatMessageBody({
188
239
 
189
240
  export function ChatMessageItem({
190
241
  afterMessage,
242
+ attachments,
191
243
  assistantLabel = 'Assistant',
192
244
  contentMode: contentModeProp,
193
245
  footer,
194
246
  isStreaming = false,
247
+ labelIcon,
195
248
  layout = 'assistant',
196
249
  message,
197
250
  onCommandProposalAllow,
@@ -204,7 +257,9 @@ export function ChatMessageItem({
204
257
  const timestamp = renderMessageTimestamp(message, showTimestamp);
205
258
  const bubbleAlign = message.source === 'user' ? 'end' : 'start';
206
259
  const contentMode = resolveChatMessageContentMode(message, layout, contentModeProp);
207
- const toneClass = resolveChatMessageToneClass(toneProp ?? message.tone);
260
+ const resolvedTone = toneProp ?? message.tone;
261
+ const toneClass = resolveChatMessageToneClass(resolvedTone);
262
+ const assistantLabelIcon = resolveAssistantLabelIcon(labelIcon, resolvedTone);
208
263
  const body = (
209
264
  <ChatMessageBody
210
265
  content={message.content}
@@ -234,6 +289,7 @@ export function ChatMessageItem({
234
289
  ) : null}
235
290
  <MessageBubbleLine align={bubbleAlign} timestamp={timestamp}>
236
291
  <ChatMessageCollapsible
292
+ attachments={attachments}
237
293
  content={message.content}
238
294
  footer={footer}
239
295
  isStreaming={isStreaming}
@@ -266,6 +322,7 @@ export function ChatMessageItem({
266
322
  {peerLabel ? <div className="message__peer-label">{peerLabel}</div> : null}
267
323
  <MessageBubbleLine align={bubbleAlign} timestamp={timestamp}>
268
324
  <ChatMessageCollapsible
325
+ attachments={attachments}
269
326
  content={message.content}
270
327
  footer={footer}
271
328
  isStreaming={isStreaming}
@@ -291,11 +348,12 @@ export function ChatMessageItem({
291
348
  <div className="message__row">
292
349
  <div className="message__main">
293
350
  <div className="message__label message__label--assistant">
294
- <i className="codicon codicon-sparkle message__label-icon" />
351
+ {assistantLabelIcon}
295
352
  {message.label ?? assistantLabel}
296
353
  </div>
297
354
  <MessageBubbleLine align={bubbleAlign} timestamp={timestamp}>
298
355
  <ChatMessageCollapsible
356
+ attachments={attachments}
299
357
  className="message__assistant-collapsible"
300
358
  content={message.content}
301
359
  footer={footer}
@@ -23,7 +23,7 @@ export interface ChatMessageListProps {
23
23
  * Host slot rendered inside the message list area after messages, or as a
24
24
  * sibling before the empty state when there are no messages.
25
25
  */
26
- messageListAddon?: ReactNode | undefined;
26
+ messageListAddon?: ReactNode;
27
27
  messages: ChatMessage[];
28
28
  onCommandProposalAllow?: ChatMessageItemProps['onCommandProposalAllow'];
29
29
  onCommandProposalDeny?: ChatMessageItemProps['onCommandProposalDeny'];
@@ -22,18 +22,23 @@ export interface ChatPanelProps
22
22
  | 'showTools'
23
23
  | 'value'
24
24
  > {
25
- className?: string | undefined;
26
- composerRef?: Ref<HTMLTextAreaElement> | undefined;
25
+ className?: string;
26
+ composerRef?: Ref<HTMLTextAreaElement>;
27
27
  /** Label shown on the file-drop overlay. Defaults to "Drop files to attach". */
28
- filesDropLabel?: string | undefined;
29
- headerAddon?: ReactNode | undefined;
28
+ filesDropLabel?: string;
29
+ headerAddon?: ReactNode;
30
30
  /** Called when files are dropped onto the panel (disabled while `disabled` or `isRunning`). */
31
- onFilesDrop?: ((files: File[]) => void) | undefined;
31
+ onFilesDrop?: (files: File[]) => void;
32
32
  /**
33
33
  * Wrap or replace the default `<ChatMessageList />` so hosts can inject
34
34
  * hybrid timelines around the kit list.
35
35
  */
36
- renderMessageList?: ((defaultList: ReactNode) => ReactNode) | undefined;
36
+ renderMessageList?: (defaultList: ReactNode) => ReactNode;
37
+ /**
38
+ * Wrap or replace the default `<ChatComposer />` while keeping panel chrome
39
+ * and file-drop overlay behavior.
40
+ */
41
+ renderComposer?: (defaultComposer: ReactNode) => ReactNode;
37
42
  title?: string;
38
43
  }
39
44
 
@@ -53,6 +58,7 @@ export function ChatPanel({
53
58
  filesDropLabel = 'Drop files to attach',
54
59
  headerAddon,
55
60
  onFilesDrop,
61
+ renderComposer,
56
62
  renderMessageList,
57
63
  title = 'Chat',
58
64
  value,
@@ -125,6 +131,25 @@ export function ChatPanel({
125
131
  ? renderMessageList(defaultMessageList)
126
132
  : defaultMessageList;
127
133
 
134
+ const defaultComposer = (
135
+ <ChatComposer
136
+ ref={composerRef}
137
+ commandLabel={commandLabel}
138
+ commandSuggestPopover={commandSuggestPopover}
139
+ disabled={disabled}
140
+ isRunning={isRunning}
141
+ placeholder={placeholder}
142
+ showTools={showTools}
143
+ value={value}
144
+ onCancel={onCancel}
145
+ onCommandClick={onCommandClick}
146
+ onKeyDown={onKeyDown}
147
+ onSubmit={onSubmit}
148
+ onValueChange={onValueChange}
149
+ />
150
+ );
151
+ const composer = renderComposer ? renderComposer(defaultComposer) : defaultComposer;
152
+
128
153
  return (
129
154
  <div
130
155
  className={cx('chat-panel-drop-target', isFileDragActive && 'chat-panel-drop-target--active')}
@@ -135,23 +160,7 @@ export function ChatPanel({
135
160
  >
136
161
  <SideBarViewFrame
137
162
  className={cx('chat-sidebar-view', className)}
138
- footer={
139
- <ChatComposer
140
- ref={composerRef}
141
- commandLabel={commandLabel}
142
- commandSuggestPopover={commandSuggestPopover}
143
- disabled={disabled}
144
- isRunning={isRunning}
145
- placeholder={placeholder}
146
- showTools={showTools}
147
- value={value}
148
- onCancel={onCancel}
149
- onCommandClick={onCommandClick}
150
- onKeyDown={onKeyDown}
151
- onSubmit={onSubmit}
152
- onValueChange={onValueChange}
153
- />
154
- }
163
+ footer={composer}
155
164
  footerPlacement="overlay"
156
165
  headerAddon={headerAddon}
157
166
  title={title}
@@ -7,7 +7,7 @@ import {
7
7
  getWorkbenchStatusDescriptor,
8
8
  type WorkbenchStatus,
9
9
  type WorkbenchStatusVariant,
10
- } from '../status';
10
+ } from '../shell/status';
11
11
 
12
12
  /** Product-neutral phase lifecycle for run-progress UIs. */
13
13
  export type ChatRunPhaseStatus =
@@ -29,6 +29,20 @@ export interface ChatPhasedRunProgressAction {
29
29
  readonly variant?: 'default' | 'primary' | 'danger';
30
30
  }
31
31
 
32
+ /**
33
+ * Overridable chrome strings for locale-aware hosts. Unset keys keep English defaults.
34
+ */
35
+ export interface ChatPhasedRunProgressLabels {
36
+ readonly expand?: string;
37
+ readonly collapse?: string;
38
+ readonly noPhases?: string;
39
+ readonly phasesComplete?: (completed: number, total: number) => string;
40
+ /** Summary badge label for the aggregate run status. */
41
+ readonly summaryStatus?: (status: ChatRunPhaseStatus) => string;
42
+ /** Per-phase status badge label (also used when `summaryStatus` is omitted). */
43
+ readonly getStatusLabel?: (status: ChatRunPhaseStatus) => string;
44
+ }
45
+
32
46
  function toBadgeVariant(variant: WorkbenchStatusVariant): 'accent' | 'muted' | 'danger' {
33
47
  if (variant === 'danger' || variant === 'warning') {
34
48
  return 'danger';
@@ -50,6 +64,8 @@ export interface ChatPhasedRunProgressProps {
50
64
  /** Extra footer content (host-owned). */
51
65
  readonly footer?: ReactNode;
52
66
  readonly className?: string;
67
+ /** Locale-aware chrome labels; defaults preserve English copy. */
68
+ readonly labels?: ChatPhasedRunProgressLabels;
53
69
  }
54
70
 
55
71
  function mapPhaseStatusToWorkbenchStatus(status: ChatRunPhaseStatus): WorkbenchStatus {
@@ -73,26 +89,73 @@ function mapPhaseStatusToWorkbenchStatus(status: ChatRunPhaseStatus): WorkbenchS
73
89
  }
74
90
  }
75
91
 
76
- function summarizePhases(phases: readonly ChatRunPhase[]): {
92
+ const DEFAULT_SUMMARY_STATUS_LABELS: Record<ChatRunPhaseStatus, string> = {
93
+ pending: 'Pending',
94
+ running: 'Running',
95
+ completed: 'Completed',
96
+ failed: 'Failed',
97
+ skipped: 'Idle',
98
+ cancelled: 'Cancelled',
99
+ };
100
+
101
+ function resolveStatusLabel(
102
+ status: ChatRunPhaseStatus,
103
+ labels: ChatPhasedRunProgressLabels | undefined,
104
+ prefer: 'summary' | 'phase' = 'summary',
105
+ ): string {
106
+ const primary =
107
+ prefer === 'summary'
108
+ ? (labels?.summaryStatus?.(status) ?? labels?.getStatusLabel?.(status))
109
+ : (labels?.getStatusLabel?.(status) ?? labels?.summaryStatus?.(status));
110
+ if (primary) {
111
+ return primary;
112
+ }
113
+ if (status === 'skipped') {
114
+ return getWorkbenchStatusDescriptor(mapPhaseStatusToWorkbenchStatus(status)).label;
115
+ }
116
+ return (
117
+ DEFAULT_SUMMARY_STATUS_LABELS[status] ??
118
+ getWorkbenchStatusDescriptor(mapPhaseStatusToWorkbenchStatus(status)).label
119
+ );
120
+ }
121
+
122
+ function summarizePhases(
123
+ phases: readonly ChatRunPhase[],
124
+ labels: ChatPhasedRunProgressLabels | undefined,
125
+ ): {
77
126
  label: string;
78
127
  status: ChatRunPhaseStatus;
79
128
  } {
80
129
  if (phases.some((phase) => phase.status === 'failed')) {
81
- return { label: 'Failed', status: 'failed' };
130
+ return { label: resolveStatusLabel('failed', labels, 'summary'), status: 'failed' };
82
131
  }
83
132
  if (phases.some((phase) => phase.status === 'running')) {
84
- return { label: 'Running', status: 'running' };
133
+ return { label: resolveStatusLabel('running', labels, 'summary'), status: 'running' };
85
134
  }
86
135
  if (phases.some((phase) => phase.status === 'cancelled')) {
87
- return { label: 'Cancelled', status: 'cancelled' };
136
+ return { label: resolveStatusLabel('cancelled', labels, 'summary'), status: 'cancelled' };
88
137
  }
89
138
  if (
90
139
  phases.length > 0 &&
91
140
  phases.every((phase) => phase.status === 'completed' || phase.status === 'skipped')
92
141
  ) {
93
- return { label: 'Completed', status: 'completed' };
142
+ return { label: resolveStatusLabel('completed', labels, 'summary'), status: 'completed' };
143
+ }
144
+ return { label: resolveStatusLabel('pending', labels, 'summary'), status: 'pending' };
145
+ }
146
+
147
+ function resolvePhaseBadgeLabel(
148
+ status: ChatRunPhaseStatus,
149
+ labels: ChatPhasedRunProgressLabels | undefined,
150
+ ): string {
151
+ if (labels?.getStatusLabel) {
152
+ return labels.getStatusLabel(status);
153
+ }
154
+ // Pending maps to workbench "waiting" / "Waiting"; keep summary vocabulary ("Pending").
155
+ if (status === 'pending') {
156
+ return resolveStatusLabel('pending', labels, 'summary');
94
157
  }
95
- return { label: 'Pending', status: 'pending' };
158
+ return getWorkbenchStatusDescriptor(mapPhaseStatusToWorkbenchStatus(status)).label;
96
159
  }
97
160
 
98
161
  /**
@@ -108,14 +171,24 @@ export function ChatPhasedRunProgress({
108
171
  actions,
109
172
  footer,
110
173
  className,
174
+ labels,
111
175
  }: ChatPhasedRunProgressProps) {
112
176
  const panelId = useId();
113
177
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);
114
178
  const isExpanded = expanded ?? uncontrolledExpanded;
115
- const summary = summarizePhases(phases);
179
+ const summary = summarizePhases(phases, labels);
116
180
  const summaryDescriptor = getWorkbenchStatusDescriptor(
117
181
  mapPhaseStatusToWorkbenchStatus(summary.status),
118
182
  );
183
+ const expandLabel = labels?.expand ?? 'Expand';
184
+ const collapseLabel = labels?.collapse ?? 'Collapse';
185
+ const noPhasesLabel = labels?.noPhases ?? 'No phases';
186
+ const completedCount = phases.filter(
187
+ (phase) => phase.status === 'completed' || phase.status === 'skipped',
188
+ ).length;
189
+ const phasesCompleteLabel =
190
+ labels?.phasesComplete?.(completedCount, phases.length) ??
191
+ `${completedCount}/${phases.length} phases complete`;
119
192
 
120
193
  const setExpanded = (next: boolean) => {
121
194
  if (expanded === undefined) {
@@ -143,7 +216,7 @@ export function ChatPhasedRunProgress({
143
216
  aria-controls={panelId}
144
217
  onClick={() => setExpanded(!isExpanded)}
145
218
  >
146
- {isExpanded ? 'Collapse' : 'Expand'}
219
+ {isExpanded ? collapseLabel : expandLabel}
147
220
  </Button>
148
221
  </header>
149
222
 
@@ -162,7 +235,9 @@ export function ChatPhasedRunProgress({
162
235
  >
163
236
  <div className="chat-phased-run-progress__phase-main">
164
237
  <span className="chat-phased-run-progress__phase-label">{phase.label}</span>
165
- <Badge variant={toBadgeVariant(descriptor.variant)}>{descriptor.label}</Badge>
238
+ <Badge variant={toBadgeVariant(descriptor.variant)}>
239
+ {resolvePhaseBadgeLabel(phase.status, labels)}
240
+ </Badge>
166
241
  </div>
167
242
  {phase.detail ? (
168
243
  <p className="chat-phased-run-progress__phase-detail">{phase.detail}</p>
@@ -173,9 +248,7 @@ export function ChatPhasedRunProgress({
173
248
  </ol>
174
249
  ) : (
175
250
  <p className="chat-phased-run-progress__summary" id={panelId}>
176
- {phases.length === 0
177
- ? 'No phases'
178
- : `${phases.filter((phase) => phase.status === 'completed' || phase.status === 'skipped').length}/${phases.length} phases complete`}
251
+ {phases.length === 0 ? noPhasesLabel : phasesCompleteLabel}
179
252
  </p>
180
253
  )}
181
254
 
@@ -10,7 +10,7 @@ import { defaultWorkbenchChatConversationLabels } from './chatConversation';
10
10
 
11
11
  export interface ChatRenameDialogProps {
12
12
  conversation: WorkbenchChatConversation;
13
- labels?: Partial<WorkbenchChatConversationLabels> | undefined;
13
+ labels?: Partial<WorkbenchChatConversationLabels>;
14
14
  onCancel: () => void;
15
15
  onRename: (title: string) => void;
16
16
  }
@@ -65,7 +65,7 @@
65
65
  .chat-conversation-bar__pill-wrap {
66
66
  position: relative;
67
67
  flex: 1 1 auto;
68
- min-width: 7.5rem;
68
+ min-width: var(--workbench-chat-conversation-pill-min-width, 7.5rem);
69
69
  max-width: min(160px, 100%);
70
70
  overflow: visible;
71
71
  z-index: 1;
@@ -78,7 +78,7 @@
78
78
  .chat-conversation-bar__pill--session {
79
79
  flex: 0 1 auto;
80
80
  flex-shrink: 1;
81
- min-width: 5.5rem;
81
+ min-width: var(--workbench-chat-conversation-session-pill-min-width, 5.5rem);
82
82
  max-width: min(108px, 100%);
83
83
  color: var(--color-text-subtle);
84
84
  }
@@ -379,6 +379,14 @@
379
379
  margin-top: 2px;
380
380
  }
381
381
 
382
+ .message__attachments {
383
+ display: flex;
384
+ flex-wrap: wrap;
385
+ gap: 6px;
386
+ margin-top: 6px;
387
+ max-width: 100%;
388
+ }
389
+
382
390
  .message__after {
383
391
  display: flex;
384
392
  flex-wrap: wrap;
@@ -4,9 +4,9 @@ export interface WorkbenchChatConversation {
4
4
  id: string;
5
5
  title: string;
6
6
  /** Short secondary line shown in history menu rows. */
7
- metaSummary?: string | undefined;
7
+ metaSummary?: string;
8
8
  /** Tooltip content for the active conversation pill. */
9
- metaTooltip?: ReactNode | undefined;
9
+ metaTooltip?: ReactNode;
10
10
  }
11
11
 
12
12
  export interface WorkbenchChatConversationLabels {
@@ -118,8 +118,8 @@ export function toChatMessageTimeDateTime(value: ChatMessageTimestampInput): str
118
118
  export function getPeerChatSenderKey(
119
119
  message: ChatMessage,
120
120
  options: {
121
- assistantLabel?: string | undefined;
122
- userLabel?: string | undefined;
121
+ assistantLabel?: string;
122
+ userLabel?: string;
123
123
  } = {},
124
124
  ): string {
125
125
  if (message.source === 'user') {
@@ -133,8 +133,8 @@ export function shouldShowPeerChatSenderLabel(
133
133
  messages: readonly ChatMessage[],
134
134
  index: number,
135
135
  options: {
136
- assistantLabel?: string | undefined;
137
- userLabel?: string | undefined;
136
+ assistantLabel?: string;
137
+ userLabel?: string;
138
138
  } = {},
139
139
  ): boolean {
140
140
  const message = messages[index];
@@ -5,6 +5,7 @@ export type { ChatCommandProposalCardProps } from './ChatCommandProposalCard';
5
5
  export { ChatPhasedRunProgress } from './ChatPhasedRunProgress';
6
6
  export type {
7
7
  ChatPhasedRunProgressAction,
8
+ ChatPhasedRunProgressLabels,
8
9
  ChatPhasedRunProgressProps,
9
10
  ChatRunPhase,
10
11
  ChatRunPhaseStatus,
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
- import type { WorkbenchCommandExecutionPolicy } from '../command-model';
3
+ import type { WorkbenchCommandExecutionPolicy } from '../commands/command-model';
4
4
 
5
5
  export type ChatMessageSource = 'assistant' | 'user';
6
6
 
@@ -15,11 +15,11 @@ export type ChatCommandProposalStatus =
15
15
  'pending' | 'running' | 'allowed' | 'denied' | 'blocked' | 'executed' | 'failed';
16
16
 
17
17
  export interface ChatCommandProposal {
18
- args?: readonly unknown[] | undefined;
18
+ args?: readonly unknown[];
19
19
  commandId: string;
20
- description?: string | undefined;
20
+ description?: string;
21
21
  id: string;
22
- label?: string | undefined;
22
+ label?: string;
23
23
  policy: WorkbenchCommandExecutionPolicy;
24
24
  status: ChatCommandProposalStatus;
25
25
  }
@@ -27,13 +27,13 @@ export interface ChatCommandProposal {
27
27
  export type ChatMessageTimestamp = number | Date | string;
28
28
 
29
29
  export interface ChatMessage {
30
- commandProposals?: readonly ChatCommandProposal[] | undefined;
30
+ commandProposals?: readonly ChatCommandProposal[];
31
31
  content: string;
32
32
  /**
33
33
  * How `content` is rendered. Defaults to markdown for assistant-layout
34
34
  * assistant messages; otherwise plain text.
35
35
  */
36
- contentMode?: ChatMessageContentMode | undefined;
36
+ contentMode?: ChatMessageContentMode;
37
37
  createdAt?: string;
38
38
  id: string;
39
39
  label?: ReactNode;
@@ -41,5 +41,5 @@ export interface ChatMessage {
41
41
  /** Preferred timestamp for display; falls back to `createdAt` when omitted. */
42
42
  timestamp?: ChatMessageTimestamp;
43
43
  /** Optional visual tone for host-driven status / error messages. */
44
- tone?: ChatMessageTone | undefined;
44
+ tone?: ChatMessageTone;
45
45
  }
@@ -5,10 +5,10 @@ export const DEFAULT_RUNTIME_UNMOUNT_CANCELLABLE_STATUSES = ['running'] as const
5
5
 
6
6
  export interface UseCancelRuntimeOnUnmountOptions<TStatus extends string = RuntimeStatus> {
7
7
  cancel: () => void;
8
- cancellableStatuses?: readonly TStatus[] | undefined;
9
- enabled?: boolean | undefined;
10
- getStatus?: (() => TStatus) | undefined;
11
- status?: TStatus | undefined;
8
+ cancellableStatuses?: readonly TStatus[];
9
+ enabled?: boolean;
10
+ getStatus?: () => TStatus;
11
+ status?: TStatus;
12
12
  }
13
13
 
14
14
  export function shouldCancelRuntimeOnUnmount(