@workbench-kit/react 0.0.1-prototype.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 (51) hide show
  1. package/package.json +62 -0
  2. package/src/index.ts +43 -0
  3. package/src/layout/Panel.tsx +27 -0
  4. package/src/layout/SideBarViewFrame.tsx +328 -0
  5. package/src/modal/ConfirmDialog.tsx +53 -0
  6. package/src/modal/Modal.tsx +110 -0
  7. package/src/overlay/ContextMenu.tsx +145 -0
  8. package/src/primitives/Badge.tsx +12 -0
  9. package/src/primitives/Button.tsx +14 -0
  10. package/src/primitives/Checkbox.tsx +15 -0
  11. package/src/primitives/EmptyState.tsx +26 -0
  12. package/src/primitives/Field.tsx +38 -0
  13. package/src/primitives/IconButton.tsx +32 -0
  14. package/src/primitives/Select.tsx +12 -0
  15. package/src/primitives/TextInput.tsx +24 -0
  16. package/src/primitives/Toolbar.tsx +8 -0
  17. package/src/styles.css +1762 -0
  18. package/src/utils/cx.ts +3 -0
  19. package/src/workbench/ActivityBar.tsx +52 -0
  20. package/src/workbench/SplitView.tsx +136 -0
  21. package/src/workbench/StatusBar.tsx +123 -0
  22. package/src/workbench/WorkbenchShell.tsx +70 -0
  23. package/src/workbench/WorkbenchStandaloneShell.tsx +291 -0
  24. package/src/workbench/chat/ChatComposer.tsx +148 -0
  25. package/src/workbench/chat/ChatMessageItem.tsx +35 -0
  26. package/src/workbench/chat/ChatMessageList.tsx +49 -0
  27. package/src/workbench/chat/ChatPanel.tsx +55 -0
  28. package/src/workbench/chat/index.ts +9 -0
  29. package/src/workbench/chat/types.ts +10 -0
  30. package/src/workbench/commands.ts +490 -0
  31. package/src/workbench/index.ts +98 -0
  32. package/src/workbench/settings/WorkbenchSettingsModal.tsx +188 -0
  33. package/src/workbench/settings/WorkbenchSettingsNav.tsx +41 -0
  34. package/src/workbench/settings/WorkbenchSettingsSection.tsx +30 -0
  35. package/src/workbench/settings/index.ts +7 -0
  36. package/src/workbench/settings/types.ts +16 -0
  37. package/src/workbench/shellState.ts +203 -0
  38. package/src/workbench/standalone.ts +94 -0
  39. package/src/workbench/workspace/WorkspaceEditor.tsx +226 -0
  40. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +353 -0
  41. package/src/workbench/workspace/WorkspaceExplorer.tsx +524 -0
  42. package/src/workbench/workspace/WorkspaceFileIcon.tsx +149 -0
  43. package/src/workbench/workspace/WorkspaceHighlightedText.tsx +22 -0
  44. package/src/workbench/workspace/WorkspaceSearchPanel.tsx +113 -0
  45. package/src/workbench/workspace/WorkspaceSearchResults.tsx +47 -0
  46. package/src/workbench/workspace/index.ts +98 -0
  47. package/src/workbench/workspace/path.ts +10 -0
  48. package/src/workbench/workspace/search.ts +6 -0
  49. package/src/workbench/workspace/tree.ts +1 -0
  50. package/src/workbench/workspace/types.ts +10 -0
  51. package/src/workbench/workspace/useVirtualWorkspace.ts +98 -0
