@workbench-kit/react 0.0.2-prototype.0.2.2 → 0.0.2-prototype.0.2.21
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.
- package/README.md +125 -0
- package/package.json +27 -20
- package/src/index.ts +60 -29
- package/src/jdw/JsonCodeEditorPane.tsx +7 -6
- package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +1 -3
- package/src/jdw/createBuiltinJdwRegistry.ts +1 -4
- package/src/jdw/cssRenderBackend.tsx +1 -4
- package/src/json-config/JsonConfigWorkbench.tsx +1 -1
- package/src/layout/index.ts +11 -0
- package/src/layout/sidebar/SideBarTree.tsx +301 -0
- package/src/layout/sidebar/SidebarActionIconBar.tsx +3 -3
- package/src/layout/sidebar/WorkbenchSidebarActions.tsx +1 -1
- package/src/layout/sidebar/index.ts +13 -0
- package/src/modal/Modal.tsx +55 -25
- package/src/modal/ModalTitlebar.tsx +8 -8
- package/src/modal/modalPosition.ts +3 -6
- package/src/modal/useModalFocusTrap.ts +142 -0
- package/src/modal/useModalWindowFrame.ts +2 -2
- package/src/overlay/ContextMenu.tsx +112 -5
- package/src/overlay/context-menu.css +4 -2
- package/src/overlay/measureAnchoredOverlayPanel.ts +4 -6
- package/src/overlay/usePointerPassthroughRegion.ts +2 -0
- package/src/primitives/absolute-box/AbsoluteBox.tsx +4 -4
- package/src/primitives/button/Button.tsx +4 -4
- package/src/primitives/catalog-browse-card/CatalogBrowseCard.tsx +6 -6
- package/src/primitives/catalog-browse-pane/CatalogBrowsePane.tsx +15 -15
- package/src/primitives/catalog-filter-overlay/CatalogFilterOverlay.tsx +2 -2
- package/src/primitives/checkbox/Checkbox.tsx +1 -1
- package/src/primitives/chip/Chip.tsx +2 -2
- package/src/primitives/codicon/Codicon.tsx +1 -1
- package/src/primitives/file-icon/FileIcon.tsx +1 -1
- package/src/primitives/icon-button/IconButton.tsx +1 -1
- package/src/primitives/index.ts +5 -0
- package/src/primitives/library-detail-layout/LibraryDetailLayout.tsx +123 -9
- package/src/primitives/library-detail-layout/index.ts +5 -0
- package/src/primitives/library-detail-layout/library-detail-layout.css +57 -0
- package/src/primitives/library-detail-layout/resolve-hero-cover-media.ts +80 -0
- package/src/primitives/library-facet-filter-panel/LibraryFacetFilterPanel.tsx +12 -12
- package/src/primitives/library-facet-filter-strip/LibraryFacetFilterStrip.tsx +4 -4
- package/src/primitives/number-input/NumberInput.tsx +5 -5
- package/src/primitives/record-media-hero/RecordMediaHero.tsx +10 -6
- package/src/primitives/scroll-area/ScrollArea.tsx +5 -5
- package/src/primitives/scroll-area-infinite-load/useScrollAreaInfiniteLoad.ts +5 -5
- package/src/primitives/searchable-multi-select/SearchableMultiSelect.tsx +7 -7
- package/src/primitives/select/Select.tsx +1 -13
- package/src/primitives/text-area/TextArea.tsx +1 -1
- package/src/primitives/text-input/TextInput.tsx +1 -1
- package/src/primitives/workbench-media-slot/WorkbenchMediaPlaceholder.tsx +2 -2
- package/src/primitives/workbench-media-slot/WorkbenchMediaSlot.tsx +16 -10
- package/src/primitives/workbench-thumbnail/WorkbenchThumbnail.tsx +4 -4
- package/src/styles.css +1 -2
- package/src/test-utils/workbenchMonacoMock.tsx +20 -0
- package/src/utils/clamp.ts +4 -0
- package/src/utils/normalizeKeyToken.ts +13 -0
- package/src/utils/readNumber.ts +4 -0
- package/src/widget-tree/WidgetTreeCanvasPreview.tsx +1 -4
- package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +2 -2
- package/src/workbench/chat/ChatCommandProposalCard.tsx +4 -4
- package/src/workbench/chat/ChatConversationBar.tsx +3 -3
- package/src/workbench/chat/ChatHistoryMenu.tsx +1 -1
- package/src/workbench/chat/ChatMessageCollapsible.tsx +4 -0
- package/src/workbench/chat/ChatMessageItem.tsx +68 -10
- package/src/workbench/chat/ChatMessageList.tsx +1 -1
- package/src/workbench/chat/ChatPanel.tsx +32 -23
- package/src/workbench/chat/ChatPhasedRunProgress.tsx +86 -13
- package/src/workbench/chat/ChatRenameDialog.tsx +1 -1
- package/src/workbench/chat/chat-conversation.css +2 -2
- package/src/workbench/chat/chat-ui.css +8 -0
- package/src/workbench/chat/chatConversation.ts +2 -2
- package/src/workbench/chat/chatMessageMeta.tsx +4 -4
- package/src/workbench/chat/index.ts +1 -0
- package/src/workbench/chat/types.ts +7 -7
- package/src/workbench/chat/useCancelRuntimeOnUnmount.ts +4 -4
- package/src/workbench/chat/useChatPrependPagination.ts +10 -10
- package/src/workbench/chat/useSlashCommandSuggest.ts +1 -1
- package/src/workbench/{WorkbenchModalPortal.tsx → chrome/WorkbenchModalPortal.tsx} +1 -1
- package/src/workbench/chrome/workbench-chrome.css +4 -0
- package/src/workbench/{CommandGroupShell.tsx → commands/CommandGroupShell.tsx} +2 -2
- package/src/workbench/{CommandList.tsx → commands/CommandList.tsx} +5 -5
- package/src/workbench/{CommandPalette.tsx → commands/CommandPalette.tsx} +16 -42
- package/src/workbench/{ConfirmationFlow.tsx → commands/ConfirmationFlow.tsx} +5 -5
- package/src/workbench/{ShortcutCommandBridge.tsx → commands/ShortcutCommandBridge.tsx} +1 -13
- package/src/workbench/commands/WorkbenchQuickOpen.tsx +462 -0
- package/src/workbench/{command-model.ts → commands/command-model.ts} +5 -1
- package/src/workbench/commands/command-ui.ts +3 -0
- package/src/workbench/{commands.ts → commands/commands.ts} +1 -1
- package/src/workbench/commands/createWorkspaceFilesQuickOpenProvider.ts +86 -0
- package/src/workbench/commands/quick-open-model.ts +66 -0
- package/src/workbench/{WorkbenchEditorTabs.tsx → editor/WorkbenchEditorTabs.tsx} +1 -1
- package/src/workbench/{editor-tabs-dnd.ts → editor/editor-tabs-dnd.ts} +1 -1
- package/src/workbench/{editorTabContextMenu.ts → editor/editorTabContextMenu.ts} +2 -2
- package/src/workbench/{useEditorTabsStripDnd.ts → editor/useEditorTabsStripDnd.ts} +1 -1
- package/src/workbench/{useWorkbenchEditorTabContextMenu.tsx → editor/useWorkbenchEditorTabContextMenu.tsx} +1 -1
- package/src/workbench/editor-tabs/index.ts +9 -9
- package/src/workbench/index.ts +110 -74
- package/src/workbench/management/ExtensionManagementPanel.tsx +32 -36
- package/src/workbench/management/ExtensionManagementSidebar.tsx +19 -35
- package/src/workbench/management/IntegrationSettingsSurface.tsx +2 -5
- package/src/workbench/management/IntegrationsShell.tsx +1 -1
- package/src/workbench/management/KeybindingCaptureField.tsx +1 -9
- package/src/workbench/management/WorkbenchDialogFrame.tsx +2 -2
- package/src/workbench/management/WorkbenchNotice.tsx +2 -5
- package/src/workbench/management/createWorkbenchNotify.tsx +88 -0
- package/src/workbench/management/extension-install-approval.ts +72 -0
- package/src/workbench/management/extension-management-filters.ts +38 -0
- package/src/workbench/management/index.ts +14 -0
- package/src/workbench/management/management.css +28 -0
- package/src/workbench/management/types.ts +14 -1
- package/src/workbench/management/useWorkbenchNotify.ts +10 -0
- package/src/workbench/{MarkdownPreview.tsx → markdown/MarkdownPreview.tsx} +22 -7
- package/src/workbench/markdown/markdownRemarkPlugins.ts +12 -0
- package/src/workbench/markdown/sanitizeMarkdownHref.ts +29 -0
- package/src/workbench/settings/StructuredDataForm.tsx +1 -4
- package/src/workbench/settings/structuredDataFormModel.ts +2 -1
- package/src/workbench/{ActivityBar.tsx → shell/ActivityBar.tsx} +8 -8
- package/src/workbench/{ArtifactShell.tsx → shell/ArtifactShell.tsx} +25 -25
- package/src/workbench/{SplitView.tsx → shell/SplitView.tsx} +3 -3
- package/src/workbench/{StatusBar.tsx → shell/StatusBar.tsx} +8 -8
- package/src/workbench/{StructuredArtifactEditor.tsx → shell/StructuredArtifactEditor.tsx} +9 -9
- package/src/workbench/{Timeline.tsx → shell/Timeline.tsx} +77 -20
- package/src/workbench/{WorkbenchCanvasShell.defaults.ts → shell/WorkbenchCanvasShell.defaults.ts} +1 -1
- package/src/workbench/{WorkbenchCanvasShell.model.ts → shell/WorkbenchCanvasShell.model.ts} +5 -5
- package/src/workbench/{WorkbenchCanvasShell.tsx → shell/WorkbenchCanvasShell.tsx} +6 -6
- package/src/workbench/{WorkbenchDesktopTitleBar.tsx → shell/WorkbenchDesktopTitleBar.tsx} +15 -15
- package/src/workbench/{WorkbenchShell.tsx → shell/WorkbenchShell.tsx} +45 -6
- package/src/workbench/{WorkbenchShellTitleBarLayoutControls.tsx → shell/WorkbenchShellTitleBarLayoutControls.tsx} +16 -19
- package/src/workbench/{WorkbenchStandaloneShell.tsx → shell/WorkbenchStandaloneShell.tsx} +4 -4
- package/src/workbench/{WorkbenchViewEditor.tsx → shell/WorkbenchViewEditor.tsx} +7 -7
- package/src/workbench/{WorkbenchViewSidebar.tsx → shell/WorkbenchViewSidebar.tsx} +7 -7
- package/src/workbench/{WorkbenchWindowChromeControls.tsx → shell/WorkbenchWindowChromeControls.tsx} +18 -15
- package/src/workbench/shell/activity-bar.css +47 -0
- package/src/workbench/{shell.ts → shell/shell.ts} +18 -12
- package/src/workbench/{shellViewModel.ts → shell/shellViewModel.ts} +3 -3
- package/src/workbench/{standalone.ts → shell/standalone.ts} +14 -2
- package/src/workbench/{useDeferredWorkbenchMount.ts → shell/useDeferredWorkbenchMount.ts} +3 -3
- package/src/workbench/{useWorkbenchAppearanceDocumentSync.tsx → shell/useWorkbenchAppearanceDocumentSync.tsx} +1 -1
- package/src/workbench/{useWorkbenchSidebarActionBarDnd.ts → shell/useWorkbenchSidebarActionBarDnd.ts} +1 -1
- package/src/workbench/{useWorkbenchSidebarViewPlacementDropZone.ts → shell/useWorkbenchSidebarViewPlacementDropZone.ts} +2 -2
- package/src/workbench/{workbenchViewRouteState.ts → shell/workbenchViewRouteState.ts} +4 -4
- package/src/workbench/{WorkbenchThemeProvider.tsx → theme/WorkbenchThemeProvider.tsx} +4 -4
- package/src/workbench/{theme.ts → theme/theme.ts} +1 -1
- package/src/workbench/{themePresets.ts → theme/themePresets.ts} +3 -2
- package/src/workbench/views/view-hosts.css +3 -3
- package/src/workbench/workspace/WorkspaceEditorPanel.tsx +1 -1
- package/src/workbench/workspace/WorkspaceExplorer.tsx +104 -3
- package/src/workbench/workspace/workspaceExplorerKeyboard.ts +109 -0
- package/src/workbench/workspace/workspaceJsonDiagnostics.ts +6 -5
- package/src/workbench/jdw/jdw-editors.css +0 -4
- package/src/workbench/jdw/jdw.css +0 -2
- package/src/workbench/markdownRemarkPlugins.ts +0 -4
- package/src/workbench/workbench-chrome.css +0 -4
- /package/src/workbench/{WorkbenchPlatformContext.tsx → chrome/WorkbenchPlatformContext.tsx} +0 -0
- /package/src/workbench/{workbenchModalViewState.ts → chrome/workbenchModalViewState.ts} +0 -0
- /package/src/workbench/{workbenchOverlaysContext.tsx → chrome/workbenchOverlaysContext.tsx} +0 -0
- /package/src/workbench/{workbenchPlatformChrome.ts → chrome/workbenchPlatformChrome.ts} +0 -0
- /package/src/workbench/{command-execution-policy.ts → commands/command-execution-policy.ts} +0 -0
- /package/src/workbench/{keyboard.ts → commands/keyboard.ts} +0 -0
- /package/src/workbench/{workbenchContextMenu.ts → commands/workbenchContextMenu.ts} +0 -0
- /package/src/workbench/{activityBarOrder.ts → shell/activityBarOrder.ts} +0 -0
- /package/src/workbench/{appearanceLabels.ts → shell/appearanceLabels.ts} +0 -0
- /package/src/workbench/{hardReset.ts → shell/hardReset.ts} +0 -0
- /package/src/workbench/{shellPresets.ts → shell/shellPresets.ts} +0 -0
- /package/src/workbench/{shellState.ts → shell/shellState.ts} +0 -0
- /package/src/workbench/{sidebarViewPlacementDnd.ts → shell/sidebarViewPlacementDnd.ts} +0 -0
- /package/src/workbench/{status.ts → shell/status.ts} +0 -0
- /package/src/workbench/{useWorkbenchSidebarLayout.tsx → shell/useWorkbenchSidebarLayout.tsx} +0 -0
- /package/src/workbench/{useWorkbenchStandaloneShellStateSync.tsx → shell/useWorkbenchStandaloneShellStateSync.tsx} +0 -0
- /package/src/workbench/{workbenchSidebarLayoutContext.ts → shell/workbenchSidebarLayoutContext.ts} +0 -0
- /package/src/workbench/{workbenchStandaloneShellReactContext.tsx → shell/workbenchStandaloneShellReactContext.tsx} +0 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
WorkbenchPropertySection,
|
|
8
8
|
WorkbenchPropertyStack,
|
|
9
9
|
} from '../../primitives/index';
|
|
10
|
+
import { cx } from '../../utils/cx';
|
|
10
11
|
|
|
11
12
|
type IntegrationSettingsDensity = 'compact' | 'default';
|
|
12
13
|
type IntegrationSettingsInset = 'default' | 'flush';
|
|
@@ -206,7 +207,7 @@ export function IntegrationAccountRowEditor({
|
|
|
206
207
|
export function IntegrationBodyText({ children, size = 'default' }: IntegrationBodyTextProps) {
|
|
207
208
|
return (
|
|
208
209
|
<WorkbenchPropertyHint
|
|
209
|
-
className={
|
|
210
|
+
className={cx('block', size === 'compact' ? 'text-xs' : undefined)}
|
|
210
211
|
data-size={size}
|
|
211
212
|
>
|
|
212
213
|
{children}
|
|
@@ -284,7 +285,3 @@ function IntegrationEditorDescription({ children }: { children?: ReactNode }) {
|
|
|
284
285
|
function hasRenderableNode(node: ReactNode | undefined): boolean {
|
|
285
286
|
return node !== undefined && node !== null && node !== false;
|
|
286
287
|
}
|
|
287
|
-
|
|
288
|
-
function joinClasses(...classes: Array<string | undefined>): string {
|
|
289
|
-
return classes.filter(Boolean).join(' ');
|
|
290
|
-
}
|
|
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
import { PanelHeader } from '../../layout/panel';
|
|
3
3
|
import { WorkbenchEditorFrame, WorkbenchPanelScroll } from '../../layout/WorkbenchLayoutBase';
|
|
4
4
|
import { WorkbenchPropertyHint } from '../../layout/WorkbenchPropertyPanel';
|
|
5
|
-
import { SplitView } from '../SplitView';
|
|
5
|
+
import { SplitView } from '../shell/SplitView';
|
|
6
6
|
|
|
7
7
|
export interface IntegrationsShellProps {
|
|
8
8
|
children: ReactNode;
|
|
@@ -2,6 +2,7 @@ import { useEffect, useId, useRef, useState, type KeyboardEvent } from 'react';
|
|
|
2
2
|
import { formatKeybindingLabel } from '@workbench-kit/platform';
|
|
3
3
|
import { Button } from '../../primitives/button';
|
|
4
4
|
import { cx } from '../../utils/cx';
|
|
5
|
+
import { normalizeKeyToken } from '../../utils/normalizeKeyToken';
|
|
5
6
|
|
|
6
7
|
export interface KeybindingCaptureFieldProps {
|
|
7
8
|
ariaLabel?: string | undefined;
|
|
@@ -131,12 +132,3 @@ function normalizeKeybindingKeyFromEvent(
|
|
|
131
132
|
parts.push(normalizeKeyToken(event.key));
|
|
132
133
|
return parts.join('+');
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
-
function normalizeKeyToken(token: string): string {
|
|
136
|
-
const key = token.trim().toLowerCase();
|
|
137
|
-
if (key === 'del') return 'delete';
|
|
138
|
-
if (key === 'esc') return 'escape';
|
|
139
|
-
if (key === 'return') return 'enter';
|
|
140
|
-
if (key === 'spacebar' || key === 'space') return 'space';
|
|
141
|
-
return key.length === 1 ? key : key;
|
|
142
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FormEventHandler, ReactNode } from 'react';
|
|
2
2
|
import { Modal, type ModalBodyLayout, type ModalBodyPadding } from '../../modal/Modal';
|
|
3
|
-
import type { WorkbenchWindowChromeMode } from '../workbenchPlatformChrome';
|
|
4
|
-
import { WorkbenchModalPortal } from '../WorkbenchModalPortal';
|
|
3
|
+
import type { WorkbenchWindowChromeMode } from '../chrome/workbenchPlatformChrome';
|
|
4
|
+
import { WorkbenchModalPortal } from '../chrome/WorkbenchModalPortal';
|
|
5
5
|
|
|
6
6
|
export type WorkbenchDialogBodyLayout = 'column-fill' | 'default' | 'padded-fill';
|
|
7
7
|
export type WorkbenchDialogFrameSize =
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type ReactNode,
|
|
10
10
|
} from 'react';
|
|
11
11
|
import { IconButton, WorkbenchBanner, WorkbenchBannerMessage } from '../../primitives/index';
|
|
12
|
+
import { cx } from '../../utils/cx';
|
|
12
13
|
|
|
13
14
|
export type WorkbenchNoticeTone = 'error' | 'info' | 'success' | 'warning';
|
|
14
15
|
export type WorkbenchNoticePosition = 'bottom-center' | 'bottom-right';
|
|
@@ -187,7 +188,7 @@ export function useWorkbenchNotice(): WorkbenchNoticeController {
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
function resolveNoticeViewportClassName(position: WorkbenchNoticePosition): string {
|
|
190
|
-
return
|
|
191
|
+
return cx(
|
|
191
192
|
'pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col gap-2 px-4',
|
|
192
193
|
position === 'bottom-center' ? 'items-center' : 'items-end',
|
|
193
194
|
);
|
|
@@ -205,7 +206,3 @@ function resolveNoticeBannerStyle(tone: WorkbenchNoticeTone): CSSProperties {
|
|
|
205
206
|
minWidth: 'min(420px, calc(100vw - 32px))',
|
|
206
207
|
};
|
|
207
208
|
}
|
|
208
|
-
|
|
209
|
-
function joinClasses(...classes: Array<string | undefined>): string {
|
|
210
|
-
return classes.filter(Boolean).join(' ');
|
|
211
|
-
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ShowWorkbenchNoticeInput,
|
|
5
|
+
WorkbenchNoticeController,
|
|
6
|
+
WorkbenchNoticeTone,
|
|
7
|
+
} from './WorkbenchNotice.js';
|
|
8
|
+
|
|
9
|
+
export interface WorkbenchNotificationAction {
|
|
10
|
+
readonly label: string;
|
|
11
|
+
readonly onAction: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WorkbenchNotifyOptions {
|
|
15
|
+
readonly actions?: readonly WorkbenchNotificationAction[];
|
|
16
|
+
readonly dataAttributes?: ShowWorkbenchNoticeInput['dataAttributes'];
|
|
17
|
+
readonly durationMs?: number;
|
|
18
|
+
readonly id?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* NotificationService-shaped facade over {@link WorkbenchNoticeController}.
|
|
23
|
+
* Prefer this for command/install/save feedback; keep modal confirms separate.
|
|
24
|
+
*/
|
|
25
|
+
export interface WorkbenchNotify {
|
|
26
|
+
info(message: ReactNode, options?: WorkbenchNotifyOptions): string;
|
|
27
|
+
error(message: ReactNode, options?: WorkbenchNotifyOptions): string;
|
|
28
|
+
success(message: ReactNode, options?: WorkbenchNotifyOptions): string;
|
|
29
|
+
warning(message: ReactNode, options?: WorkbenchNotifyOptions): string;
|
|
30
|
+
dismiss(id: string): void;
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function showTone(
|
|
35
|
+
controller: WorkbenchNoticeController,
|
|
36
|
+
tone: WorkbenchNoticeTone,
|
|
37
|
+
message: ReactNode,
|
|
38
|
+
options: WorkbenchNotifyOptions = {},
|
|
39
|
+
): string {
|
|
40
|
+
const { actions, dataAttributes, durationMs, id } = options;
|
|
41
|
+
const noticeId = id ?? `workbench-notify-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
42
|
+
|
|
43
|
+
const noticeMessage =
|
|
44
|
+
actions && actions.length > 0 ? (
|
|
45
|
+
<span className="ui-workbench-notify-message">
|
|
46
|
+
<span className="ui-workbench-notify-message__text">{message}</span>
|
|
47
|
+
<span className="ui-workbench-notify-message__actions">
|
|
48
|
+
{actions.map((action) => (
|
|
49
|
+
<button
|
|
50
|
+
className="ui-workbench-notify-message__action"
|
|
51
|
+
key={action.label}
|
|
52
|
+
type="button"
|
|
53
|
+
onClick={() => {
|
|
54
|
+
action.onAction();
|
|
55
|
+
controller.dismissNotice(noticeId);
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
{action.label}
|
|
59
|
+
</button>
|
|
60
|
+
))}
|
|
61
|
+
</span>
|
|
62
|
+
</span>
|
|
63
|
+
) : (
|
|
64
|
+
message
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return controller.showNotice({
|
|
68
|
+
...(dataAttributes === undefined ? {} : { dataAttributes }),
|
|
69
|
+
...(durationMs === undefined ? {} : { durationMs }),
|
|
70
|
+
id: noticeId,
|
|
71
|
+
message: noticeMessage,
|
|
72
|
+
tone,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a `notify.info` / `notify.error` facade over an existing notice controller.
|
|
78
|
+
*/
|
|
79
|
+
export function createWorkbenchNotify(controller: WorkbenchNoticeController): WorkbenchNotify {
|
|
80
|
+
return {
|
|
81
|
+
info: (message, options) => showTone(controller, 'info', message, options),
|
|
82
|
+
error: (message, options) => showTone(controller, 'error', message, options),
|
|
83
|
+
success: (message, options) => showTone(controller, 'success', message, options),
|
|
84
|
+
warning: (message, options) => showTone(controller, 'warning', message, options),
|
|
85
|
+
dismiss: (noticeId) => controller.dismissNotice(noticeId),
|
|
86
|
+
clear: () => controller.clearNotices(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ExtensionCatalogBrowseEntry, ExtensionInstallOptions } from './types.js';
|
|
2
|
+
|
|
3
|
+
export function extensionInstallRequiresApproval(entry: ExtensionCatalogBrowseEntry): boolean {
|
|
4
|
+
return Boolean(
|
|
5
|
+
entry.installPlan?.requiresApproval && !entry.installed && !entry.installPlan.blocked,
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function formatExtensionInstallApprovalMessage(entry: ExtensionCatalogBrowseEntry): string {
|
|
10
|
+
const permissions = entry.installPlan?.permissions ?? [];
|
|
11
|
+
const permissionLine =
|
|
12
|
+
permissions.length > 0
|
|
13
|
+
? `Requested permissions: ${permissions.join(', ')}.`
|
|
14
|
+
: 'This package requests privileges that require approval.';
|
|
15
|
+
|
|
16
|
+
return [
|
|
17
|
+
`Install "${entry.displayName}"?`,
|
|
18
|
+
'',
|
|
19
|
+
permissionLine,
|
|
20
|
+
'Continue only if you trust this package.',
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ResolveExtensionInstallOptionsInput {
|
|
25
|
+
readonly confirm?: ((message: string) => boolean) | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* When true (or when the predicate returns true), skip the confirm prompt and
|
|
28
|
+
* return `{ approved: true }`. Hosts typically back this with a durable trust
|
|
29
|
+
* store keyed by extension id + permission fingerprint.
|
|
30
|
+
*/
|
|
31
|
+
readonly isTrusted?: boolean | ((entry: ExtensionCatalogBrowseEntry) => boolean) | undefined;
|
|
32
|
+
/** Called after the user explicitly confirms trust for this install. */
|
|
33
|
+
readonly rememberTrust?: ((entry: ExtensionCatalogBrowseEntry) => void) | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve install options for UI actions.
|
|
38
|
+
*
|
|
39
|
+
* Approval is required when the install plan requests permissions. A trusted
|
|
40
|
+
* predicate may skip the prompt; otherwise the host confirm dialog runs and
|
|
41
|
+
* successful consent may be persisted via `rememberTrust`.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveExtensionInstallOptions(
|
|
44
|
+
entry: ExtensionCatalogBrowseEntry,
|
|
45
|
+
confirmOrOptions: ((message: string) => boolean) | ResolveExtensionInstallOptionsInput = (
|
|
46
|
+
message,
|
|
47
|
+
) => (typeof globalThis.confirm === 'function' ? globalThis.confirm(message) : false),
|
|
48
|
+
): ExtensionInstallOptions | undefined {
|
|
49
|
+
if (!extensionInstallRequiresApproval(entry)) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const options =
|
|
54
|
+
typeof confirmOrOptions === 'function' ? { confirm: confirmOrOptions } : confirmOrOptions;
|
|
55
|
+
const confirm =
|
|
56
|
+
options.confirm ??
|
|
57
|
+
((message: string) =>
|
|
58
|
+
typeof globalThis.confirm === 'function' ? globalThis.confirm(message) : false);
|
|
59
|
+
|
|
60
|
+
const trusted =
|
|
61
|
+
typeof options.isTrusted === 'function' ? options.isTrusted(entry) : Boolean(options.isTrusted);
|
|
62
|
+
if (trusted) {
|
|
63
|
+
return { approved: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!confirm(formatExtensionInstallApprovalMessage(entry))) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
options.rememberTrust?.(entry);
|
|
71
|
+
return { approved: true };
|
|
72
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ExtensionCatalogBrowseEntry, ExtensionManagementEntry } from './types.js';
|
|
2
|
+
|
|
3
|
+
export function filterInstalledEntries(
|
|
4
|
+
entries: readonly ExtensionManagementEntry[],
|
|
5
|
+
query: string,
|
|
6
|
+
): readonly ExtensionManagementEntry[] {
|
|
7
|
+
const normalized = query.trim().toLowerCase();
|
|
8
|
+
if (!normalized) {
|
|
9
|
+
return entries;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return entries.filter((entry) =>
|
|
13
|
+
[entry.displayName, entry.id, entry.category, entry.description ?? '']
|
|
14
|
+
.join(' ')
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.includes(normalized),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function filterBrowseEntries(
|
|
21
|
+
entries: readonly ExtensionCatalogBrowseEntry[],
|
|
22
|
+
query: string,
|
|
23
|
+
category?: string,
|
|
24
|
+
): readonly ExtensionCatalogBrowseEntry[] {
|
|
25
|
+
const normalized = query.trim().toLowerCase();
|
|
26
|
+
return entries.filter((entry) => {
|
|
27
|
+
if (category && entry.category !== category) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (!normalized) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return [entry.displayName, entry.id, entry.category, entry.description]
|
|
34
|
+
.join(' ')
|
|
35
|
+
.toLowerCase()
|
|
36
|
+
.includes(normalized);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -75,6 +75,13 @@ export type {
|
|
|
75
75
|
WorkbenchNoticePosition,
|
|
76
76
|
WorkbenchNoticeTone,
|
|
77
77
|
} from './WorkbenchNotice.js';
|
|
78
|
+
export { createWorkbenchNotify } from './createWorkbenchNotify.js';
|
|
79
|
+
export type {
|
|
80
|
+
WorkbenchNotificationAction,
|
|
81
|
+
WorkbenchNotify,
|
|
82
|
+
WorkbenchNotifyOptions,
|
|
83
|
+
} from './createWorkbenchNotify.js';
|
|
84
|
+
export { useWorkbenchNotify } from './useWorkbenchNotify.js';
|
|
78
85
|
export { KeybindingCaptureField } from './KeybindingCaptureField.js';
|
|
79
86
|
export type { KeybindingCaptureFieldProps } from './KeybindingCaptureField.js';
|
|
80
87
|
export { KeybindingManagementPanel } from './KeybindingManagementPanel.js';
|
|
@@ -112,6 +119,12 @@ export {
|
|
|
112
119
|
extensionCategoryIconTone,
|
|
113
120
|
formatExtensionCategoryLabel,
|
|
114
121
|
} from './extension-category-display.js';
|
|
122
|
+
export {
|
|
123
|
+
extensionInstallRequiresApproval,
|
|
124
|
+
formatExtensionInstallApprovalMessage,
|
|
125
|
+
resolveExtensionInstallOptions,
|
|
126
|
+
type ResolveExtensionInstallOptionsInput,
|
|
127
|
+
} from './extension-install-approval.js';
|
|
115
128
|
export type {
|
|
116
129
|
AccountManagementEntry,
|
|
117
130
|
AccountManagementEntryStatus,
|
|
@@ -122,6 +135,7 @@ export type {
|
|
|
122
135
|
CommandManagementPanelProps,
|
|
123
136
|
CommandManagementRunState,
|
|
124
137
|
ExtensionCatalogBrowseEntry,
|
|
138
|
+
ExtensionInstallOptions,
|
|
125
139
|
ExtensionManagementDiagnosticSummary,
|
|
126
140
|
ExtensionManagementEntry,
|
|
127
141
|
ExtensionManagementFeatureItem,
|
|
@@ -2,3 +2,31 @@
|
|
|
2
2
|
@import './library-catalog-picker-dialog.css';
|
|
3
3
|
@import './library-facet-filter-dialog.css';
|
|
4
4
|
@import './management-panel.css';
|
|
5
|
+
|
|
6
|
+
.ui-workbench-notify-message {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
flex-wrap: wrap;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
align-items: center;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.ui-workbench-notify-message__actions {
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
gap: 6px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.ui-workbench-notify-message__action {
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 2px 8px;
|
|
22
|
+
border: 1px solid var(--color-border, currentColor);
|
|
23
|
+
border-radius: var(--radius-sm, 4px);
|
|
24
|
+
background: transparent;
|
|
25
|
+
color: inherit;
|
|
26
|
+
font: inherit;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ui-workbench-notify-message__action:hover {
|
|
31
|
+
background: var(--color-surface-hover, rgba(255, 255, 255, 0.08));
|
|
32
|
+
}
|
|
@@ -115,12 +115,25 @@ export interface ExtensionCatalogBrowseEntry {
|
|
|
115
115
|
readonly manifestUrl: string;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
export interface ExtensionInstallOptions {
|
|
119
|
+
/** Required when the install plan sets `requiresApproval`. */
|
|
120
|
+
readonly approved?: boolean | undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
export interface ExtensionManagementPanelProps {
|
|
119
124
|
browseEntries: readonly ExtensionCatalogBrowseEntry[];
|
|
120
125
|
catalogError?: string | undefined;
|
|
121
126
|
catalogLoading?: boolean | undefined;
|
|
122
127
|
className?: string | undefined;
|
|
123
128
|
installedEntries: readonly ExtensionManagementEntry[];
|
|
124
|
-
onInstall?:
|
|
129
|
+
onInstall?:
|
|
130
|
+
((entry: ExtensionCatalogBrowseEntry, options?: ExtensionInstallOptions) => void) | undefined;
|
|
125
131
|
onToggleEnabled?: ((entry: ExtensionManagementEntry, enabled: boolean) => void) | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Host-owned durable trust check. When true for an entry, the install confirm
|
|
134
|
+
* prompt is skipped and `{ approved: true }` is used.
|
|
135
|
+
*/
|
|
136
|
+
isInstallTrusted?: ((entry: ExtensionCatalogBrowseEntry) => boolean) | undefined;
|
|
137
|
+
/** Persist trust after the user confirms a permissioned install. */
|
|
138
|
+
onRememberInstallTrust?: ((entry: ExtensionCatalogBrowseEntry) => void) | undefined;
|
|
126
139
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { createWorkbenchNotify, type WorkbenchNotify } from './createWorkbenchNotify.js';
|
|
4
|
+
import { useWorkbenchNotice } from './WorkbenchNotice.js';
|
|
5
|
+
|
|
6
|
+
/** Convenience hook: NotificationService-shaped API inside a notice provider. */
|
|
7
|
+
export function useWorkbenchNotify(): WorkbenchNotify {
|
|
8
|
+
const controller = useWorkbenchNotice();
|
|
9
|
+
return useMemo(() => createWorkbenchNotify(controller), [controller]);
|
|
10
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import Markdown from 'react-markdown';
|
|
2
2
|
import { Children, isValidElement, type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
3
|
-
import { cx } from '
|
|
4
|
-
import {
|
|
3
|
+
import { cx } from '../../utils/cx';
|
|
4
|
+
import {
|
|
5
|
+
workbenchMarkdownRemarkPlugins,
|
|
6
|
+
workbenchMarkdownRehypePlugins,
|
|
7
|
+
} from './markdownRemarkPlugins';
|
|
8
|
+
import { sanitizeMarkdownHref } from './sanitizeMarkdownHref';
|
|
5
9
|
|
|
6
10
|
export interface WorkbenchMarkdownPreviewProps extends ComponentPropsWithoutRef<'article'> {
|
|
7
11
|
source: string;
|
|
@@ -16,12 +20,23 @@ export function WorkbenchMarkdownPreview({
|
|
|
16
20
|
<article className={cx('ui-workbench-markdown-preview', className)} {...props}>
|
|
17
21
|
<Markdown
|
|
18
22
|
remarkPlugins={workbenchMarkdownRemarkPlugins}
|
|
23
|
+
rehypePlugins={workbenchMarkdownRehypePlugins}
|
|
19
24
|
components={{
|
|
20
|
-
a: ({ children, href }) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
a: ({ children, href }) => {
|
|
26
|
+
const safeHref = sanitizeMarkdownHref(href);
|
|
27
|
+
if (!safeHref) {
|
|
28
|
+
return <span>{children}</span>;
|
|
29
|
+
}
|
|
30
|
+
return (
|
|
31
|
+
<a
|
|
32
|
+
href={safeHref}
|
|
33
|
+
rel="noreferrer"
|
|
34
|
+
target={safeHref.startsWith('#') ? undefined : '_blank'}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</a>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
25
40
|
code: ({ children, className: codeClassName }) => (
|
|
26
41
|
<code className={cx('ui-workbench-markdown-preview__code', codeClassName)}>
|
|
27
42
|
{children}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import remarkGfm from 'remark-gfm';
|
|
2
|
+
import rehypeSanitize from 'rehype-sanitize';
|
|
3
|
+
|
|
4
|
+
/** Shared remark plugins for workbench markdown surfaces (preview, chat, etc.). */
|
|
5
|
+
export const workbenchMarkdownRemarkPlugins = [remarkGfm];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default rehype pipeline for untrusted markdown (chat / preview).
|
|
9
|
+
* Strips script/event handlers and dangerous URLs via rehype-sanitize.
|
|
10
|
+
* Hosts that need raw HTML must render outside these kit surfaces.
|
|
11
|
+
*/
|
|
12
|
+
export const workbenchMarkdownRehypePlugins = [rehypeSanitize];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowlist markdown link targets for safe preview rendering.
|
|
3
|
+
* Blocks javascript:/data:/vbscript: and other non-navigation schemes.
|
|
4
|
+
*/
|
|
5
|
+
export function sanitizeMarkdownHref(href: string | undefined): string | undefined {
|
|
6
|
+
if (href == null || href.trim() === '') {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const trimmed = href.trim();
|
|
11
|
+
if (trimmed.startsWith('#')) {
|
|
12
|
+
return trimmed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const base =
|
|
17
|
+
typeof globalThis.location?.href === 'string' && globalThis.location.href.length > 0
|
|
18
|
+
? globalThis.location.href
|
|
19
|
+
: 'https://workbench.local/';
|
|
20
|
+
const url = new URL(trimmed, base);
|
|
21
|
+
const protocol = url.protocol.toLowerCase();
|
|
22
|
+
if (protocol === 'http:' || protocol === 'https:' || protocol === 'mailto:') {
|
|
23
|
+
return trimmed;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type WorkbenchStructuredDataSchemaFieldInputProps,
|
|
38
38
|
type WorkbenchStructuredDataTextArrayInputProps,
|
|
39
39
|
} from './structuredDataSchema';
|
|
40
|
+
import { isRenderableWorkbenchStructuredDataFormError } from './structuredDataFormModel';
|
|
40
41
|
|
|
41
42
|
export function WorkbenchStructuredDataForm({
|
|
42
43
|
ariaLabel,
|
|
@@ -156,10 +157,6 @@ export function WorkbenchStructuredDataForm({
|
|
|
156
157
|
);
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
function isRenderableWorkbenchStructuredDataFormError(error: ReactNode) {
|
|
160
|
-
return error !== undefined && error !== null && error !== false && error !== '';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
160
|
function StructuredDataSection({
|
|
164
161
|
data,
|
|
165
162
|
disabled,
|
|
@@ -135,6 +135,7 @@ function isWorkbenchStructuredDataFormEmptyValue(value: WorkbenchStructuredDataF
|
|
|
135
135
|
return value === '' || value === false;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
/** True when a form field error should render (non-empty ReactNode). */
|
|
139
|
+
export function isRenderableWorkbenchStructuredDataFormError(error: ReactNode) {
|
|
139
140
|
return error !== undefined && error !== null && error !== false && error !== '';
|
|
140
141
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import './
|
|
1
|
+
import './activity-bar.css';
|
|
2
2
|
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
3
|
-
import { Button } from '
|
|
4
|
-
import { cx } from '
|
|
3
|
+
import { Button } from '../../primitives/button';
|
|
4
|
+
import { cx } from '../../utils/cx';
|
|
5
5
|
import { type ActivityBarOrientation } from './activityBarOrder';
|
|
6
6
|
import {
|
|
7
7
|
mergeWorkbenchSidebarViewPlacementDropZoneProps,
|
|
@@ -20,18 +20,18 @@ export interface ActivityBarItem {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface ActivityBarProps extends Omit<ComponentPropsWithoutRef<'nav'>, 'children'> {
|
|
23
|
-
acceptSidebarViewPlacementDrop?: (
|
|
23
|
+
acceptSidebarViewPlacementDrop?: (viewId: string) => boolean;
|
|
24
24
|
itemDataAttributeName?: string;
|
|
25
25
|
items: ActivityBarItem[];
|
|
26
26
|
onItemActivate?: (item: ActivityBarItem) => void;
|
|
27
27
|
onItemsReorder?: (itemIds: string[]) => void;
|
|
28
|
-
onSidebarViewPlacementDrop?: (
|
|
29
|
-
orientation?: ActivityBarOrientation
|
|
28
|
+
onSidebarViewPlacementDrop?: (viewId: string) => void;
|
|
29
|
+
orientation?: ActivityBarOrientation;
|
|
30
30
|
reorderable?: boolean;
|
|
31
31
|
secondaryItems?: ActivityBarItem[];
|
|
32
|
-
sidebarViewPlacementDropZoneId?: string
|
|
32
|
+
sidebarViewPlacementDropZoneId?: string;
|
|
33
33
|
/** Enables cross-slot placement drag without reordering within the bar. */
|
|
34
|
-
placementDraggable?: boolean
|
|
34
|
+
placementDraggable?: boolean;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/** @deprecated Use WORKBENCH_SIDEBAR_VIEW_PLACEMENT_DRAG_DATA_TYPE */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useState, type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
-
import { EmptyState } from '
|
|
3
|
-
import { IconButton } from '
|
|
4
|
-
import { cx } from '
|
|
5
|
-
import { WorkspacePathLabel } from '
|
|
2
|
+
import { EmptyState } from '../../primitives/empty-state';
|
|
3
|
+
import { IconButton } from '../../primitives/icon-button';
|
|
4
|
+
import { cx } from '../../utils/cx';
|
|
5
|
+
import { WorkspacePathLabel } from '../workspace/WorkspacePathLabel';
|
|
6
6
|
import { SplitView } from './SplitView';
|
|
7
7
|
|
|
8
8
|
export type WorkbenchArtifactMode = 'code' | 'preview' | 'split';
|
|
@@ -31,8 +31,8 @@ export interface WorkbenchPreviewRendererMatch {
|
|
|
31
31
|
|
|
32
32
|
export interface WorkbenchPreviewRendererContext {
|
|
33
33
|
artifact: WorkbenchArtifactDescriptor;
|
|
34
|
-
match?: WorkbenchPreviewRendererMatch
|
|
35
|
-
renderer?: WorkbenchPreviewRenderer
|
|
34
|
+
match?: WorkbenchPreviewRendererMatch;
|
|
35
|
+
renderer?: WorkbenchPreviewRenderer;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export interface WorkbenchPreviewRenderer {
|
|
@@ -58,11 +58,11 @@ export type WorkbenchArtifactShellRenderPreview = (
|
|
|
58
58
|
) => ReactNode;
|
|
59
59
|
|
|
60
60
|
export interface WorkbenchArtifactModeControlsProps extends ComponentPropsWithRef<'div'> {
|
|
61
|
-
codeLabel?: string
|
|
61
|
+
codeLabel?: string;
|
|
62
62
|
mode: WorkbenchArtifactMode;
|
|
63
63
|
onModeChange: (mode: WorkbenchArtifactMode) => void;
|
|
64
|
-
previewLabel?: string
|
|
65
|
-
splitLabel?: string
|
|
64
|
+
previewLabel?: string;
|
|
65
|
+
splitLabel?: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function normalizeToken(value: string | undefined) {
|
|
@@ -229,9 +229,9 @@ export function WorkbenchArtifactModeControls({
|
|
|
229
229
|
|
|
230
230
|
export interface WorkbenchArtifactPreviewProps extends ComponentPropsWithRef<'div'> {
|
|
231
231
|
artifact: WorkbenchArtifactDescriptor;
|
|
232
|
-
renderPreview?: WorkbenchArtifactShellRenderPreview
|
|
233
|
-
renderers?: readonly WorkbenchPreviewRenderer[]
|
|
234
|
-
unsupportedLabel?: ReactNode
|
|
232
|
+
renderPreview?: WorkbenchArtifactShellRenderPreview;
|
|
233
|
+
renderers?: readonly WorkbenchPreviewRenderer[];
|
|
234
|
+
unsupportedLabel?: ReactNode;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
export function WorkbenchArtifactPreview({
|
|
@@ -270,19 +270,19 @@ export interface WorkbenchArtifactShellProps extends Omit<
|
|
|
270
270
|
ComponentPropsWithRef<'div'>,
|
|
271
271
|
'children'
|
|
272
272
|
> {
|
|
273
|
-
artifact?: WorkbenchArtifactDescriptor
|
|
274
|
-
codeLabel?: string
|
|
275
|
-
defaultMode?: WorkbenchArtifactMode
|
|
276
|
-
emptyLabel?: ReactNode
|
|
277
|
-
mode?: WorkbenchArtifactMode
|
|
278
|
-
onModeChange?: (
|
|
279
|
-
previewLabel?: string
|
|
280
|
-
previewRenderers?: readonly WorkbenchPreviewRenderer[]
|
|
281
|
-
renderCode?: WorkbenchArtifactShellRenderCode
|
|
282
|
-
renderPreview?: WorkbenchArtifactShellRenderPreview
|
|
283
|
-
showHeader?: boolean
|
|
284
|
-
splitLabel?: string
|
|
285
|
-
unsupportedPreviewLabel?: ReactNode
|
|
273
|
+
artifact?: WorkbenchArtifactDescriptor;
|
|
274
|
+
codeLabel?: string;
|
|
275
|
+
defaultMode?: WorkbenchArtifactMode;
|
|
276
|
+
emptyLabel?: ReactNode;
|
|
277
|
+
mode?: WorkbenchArtifactMode;
|
|
278
|
+
onModeChange?: (mode: WorkbenchArtifactMode) => void;
|
|
279
|
+
previewLabel?: string;
|
|
280
|
+
previewRenderers?: readonly WorkbenchPreviewRenderer[];
|
|
281
|
+
renderCode?: WorkbenchArtifactShellRenderCode;
|
|
282
|
+
renderPreview?: WorkbenchArtifactShellRenderPreview;
|
|
283
|
+
showHeader?: boolean;
|
|
284
|
+
splitLabel?: string;
|
|
285
|
+
unsupportedPreviewLabel?: ReactNode;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
export function WorkbenchArtifactShell({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import './
|
|
1
|
+
import './split-view.css';
|
|
2
2
|
import {
|
|
3
3
|
useEffect,
|
|
4
4
|
useLayoutEffect,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type PointerEvent,
|
|
10
10
|
type ReactNode,
|
|
11
11
|
} from 'react';
|
|
12
|
-
import { cx } from '
|
|
12
|
+
import { cx } from '../../utils/cx';
|
|
13
13
|
|
|
14
14
|
export type SplitViewOrientation = 'horizontal' | 'vertical';
|
|
15
15
|
export type SplitViewPrimarySizeUnit = 'percent' | 'pixels';
|
|
@@ -42,7 +42,7 @@ export interface SplitViewProps {
|
|
|
42
42
|
onPrimarySizePxPreviewChange?: (primarySizePx: number) => void;
|
|
43
43
|
onSecondarySizePxChange?: (secondarySizePx: number) => void;
|
|
44
44
|
onSecondarySizePxPreviewChange?: (secondarySizePx: number) => void;
|
|
45
|
-
orientation?: SplitViewOrientation
|
|
45
|
+
orientation?: SplitViewOrientation;
|
|
46
46
|
primary: ReactNode;
|
|
47
47
|
primarySizePercent?: number;
|
|
48
48
|
primarySizePx?: number;
|