@agentuity/workbench 0.0.93 → 0.0.95
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/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +3 -1
- package/dist/components/App.js.map +1 -1
- package/dist/components/internal/Chat.d.ts +3 -1
- package/dist/components/internal/Chat.d.ts.map +1 -1
- package/dist/components/internal/Chat.js +5 -5
- package/dist/components/internal/Chat.js.map +1 -1
- package/dist/components/internal/Header.d.ts +4 -0
- package/dist/components/internal/Header.d.ts.map +1 -1
- package/dist/components/internal/Header.js +1 -1
- package/dist/components/internal/Header.js.map +1 -1
- package/dist/components/internal/InputSection.d.ts.map +1 -1
- package/dist/components/internal/InputSection.js +10 -10
- package/dist/components/internal/InputSection.js.map +1 -1
- package/dist/components/internal/MonacoJsonEditor.d.ts.map +1 -1
- package/dist/components/internal/MonacoJsonEditor.js +10 -1
- package/dist/components/internal/MonacoJsonEditor.js.map +1 -1
- package/dist/components/internal/WorkbenchProvider.d.ts +6 -2
- package/dist/components/internal/WorkbenchProvider.d.ts.map +1 -1
- package/dist/components/internal/WorkbenchProvider.js +117 -46
- package/dist/components/internal/WorkbenchProvider.js.map +1 -1
- package/dist/hooks/useAgentSchemas.d.ts.map +1 -1
- package/dist/hooks/useAgentSchemas.js +17 -4
- package/dist/hooks/useAgentSchemas.js.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.d.ts +1 -0
- package/dist/hooks/useWorkbenchWebsocket.d.ts.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.js +1 -1
- package/dist/hooks/useWorkbenchWebsocket.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/config.d.ts +3 -0
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +3 -4
- package/src/components/App.tsx +9 -4
- package/src/components/internal/Chat.tsx +100 -94
- package/src/components/internal/Header.tsx +1 -1
- package/src/components/internal/InputSection.tsx +107 -69
- package/src/components/internal/MonacoJsonEditor.tsx +8 -1
- package/src/components/internal/WorkbenchProvider.tsx +132 -50
- package/src/hooks/useAgentSchemas.ts +16 -4
- package/src/hooks/useWorkbenchWebsocket.ts +2 -1
- package/src/index.ts +2 -1
- package/src/types/config.ts +3 -0
|
@@ -19,13 +19,14 @@ export interface ChatProps {
|
|
|
19
19
|
className?: string;
|
|
20
20
|
schemaOpen: boolean;
|
|
21
21
|
onSchemaToggle: () => void;
|
|
22
|
+
emptyState?: React.ReactNode;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Chat component - conversation and input area (everything except header)
|
|
26
27
|
* Must be used within WorkbenchProvider
|
|
27
28
|
*/
|
|
28
|
-
export function Chat({ className: _className, schemaOpen, onSchemaToggle }: ChatProps) {
|
|
29
|
+
export function Chat({ className: _className, schemaOpen, onSchemaToggle, emptyState }: ChatProps) {
|
|
29
30
|
const logger = useLogger('Chat');
|
|
30
31
|
const {
|
|
31
32
|
agents,
|
|
@@ -35,6 +36,7 @@ export function Chat({ className: _className, schemaOpen, onSchemaToggle }: Chat
|
|
|
35
36
|
setSelectedAgent,
|
|
36
37
|
isLoading,
|
|
37
38
|
submitMessage,
|
|
39
|
+
connectionStatus,
|
|
38
40
|
} = useWorkbench();
|
|
39
41
|
|
|
40
42
|
const [value, setValue] = useState('');
|
|
@@ -63,116 +65,120 @@ export function Chat({ className: _className, schemaOpen, onSchemaToggle }: Chat
|
|
|
63
65
|
return (
|
|
64
66
|
<div className="flex flex-col h-full overflow-hidden">
|
|
65
67
|
<Conversation className="flex-1 overflow-y-auto">
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
? (message as {
|
|
77
|
-
|
|
68
|
+
{connectionStatus === 'disconnected' && emptyState ? (
|
|
69
|
+
<div className="flex flex-col h-full">{emptyState}</div>
|
|
70
|
+
) : (
|
|
71
|
+
<ConversationContent className="pb-0">
|
|
72
|
+
{messages.map((message) => {
|
|
73
|
+
const { role, parts, id } = message;
|
|
74
|
+
const isStreaming = parts.some(
|
|
75
|
+
(part) => part.type === 'text' && part.state === 'streaming'
|
|
76
|
+
);
|
|
77
|
+
const tokens =
|
|
78
|
+
'tokens' in message ? (message as { tokens?: string }).tokens : undefined;
|
|
79
|
+
const duration =
|
|
80
|
+
'duration' in message
|
|
81
|
+
? (message as { duration?: string }).duration
|
|
82
|
+
: undefined;
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
className={cn(
|
|
84
|
-
'w-fit flex items-center mb-2 text-muted-foreground text-sm transition-colors',
|
|
85
|
-
!isStreaming && 'hover:text-foreground cursor-pointer'
|
|
86
|
-
)}
|
|
87
|
-
>
|
|
88
|
-
<Loader
|
|
84
|
+
return (
|
|
85
|
+
<div key={id} className="mb-2">
|
|
86
|
+
{role === 'assistant' && (
|
|
87
|
+
<div
|
|
89
88
|
className={cn(
|
|
90
|
-
'
|
|
91
|
-
isStreaming
|
|
89
|
+
'w-fit flex items-center mb-2 text-muted-foreground text-sm transition-colors',
|
|
90
|
+
!isStreaming && 'hover:text-foreground cursor-pointer'
|
|
92
91
|
)}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<>
|
|
99
|
-
{duration && (
|
|
100
|
-
<>
|
|
101
|
-
Ran for
|
|
102
|
-
<span className="mx-1">{duration}</span>
|
|
103
|
-
</>
|
|
92
|
+
>
|
|
93
|
+
<Loader
|
|
94
|
+
className={cn(
|
|
95
|
+
'size-4 transition-all',
|
|
96
|
+
isStreaming ? 'animate-spin mr-2' : 'w-0 mr-2.5'
|
|
104
97
|
)}
|
|
105
|
-
|
|
106
|
-
{(duration || tokens) && <ChevronRight className="size-4" />}
|
|
107
|
-
</>
|
|
108
|
-
)}
|
|
109
|
-
</div>
|
|
110
|
-
)}
|
|
98
|
+
/>
|
|
111
99
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
100
|
+
{isStreaming ? (
|
|
101
|
+
<Shimmer duration={1}>Running...</Shimmer>
|
|
102
|
+
) : (
|
|
103
|
+
<>
|
|
104
|
+
{duration && (
|
|
105
|
+
<>
|
|
106
|
+
Ran for
|
|
107
|
+
<span className="mx-1">{duration}</span>
|
|
108
|
+
</>
|
|
109
|
+
)}
|
|
110
|
+
{duration && tokens && ` and consumed ${tokens} tokens`}
|
|
111
|
+
{(duration || tokens) && <ChevronRight className="size-4" />}
|
|
112
|
+
</>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
{(role === 'user' || !isStreaming) && (
|
|
118
|
+
<>
|
|
119
|
+
<Message
|
|
120
|
+
key={id}
|
|
121
|
+
from={role as 'user' | 'system' | 'assistant'}
|
|
122
|
+
className="p-0"
|
|
123
|
+
>
|
|
124
|
+
<MessageContent>
|
|
125
|
+
{parts.map((part, index) => {
|
|
126
|
+
switch (part.type) {
|
|
127
|
+
case 'text':
|
|
128
|
+
return (
|
|
129
|
+
<div key={`${id}-${part.text}-${index}`}>
|
|
130
|
+
{part.text || ''}
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
})}
|
|
135
|
+
</MessageContent>
|
|
136
|
+
</Message>
|
|
137
|
+
|
|
138
|
+
<Actions
|
|
139
|
+
className={cn('mt-1 gap-0', role === 'user' && 'justify-end')}
|
|
140
|
+
>
|
|
141
|
+
{role === 'user' && (
|
|
142
|
+
<Action
|
|
143
|
+
label="Retry"
|
|
144
|
+
className="size-8 hover:bg-transparent!"
|
|
145
|
+
onClick={() =>
|
|
146
|
+
setValue(
|
|
147
|
+
parts
|
|
148
|
+
.filter((part) => part.type === 'text')
|
|
149
|
+
.map((part) => part.text)
|
|
150
|
+
.join('')
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
>
|
|
154
|
+
<RefreshCcw className="size-4" />
|
|
155
|
+
</Action>
|
|
156
|
+
)}
|
|
132
157
|
|
|
133
|
-
<Actions
|
|
134
|
-
className={cn('mt-1 gap-0', role === 'user' && 'justify-end')}
|
|
135
|
-
>
|
|
136
|
-
{role === 'user' && (
|
|
137
158
|
<Action
|
|
138
|
-
label="Retry"
|
|
139
|
-
className="size-8 hover:bg-transparent!"
|
|
140
159
|
onClick={() =>
|
|
141
|
-
|
|
160
|
+
navigator.clipboard.writeText(
|
|
142
161
|
parts
|
|
143
162
|
.filter((part) => part.type === 'text')
|
|
144
163
|
.map((part) => part.text)
|
|
145
164
|
.join('')
|
|
146
165
|
)
|
|
147
166
|
}
|
|
167
|
+
label="Copy"
|
|
168
|
+
className="size-8 hover:bg-transparent!"
|
|
148
169
|
>
|
|
149
|
-
<
|
|
170
|
+
<Copy className="size-4" />
|
|
150
171
|
</Action>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
.join('')
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
label="Copy"
|
|
163
|
-
className="size-8 hover:bg-transparent!"
|
|
164
|
-
>
|
|
165
|
-
<Copy className="size-4" />
|
|
166
|
-
</Action>
|
|
167
|
-
</Actions>
|
|
168
|
-
</>
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
171
|
-
);
|
|
172
|
-
})}
|
|
173
|
-
</ConversationContent>
|
|
172
|
+
</Actions>
|
|
173
|
+
</>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
})}
|
|
178
|
+
</ConversationContent>
|
|
179
|
+
)}
|
|
174
180
|
|
|
175
|
-
<ConversationScrollButton />
|
|
181
|
+
{connectionStatus !== 'disconnected' && <ConversationScrollButton />}
|
|
176
182
|
</Conversation>
|
|
177
183
|
<InputSection
|
|
178
184
|
value={value}
|
|
@@ -10,7 +10,7 @@ export interface HeaderProps {
|
|
|
10
10
|
className?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function StatusIndicator({ status }: { status: ConnectionStatus }) {
|
|
13
|
+
export function StatusIndicator({ status }: { status: ConnectionStatus }) {
|
|
14
14
|
if (status === 'connected') {
|
|
15
15
|
return (
|
|
16
16
|
<div className="flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400">
|
|
@@ -25,12 +25,13 @@ import {
|
|
|
25
25
|
} from '../ui/command';
|
|
26
26
|
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
|
27
27
|
import { Select, SelectContent, SelectItem, SelectTrigger } from '../ui/select';
|
|
28
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
|
28
29
|
import { cn } from '../../lib/utils';
|
|
29
30
|
import type { AgentSchemaData } from '../../hooks/useAgentSchemas';
|
|
30
31
|
import { useLogger } from '../../hooks/useLogger';
|
|
31
32
|
import type { JSONSchema7 } from 'ai';
|
|
33
|
+
import { useWorkbench } from './WorkbenchProvider';
|
|
32
34
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
33
|
-
import { zocker } from 'zocker';
|
|
34
35
|
|
|
35
36
|
export interface InputSectionProps {
|
|
36
37
|
value: string;
|
|
@@ -70,6 +71,7 @@ export function InputSection({
|
|
|
70
71
|
onSchemaToggle,
|
|
71
72
|
}: InputSectionProps) {
|
|
72
73
|
const logger = useLogger('InputSection');
|
|
74
|
+
const { generateSample, isGeneratingSample, isAuthenticated } = useWorkbench();
|
|
73
75
|
const [agentSelectOpen, setAgentSelectOpen] = useState(false);
|
|
74
76
|
const [isValidInput, setIsValidInput] = useState(true);
|
|
75
77
|
const [monacoHasErrors, setMonacoHasErrors] = useState<boolean | null>(null);
|
|
@@ -159,18 +161,14 @@ export function InputSection({
|
|
|
159
161
|
monacoHasErrors,
|
|
160
162
|
]);
|
|
161
163
|
|
|
162
|
-
const handleGenerateSample = () => {
|
|
163
|
-
if (!selectedAgentData?.schema.input?.json || !isObjectSchema) return;
|
|
164
|
+
const handleGenerateSample = async () => {
|
|
165
|
+
if (!selectedAgentData?.schema.input?.json || !isObjectSchema || !selectedAgent) return;
|
|
164
166
|
|
|
165
167
|
try {
|
|
166
|
-
const
|
|
167
|
-
const schemaObject = typeof jsonSchema === 'string' ? JSON.parse(jsonSchema) : jsonSchema;
|
|
168
|
-
|
|
169
|
-
const zodSchema = convertJsonSchemaToZod(schemaObject);
|
|
170
|
-
const sampleData = zocker(zodSchema).generate();
|
|
171
|
-
const sampleJson = JSON.stringify(sampleData, null, 2);
|
|
168
|
+
const sampleJson = await generateSample(selectedAgent);
|
|
172
169
|
onChange(sampleJson);
|
|
173
170
|
} catch (error) {
|
|
171
|
+
logger.error('Failed to generate sample JSON:', error);
|
|
174
172
|
console.error('Failed to generate sample JSON:', error);
|
|
175
173
|
}
|
|
176
174
|
};
|
|
@@ -268,17 +266,49 @@ export function InputSection({
|
|
|
268
266
|
</Select>
|
|
269
267
|
)}
|
|
270
268
|
|
|
271
|
-
{isObjectSchema &&
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
269
|
+
{isObjectSchema &&
|
|
270
|
+
(isAuthenticated ? (
|
|
271
|
+
<Button
|
|
272
|
+
aria-label="Generate Sample JSON"
|
|
273
|
+
size="sm"
|
|
274
|
+
variant="outline"
|
|
275
|
+
className="bg-none font-normal"
|
|
276
|
+
onClick={handleGenerateSample}
|
|
277
|
+
disabled={isGeneratingSample || !isAuthenticated}
|
|
278
|
+
>
|
|
279
|
+
{isGeneratingSample ? (
|
|
280
|
+
<Loader2Icon className="size-4 animate-spin" />
|
|
281
|
+
) : (
|
|
282
|
+
<Sparkles className="size-4" />
|
|
283
|
+
)}{' '}
|
|
284
|
+
Sample
|
|
285
|
+
</Button>
|
|
286
|
+
) : (
|
|
287
|
+
<Tooltip>
|
|
288
|
+
<TooltipTrigger asChild>
|
|
289
|
+
<span className="inline-flex">
|
|
290
|
+
<Button
|
|
291
|
+
aria-label="Generate Sample JSON"
|
|
292
|
+
size="sm"
|
|
293
|
+
variant="outline"
|
|
294
|
+
className="bg-none font-normal"
|
|
295
|
+
onClick={handleGenerateSample}
|
|
296
|
+
disabled={isGeneratingSample || !isAuthenticated}
|
|
297
|
+
>
|
|
298
|
+
{isGeneratingSample ? (
|
|
299
|
+
<Loader2Icon className="size-4 animate-spin" />
|
|
300
|
+
) : (
|
|
301
|
+
<Sparkles className="size-4" />
|
|
302
|
+
)}{' '}
|
|
303
|
+
Sample
|
|
304
|
+
</Button>
|
|
305
|
+
</span>
|
|
306
|
+
</TooltipTrigger>
|
|
307
|
+
<TooltipContent>
|
|
308
|
+
<p>Login to generate a sample</p>
|
|
309
|
+
</TooltipContent>
|
|
310
|
+
</Tooltip>
|
|
311
|
+
))}
|
|
282
312
|
|
|
283
313
|
<Button
|
|
284
314
|
aria-label={isSchemaOpen ? 'Hide Schema' : 'View Schema'}
|
|
@@ -293,58 +323,66 @@ export function InputSection({
|
|
|
293
323
|
|
|
294
324
|
<PromptInput onSubmit={onSubmit} className="px-3 pb-3">
|
|
295
325
|
<PromptInputBody>
|
|
296
|
-
{
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
326
|
+
{!selectedAgent ? (
|
|
327
|
+
<div className="flex flex-col items-center justify-center py-8 px-4 text-center">
|
|
328
|
+
<p className="text-sm text-muted-foreground">
|
|
329
|
+
Select an agent to get started.
|
|
330
|
+
</p>
|
|
331
|
+
</div>
|
|
332
|
+
) : (
|
|
333
|
+
(() => {
|
|
334
|
+
switch (inputType) {
|
|
335
|
+
case 'object':
|
|
336
|
+
return (
|
|
337
|
+
<MonacoJsonEditor
|
|
338
|
+
value={value}
|
|
339
|
+
onChange={onChange}
|
|
340
|
+
schema={selectedAgentData?.schema.input?.json}
|
|
341
|
+
schemaUri={`agentuity://schema/${selectedAgentData?.metadata.id}/input`}
|
|
342
|
+
aria-invalid={!isValidInput}
|
|
343
|
+
onValidationChange={setMonacoHasErrors}
|
|
344
|
+
/>
|
|
345
|
+
);
|
|
309
346
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
case 'string':
|
|
348
|
+
return (
|
|
349
|
+
<PromptInputTextarea
|
|
350
|
+
placeholder="Enter a message to send..."
|
|
351
|
+
value={value}
|
|
352
|
+
onChange={(e) => onChange(e.target.value)}
|
|
353
|
+
/>
|
|
354
|
+
);
|
|
355
|
+
default:
|
|
356
|
+
return (
|
|
357
|
+
<div className="flex flex-col items-center justify-center py-8 px-4 text-center ">
|
|
358
|
+
<p className="text-sm text-muted-foreground">
|
|
359
|
+
<span className="font-medium">
|
|
360
|
+
This agent has no input schema.{' '}
|
|
361
|
+
</span>
|
|
362
|
+
</p>
|
|
363
|
+
<Button
|
|
364
|
+
aria-label="Run Agent"
|
|
365
|
+
size="sm"
|
|
366
|
+
variant="default"
|
|
367
|
+
disabled={isLoading}
|
|
368
|
+
onClick={onSubmit}
|
|
369
|
+
className="mt-2"
|
|
370
|
+
>
|
|
371
|
+
{isLoading ? (
|
|
372
|
+
<Loader2Icon className="size-4 animate-spin mr-2" />
|
|
373
|
+
) : (
|
|
374
|
+
<SendIcon className="size-4 mr-2" />
|
|
375
|
+
)}
|
|
376
|
+
Run Agent
|
|
377
|
+
</Button>
|
|
378
|
+
</div>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
})()
|
|
382
|
+
)}
|
|
345
383
|
</PromptInputBody>
|
|
346
384
|
<PromptInputFooter>
|
|
347
|
-
{inputType !== 'none' && (
|
|
385
|
+
{selectedAgent && inputType !== 'none' && (
|
|
348
386
|
<Button
|
|
349
387
|
aria-label="Submit"
|
|
350
388
|
size="icon"
|
|
@@ -167,7 +167,8 @@ export function MonacoJsonEditor({
|
|
|
167
167
|
style={{ minHeight: '64px', maxHeight: '192px', height: `${editorHeight}px` }}
|
|
168
168
|
>
|
|
169
169
|
<Editor
|
|
170
|
-
|
|
170
|
+
// Allow the editor to be truly empty. We intentionally do NOT coerce to `{}`.
|
|
171
|
+
value={value}
|
|
171
172
|
onChange={(newValue) => onChange(newValue || '')}
|
|
172
173
|
language="json"
|
|
173
174
|
theme={resolvedTheme === 'light' ? 'custom-light' : 'custom-dark'}
|
|
@@ -243,6 +244,12 @@ export function MonacoJsonEditor({
|
|
|
243
244
|
const checkValidationErrors = () => {
|
|
244
245
|
const model = editor.getModel();
|
|
245
246
|
if (model) {
|
|
247
|
+
// Treat an empty editor as a valid "empty state" (avoid Monaco's JSON parse error).
|
|
248
|
+
if (!model.getValue().trim()) {
|
|
249
|
+
onValidationChange(false);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
246
253
|
const markers = monaco.editor.getModelMarkers({ resource: model.uri });
|
|
247
254
|
const hasErrors = markers.some(
|
|
248
255
|
(marker: monaco.editor.IMarker) =>
|