@@ -0,0 +1,148 @@
1
+ import {
2
+ useLayoutEffect,
3
+ useRef,
4
+ type KeyboardEvent,
5
+ type ReactNode,
6
+ type TextareaHTMLAttributes,
7
+ } from 'react';
8
+ import { cx } from '../../utils/cx';
9
+
10
+ export interface ChatComposerProps extends Omit<
11
+ TextareaHTMLAttributes<HTMLTextAreaElement>,
12
+ 'onChange' | 'onSubmit' | 'value'
13
+ > {
14
+ cancelLabel?: string;
15
+ commandLabel?: string;
16
+ contextLabel?: string;
17
+ isRunning?: boolean;
18
+ onCancel?: () => void;
19
+ onSubmit: (message: string) => void;
20
+ onValueChange: (value: string) => void;
21
+ showTools?: boolean;
22
+ submitLabel?: string;
23
+ toolbarStart?: ReactNode;
24
+ value: string;
25
+ }
26
+
27
+ export function ChatComposer({
28
+ cancelLabel = 'Stop response',
29
+ className,
30
+ commandLabel = 'Open commands',
31
+ contextLabel = 'Add context',
32
+ disabled,
33
+ isRunning = false,
34
+ onCancel,
35
+ onSubmit,
36
+ onValueChange,
37
+ placeholder = 'Type a message...',
38
+ showTools = true,
39
+ submitLabel = 'Send message',
40
+ toolbarStart,
41
+ value,
42
+ ...props
43
+ }: ChatComposerProps) {
44
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
45
+
46
+ const resizeTextarea = () => {
47
+ const element = textareaRef.current;
48
+ if (!element) return;
49
+
50
+ element.style.height = 'auto';
51
+ element.style.height = `${Math.min(element.scrollHeight, 160)}px`;
52
+ };
53
+
54
+ useLayoutEffect(() => {
55
+ resizeTextarea();
56
+ }, [value]);
57
+
58
+ const handleSubmit = () => {
59
+ const trimmed = value.trim();
60
+ if (!trimmed || disabled || isRunning) return;
61
+
62
+ onSubmit(trimmed);
63
+ window.requestAnimationFrame(() => {
64
+ if (!textareaRef.current) return;
65
+
66
+ textareaRef.current.style.height = 'auto';
67
+ textareaRef.current.focus();
68
+ });
69
+ };
70
+
71
+ const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
72
+ props.onKeyDown?.(event);
73
+ if (event.defaultPrevented) return;
74
+
75
+ if (event.key === 'Enter' && !event.shiftKey) {
76
+ event.preventDefault();
77
+ handleSubmit();
78
+ }
79
+ };
80
+
81
+ return (
82
+ <div className="composer">
83
+ <div className="composer__box">
84
+ <textarea
85
+ ref={textareaRef}
86
+ {...props}
87
+ className={cx('composer__textarea', className)}
88
+ disabled={disabled}
89
+ placeholder={placeholder}
90
+ rows={1}
91
+ value={value}
92
+ onChange={(event) => onValueChange(event.currentTarget.value)}
93
+ onInput={resizeTextarea}
94
+ onKeyDown={handleKeyDown}
95
+ />
96
+ <div className="composer__toolbar">
97
+ <div className="composer__toolbar-left">
98
+ {toolbarStart}
99
+ {showTools ? (
100
+ <>
101
+ <button
102
+ aria-label={contextLabel}
103
+ className="composer__tool-btn"
104
+ title={contextLabel}
105
+ type="button"
106
+ >
107
+ <i className="codicon codicon-add" />
108
+ </button>
109
+ <button
110
+ aria-label={commandLabel}
111
+ className="composer__tool-btn"
112
+ title={commandLabel}
113
+ type="button"
114
+ >
115
+ <i className="codicon codicon-terminal" />
116
+ </button>
117
+ </>
118
+ ) : null}
119
+ </div>
120
+ <div className="composer__toolbar-right">
121
+ {isRunning ? (
122
+ <button
123
+ aria-label={cancelLabel}
124
+ className="composer__send-btn composer__send-btn--cancel"
125
+ title={cancelLabel}
126
+ type="button"
127
+ onClick={onCancel}
128
+ >
129
+ <i className="codicon codicon-stop-circle" />
130
+ </button>
131
+ ) : (
132
+ <button
133
+ aria-label={submitLabel}
134
+ className="composer__send-btn"
135
+ disabled={disabled || !value.trim()}
136
+ title={submitLabel}
137
+ type="button"
138
+ onClick={handleSubmit}
139
+ >
140
+ <i className="codicon codicon-send" />
141
+ </button>
142
+ )}
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ );
148
+ }
@@ -0,0 +1,35 @@
1
+ import Markdown from 'react-markdown';
2
+ import type { ChatMessage } from './types';
3
+
4
+ export interface ChatMessageItemProps {
5
+ assistantLabel?: string;
6
+ isStreaming?: boolean;
7
+ message: ChatMessage;
8
+ }
9
+
10
+ export function ChatMessageItem({
11
+ assistantLabel = 'Assistant',
12
+ isStreaming = false,
13
+ message,
14
+ }: ChatMessageItemProps) {
15
+ if (message.source === 'user') {
16
+ return (
17
+ <div className="message message--user">
18
+ <div className="message__bubble">{message.content}</div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+ return (
24
+ <div className="message message--assistant">
25
+ <div className="message__label message__label--assistant">
26
+ <i className="codicon codicon-sparkle message__label-icon" />
27
+ {message.label ?? assistantLabel}
28
+ </div>
29
+ <div className="md-content">
30
+ <Markdown>{message.content}</Markdown>
31
+ {isStreaming ? <span aria-hidden="true" className="message__cursor" /> : null}
32
+ </div>
33
+ </div>
34
+ );
35
+ }
@@ -0,0 +1,49 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { SideBarScrollSpacer } from '../../layout/SideBarViewFrame';
3
+ import { ChatMessageItem } from './ChatMessageItem';
4
+ import type { ChatMessage } from './types';
5
+
6
+ export interface ChatMessageListProps {
7
+ assistantLabel?: string;
8
+ emptyLabel?: string;
9
+ isStreaming?: boolean;
10
+ messages: ChatMessage[];
11
+ }
12
+
13
+ export function ChatMessageList({
14
+ assistantLabel,
15
+ emptyLabel = 'How can I help?',
16
+ isStreaming = false,
17
+ messages,
18
+ }: ChatMessageListProps) {
19
+ const bottomRef = useRef<HTMLDivElement>(null);
20
+
21
+ useEffect(() => {
22
+ bottomRef.current?.scrollIntoView({ behavior: 'auto', block: 'end' });
23
+ }, [isStreaming, messages.length]);
24
+
25
+ if (messages.length === 0) {
26
+ return (
27
+ <div className="message-empty">
28
+ <i className="codicon codicon-sparkle" />
29
+ <span>{emptyLabel}</span>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ return (
35
+ <div className="message-list">
36
+ {messages.map((message, index) => (
37
+ <ChatMessageItem
38
+ key={message.id}
39
+ assistantLabel={assistantLabel}
40
+ isStreaming={
41
+ isStreaming && index === messages.length - 1 && message.source === 'assistant'
42
+ }
43
+ message={message}
44
+ />
45
+ ))}
46
+ <SideBarScrollSpacer ref={bottomRef} />
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,55 @@
1
+ import { SideBarViewFrame } from '../../layout/SideBarViewFrame';
2
+ import { ChatComposer, type ChatComposerProps } from './ChatComposer';
3
+ import { ChatMessageList, type ChatMessageListProps } from './ChatMessageList';
4
+
5
+ export interface ChatPanelProps
6
+ extends
7
+ ChatMessageListProps,
8
+ Pick<
9
+ ChatComposerProps,
10
+ | 'disabled'
11
+ | 'isRunning'
12
+ | 'onCancel'
13
+ | 'onSubmit'
14
+ | 'onValueChange'
15
+ | 'placeholder'
16
+ | 'showTools'
17
+ | 'value'
18
+ > {
19
+ title?: string;
20
+ }
21
+
22
+ export function ChatPanel({
23
+ title = 'Chat',
24
+ value,
25
+ onValueChange,
26
+ onSubmit,
27
+ onCancel,
28
+ placeholder,
29
+ disabled,
30
+ isRunning,
31
+ showTools,
32
+ ...messageListProps
33
+ }: ChatPanelProps) {
34
+ return (
35
+ <SideBarViewFrame
36
+ className="chat-side-bar-view"
37
+ footer={
38
+ <ChatComposer
39
+ disabled={disabled}
40
+ isRunning={isRunning}
41
+ placeholder={placeholder}
42
+ showTools={showTools}
43
+ value={value}
44
+ onCancel={onCancel}
45
+ onSubmit={onSubmit}
46
+ onValueChange={onValueChange}
47
+ />
48
+ }
49
+ footerPlacement="overlay"
50
+ title={title}
51
+ >
52
+ <ChatMessageList isStreaming={isRunning} {...messageListProps} />
53
+ </SideBarViewFrame>
54
+ );
55
+ }
@@ -0,0 +1,9 @@
1
+ export { ChatComposer } from './ChatComposer';
2
+ export type { ChatComposerProps } from './ChatComposer';
3
+ export { ChatMessageItem } from './ChatMessageItem';
4
+ export type { ChatMessageItemProps } from './ChatMessageItem';
5
+ export { ChatMessageList } from './ChatMessageList';
6
+ export type { ChatMessageListProps } from './ChatMessageList';
7
+ export { ChatPanel } from './ChatPanel';
8
+ export type { ChatPanelProps } from './ChatPanel';
9
+ export type { ChatMessage, ChatMessageSource } from './types';
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export type ChatMessageSource = 'assistant' | 'user';
4
+
5
+ export interface ChatMessage {
6
+ content: string;
7
+ id: string;
8
+ label?: ReactNode;
9
+ source: ChatMessageSource;
10
+ }