@dryui/feedback 0.0.2
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/annotation-marker.svelte +163 -0
- package/dist/components/annotation-marker.svelte.d.ts +11 -0
- package/dist/components/annotation-popup.svelte +669 -0
- package/dist/components/annotation-popup.svelte.d.ts +42 -0
- package/dist/components/highlight-overlay.svelte +48 -0
- package/dist/components/highlight-overlay.svelte.d.ts +8 -0
- package/dist/components/settings-panel.svelte +446 -0
- package/dist/components/settings-panel.svelte.d.ts +24 -0
- package/dist/components/toolbar.svelte +1111 -0
- package/dist/components/toolbar.svelte.d.ts +46 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +37 -0
- package/dist/feedback.svelte +2879 -0
- package/dist/feedback.svelte.d.ts +4 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/layout-mode/catalog.d.ts +16 -0
- package/dist/layout-mode/catalog.js +81 -0
- package/dist/layout-mode/component-actions.svelte +84 -0
- package/dist/layout-mode/component-actions.svelte.d.ts +18 -0
- package/dist/layout-mode/component-picker.svelte +73 -0
- package/dist/layout-mode/component-picker.svelte.d.ts +10 -0
- package/dist/layout-mode/design-mode.svelte +1115 -0
- package/dist/layout-mode/design-mode.svelte.d.ts +24 -0
- package/dist/layout-mode/design-palette.svelte +396 -0
- package/dist/layout-mode/design-palette.svelte.d.ts +20 -0
- package/dist/layout-mode/element-heuristics.d.ts +5 -0
- package/dist/layout-mode/element-heuristics.js +51 -0
- package/dist/layout-mode/freeze.d.ts +6 -0
- package/dist/layout-mode/freeze.js +163 -0
- package/dist/layout-mode/generated-library.d.ts +940 -0
- package/dist/layout-mode/generated-library.js +1445 -0
- package/dist/layout-mode/geometry.d.ts +38 -0
- package/dist/layout-mode/geometry.js +133 -0
- package/dist/layout-mode/history.d.ts +10 -0
- package/dist/layout-mode/history.js +45 -0
- package/dist/layout-mode/index.d.ts +23 -0
- package/dist/layout-mode/index.js +18 -0
- package/dist/layout-mode/live-mount.d.ts +20 -0
- package/dist/layout-mode/live-mount.js +70 -0
- package/dist/layout-mode/output.d.ts +26 -0
- package/dist/layout-mode/output.js +550 -0
- package/dist/layout-mode/placement-skeleton.d.ts +9 -0
- package/dist/layout-mode/placement-skeleton.js +535 -0
- package/dist/layout-mode/rearrange-overlay.svelte +1293 -0
- package/dist/layout-mode/rearrange-overlay.svelte.d.ts +18 -0
- package/dist/layout-mode/responsive-bar.svelte +39 -0
- package/dist/layout-mode/responsive-bar.svelte.d.ts +8 -0
- package/dist/layout-mode/route-creator.svelte +70 -0
- package/dist/layout-mode/route-creator.svelte.d.ts +8 -0
- package/dist/layout-mode/section-detection.d.ts +6 -0
- package/dist/layout-mode/section-detection.js +214 -0
- package/dist/layout-mode/spatial.d.ts +42 -0
- package/dist/layout-mode/spatial.js +156 -0
- package/dist/layout-mode/types.d.ts +144 -0
- package/dist/layout-mode/types.js +84 -0
- package/dist/types.d.ts +157 -0
- package/dist/types.js +1 -0
- package/dist/utils/dryui-detection.d.ts +1 -0
- package/dist/utils/dryui-detection.js +219 -0
- package/dist/utils/element-id.d.ts +12 -0
- package/dist/utils/element-id.js +333 -0
- package/dist/utils/freeze.d.ts +7 -0
- package/dist/utils/freeze.js +168 -0
- package/dist/utils/output.d.ts +15 -0
- package/dist/utils/output.js +245 -0
- package/dist/utils/selection.d.ts +22 -0
- package/dist/utils/selection.js +58 -0
- package/dist/utils/shadow-dom.d.ts +4 -0
- package/dist/utils/shadow-dom.js +39 -0
- package/dist/utils/storage.d.ts +30 -0
- package/dist/utils/storage.js +206 -0
- package/dist/utils/svelte-detection.d.ts +8 -0
- package/dist/utils/svelte-detection.js +86 -0
- package/dist/utils/svelte-meta.d.ts +6 -0
- package/dist/utils/svelte-meta.js +69 -0
- package/dist/utils/sync.d.ts +18 -0
- package/dist/utils/sync.js +62 -0
- package/package.json +65 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Rect } from '../types.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
rect: Rect | null;
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { rect, label }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
{#if rect}
|
|
13
|
+
<div
|
|
14
|
+
data-feedback-highlight
|
|
15
|
+
data-dryui-feedback
|
|
16
|
+
style="
|
|
17
|
+
position: fixed;
|
|
18
|
+
left: {rect.x}px;
|
|
19
|
+
top: {rect.y}px;
|
|
20
|
+
width: {rect.width}px;
|
|
21
|
+
height: {rect.height}px;
|
|
22
|
+
background: var(--dry-color-fill-brand-weak, rgba(99, 102, 241, 0.1));
|
|
23
|
+
border: 2px solid var(--dry-color-stroke-brand, rgba(99, 102, 241, 0.5));
|
|
24
|
+
border-radius: var(--dry-radius-sm, 4px);
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
z-index: 9999;
|
|
27
|
+
"
|
|
28
|
+
>
|
|
29
|
+
{#if label}
|
|
30
|
+
<span
|
|
31
|
+
style="
|
|
32
|
+
position: absolute;
|
|
33
|
+
top: -22px;
|
|
34
|
+
left: 0;
|
|
35
|
+
background: var(--dry-color-fill-brand);
|
|
36
|
+
color: var(--dry-color-on-brand, #fff);
|
|
37
|
+
font-size: 11px;
|
|
38
|
+
padding: 1px 6px;
|
|
39
|
+
border-radius: var(--dry-radius-sm, 4px);
|
|
40
|
+
white-space: nowrap;
|
|
41
|
+
pointer-events: none;
|
|
42
|
+
"
|
|
43
|
+
>
|
|
44
|
+
{label}
|
|
45
|
+
</span>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
{/if}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Rect } from '../types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
rect: Rect | null;
|
|
4
|
+
label?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const HighlightOverlay: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type HighlightOverlay = ReturnType<typeof HighlightOverlay>;
|
|
8
|
+
export default HighlightOverlay;
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '@dryui/ui/badge';
|
|
3
|
+
import { Button } from '@dryui/ui/button';
|
|
4
|
+
import { Field } from '@dryui/ui/field';
|
|
5
|
+
import { Flex } from '@dryui/ui/flex';
|
|
6
|
+
import { Input } from '@dryui/ui/input';
|
|
7
|
+
import { Label } from '@dryui/ui/label';
|
|
8
|
+
import { RadioGroup } from '@dryui/ui/radio-group';
|
|
9
|
+
import { SegmentedControl } from '@dryui/ui/segmented-control';
|
|
10
|
+
import { Separator } from '@dryui/ui/separator';
|
|
11
|
+
import { Stack } from '@dryui/ui/stack';
|
|
12
|
+
import { Text } from '@dryui/ui/text';
|
|
13
|
+
import { Toggle } from '@dryui/ui/toggle';
|
|
14
|
+
import { ANNOTATION_COLOR_OPTIONS, DEFAULT_SETTINGS } from '../constants.js';
|
|
15
|
+
import type { ConnectionStatus, FeedbackSettings } from '../types.js';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
settings: FeedbackSettings;
|
|
19
|
+
markersVisible?: boolean;
|
|
20
|
+
onChange: (settings: FeedbackSettings) => void;
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
onPause?: () => void;
|
|
23
|
+
onHide?: () => void;
|
|
24
|
+
onLayout?: () => void;
|
|
25
|
+
onRearrange?: () => void;
|
|
26
|
+
onToggleMarkers?: () => void;
|
|
27
|
+
placementCount?: number;
|
|
28
|
+
sectionCount?: number;
|
|
29
|
+
paused?: boolean;
|
|
30
|
+
hidden?: boolean;
|
|
31
|
+
layoutActive?: boolean;
|
|
32
|
+
rearrangeActive?: boolean;
|
|
33
|
+
connectionStatus?: ConnectionStatus;
|
|
34
|
+
endpoint?: string;
|
|
35
|
+
sessionId?: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let props: Props = $props();
|
|
39
|
+
|
|
40
|
+
const settings = $derived({ ...DEFAULT_SETTINGS, ...props.settings });
|
|
41
|
+
const placementCount = $derived(props.placementCount ?? 0);
|
|
42
|
+
const sectionCount = $derived(props.sectionCount ?? 0);
|
|
43
|
+
const paused = $derived(props.paused ?? false);
|
|
44
|
+
const layoutActive = $derived(props.layoutActive ?? false);
|
|
45
|
+
const rearrangeActive = $derived(props.rearrangeActive ?? false);
|
|
46
|
+
const connectionStatus = $derived(props.connectionStatus ?? 'disconnected');
|
|
47
|
+
const endpoint = $derived(props.endpoint);
|
|
48
|
+
const sessionId = $derived(props.sessionId ?? null);
|
|
49
|
+
const markersVisible = $derived(props.markersVisible ?? true);
|
|
50
|
+
let settingsPage = $state<'main' | 'automations'>('main');
|
|
51
|
+
|
|
52
|
+
const connectionTone = $derived(
|
|
53
|
+
connectionStatus === 'connected' ? 'success' : connectionStatus === 'connecting' ? 'warning' : 'info',
|
|
54
|
+
);
|
|
55
|
+
const connectionCopy = $derived(
|
|
56
|
+
connectionStatus === 'connected'
|
|
57
|
+
? 'Connected'
|
|
58
|
+
: connectionStatus === 'connecting'
|
|
59
|
+
? 'Connecting'
|
|
60
|
+
: 'Disconnected',
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
function patchSettings(patch: Partial<FeedbackSettings>) {
|
|
64
|
+
props.onChange({ ...settings, ...patch });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function openSettingsPage(page: 'main' | 'automations') {
|
|
68
|
+
settingsPage = page;
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<div
|
|
73
|
+
class="settings-surface"
|
|
74
|
+
data-feedback-settings-panel
|
|
75
|
+
data-dryui-feedback
|
|
76
|
+
>
|
|
77
|
+
<Stack gap="lg">
|
|
78
|
+
<Stack gap="sm">
|
|
79
|
+
<Text size="lg">Feedback settings</Text>
|
|
80
|
+
<Text size="sm" color="secondary">
|
|
81
|
+
Control output detail, marker behavior, and interaction blocking.
|
|
82
|
+
</Text>
|
|
83
|
+
</Stack>
|
|
84
|
+
|
|
85
|
+
<Separator />
|
|
86
|
+
|
|
87
|
+
<div class="settings-panel-container">
|
|
88
|
+
<div class:automations={settingsPage === 'automations'} class="settings-panel-track">
|
|
89
|
+
<section class="settings-page main-page" aria-label="Main settings">
|
|
90
|
+
<Stack gap="lg">
|
|
91
|
+
<Field.Root>
|
|
92
|
+
<Label>Output detail</Label>
|
|
93
|
+
<div data-testid="settings-output-detail">
|
|
94
|
+
<SegmentedControl.Root value={settings.outputDetail}>
|
|
95
|
+
<SegmentedControl.Item value="compact" onclick={() => patchSettings({ outputDetail: 'compact' })}>
|
|
96
|
+
Compact
|
|
97
|
+
</SegmentedControl.Item>
|
|
98
|
+
<SegmentedControl.Item value="standard" onclick={() => patchSettings({ outputDetail: 'standard' })}>
|
|
99
|
+
Standard
|
|
100
|
+
</SegmentedControl.Item>
|
|
101
|
+
<SegmentedControl.Item value="detailed" onclick={() => patchSettings({ outputDetail: 'detailed' })}>
|
|
102
|
+
Detailed
|
|
103
|
+
</SegmentedControl.Item>
|
|
104
|
+
<SegmentedControl.Item value="forensic" onclick={() => patchSettings({ outputDetail: 'forensic' })}>
|
|
105
|
+
Forensic
|
|
106
|
+
</SegmentedControl.Item>
|
|
107
|
+
</SegmentedControl.Root>
|
|
108
|
+
</div>
|
|
109
|
+
<Field.Description>
|
|
110
|
+
Standard keeps the summary readable. Forensic includes the most context.
|
|
111
|
+
</Field.Description>
|
|
112
|
+
</Field.Root>
|
|
113
|
+
|
|
114
|
+
<Field.Root>
|
|
115
|
+
<Label>Annotation color</Label>
|
|
116
|
+
<div class="color-grid">
|
|
117
|
+
<RadioGroup.Root
|
|
118
|
+
orientation="vertical"
|
|
119
|
+
value={settings.annotationColor}
|
|
120
|
+
aria-label="Annotation color"
|
|
121
|
+
>
|
|
122
|
+
{#each ANNOTATION_COLOR_OPTIONS as option (option.value)}
|
|
123
|
+
<RadioGroup.Item
|
|
124
|
+
value={option.value}
|
|
125
|
+
onclick={() => patchSettings({ annotationColor: option.value })}
|
|
126
|
+
>
|
|
127
|
+
<span
|
|
128
|
+
class="color-option"
|
|
129
|
+
data-testid={`settings-color-${option.value}`}
|
|
130
|
+
style={`--swatch:${option.swatch};`}
|
|
131
|
+
>
|
|
132
|
+
<span class="swatch" aria-hidden="true"></span>
|
|
133
|
+
<span>{option.label}</span>
|
|
134
|
+
</span>
|
|
135
|
+
</RadioGroup.Item>
|
|
136
|
+
{/each}
|
|
137
|
+
</RadioGroup.Root>
|
|
138
|
+
</div>
|
|
139
|
+
</Field.Root>
|
|
140
|
+
|
|
141
|
+
<Field.Root>
|
|
142
|
+
<Flex align="center" justify="between" gap="md">
|
|
143
|
+
<Stack gap="sm">
|
|
144
|
+
<Text size="sm" weight="medium">Clear on copy or send</Text>
|
|
145
|
+
<Field.Description>Start with a clean slate after exporting annotations.</Field.Description>
|
|
146
|
+
</Stack>
|
|
147
|
+
<Toggle
|
|
148
|
+
data-testid="settings-auto-clear"
|
|
149
|
+
aria-label="Clear on copy or send"
|
|
150
|
+
pressed={settings.autoClearAfterCopy}
|
|
151
|
+
onclick={() => {
|
|
152
|
+
patchSettings({ autoClearAfterCopy: !settings.autoClearAfterCopy });
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
</Flex>
|
|
156
|
+
</Field.Root>
|
|
157
|
+
|
|
158
|
+
<Field.Root>
|
|
159
|
+
<Flex align="center" justify="between" gap="md">
|
|
160
|
+
<Stack gap="sm">
|
|
161
|
+
<Text size="sm" weight="medium">Block page interactions</Text>
|
|
162
|
+
<Field.Description>Prevent clicks from reaching the app while feedback is active.</Field.Description>
|
|
163
|
+
</Stack>
|
|
164
|
+
<Toggle
|
|
165
|
+
data-testid="settings-block-interactions"
|
|
166
|
+
aria-label="Block page interactions"
|
|
167
|
+
pressed={settings.blockInteractions}
|
|
168
|
+
onclick={() => {
|
|
169
|
+
patchSettings({ blockInteractions: !settings.blockInteractions });
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
</Flex>
|
|
173
|
+
</Field.Root>
|
|
174
|
+
|
|
175
|
+
<Field.Root>
|
|
176
|
+
<Flex align="center" justify="between" gap="md">
|
|
177
|
+
<Stack gap="sm">
|
|
178
|
+
<Text size="sm" weight="medium">Detect Svelte component names</Text>
|
|
179
|
+
<Field.Description>Include the Svelte component hierarchy when it is available.</Field.Description>
|
|
180
|
+
</Stack>
|
|
181
|
+
<Toggle
|
|
182
|
+
data-testid="settings-svelte-detection"
|
|
183
|
+
aria-label="Detect Svelte component names"
|
|
184
|
+
pressed={settings.svelteDetection}
|
|
185
|
+
onclick={() => {
|
|
186
|
+
patchSettings({ svelteDetection: !settings.svelteDetection });
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
</Flex>
|
|
190
|
+
</Field.Root>
|
|
191
|
+
|
|
192
|
+
<Field.Root>
|
|
193
|
+
<Flex align="center" justify="between" gap="md">
|
|
194
|
+
<Stack gap="sm">
|
|
195
|
+
<Text size="sm" weight="medium">Show markers</Text>
|
|
196
|
+
<Field.Description>Keep annotation markers visible while feedback mode is active.</Field.Description>
|
|
197
|
+
</Stack>
|
|
198
|
+
<Toggle
|
|
199
|
+
data-testid="settings-markers-visible"
|
|
200
|
+
aria-label="Show markers"
|
|
201
|
+
pressed={markersVisible}
|
|
202
|
+
onclick={() => {
|
|
203
|
+
props.onToggleMarkers?.();
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
206
|
+
</Flex>
|
|
207
|
+
</Field.Root>
|
|
208
|
+
|
|
209
|
+
<Field.Root>
|
|
210
|
+
<Button
|
|
211
|
+
type="button"
|
|
212
|
+
variant="ghost"
|
|
213
|
+
size="sm"
|
|
214
|
+
data-testid="settings-hide-until-restart"
|
|
215
|
+
onclick={() => {
|
|
216
|
+
props.onHide?.();
|
|
217
|
+
}}
|
|
218
|
+
style="width: 100%; justify-content: flex-start;"
|
|
219
|
+
>
|
|
220
|
+
Hide until restart
|
|
221
|
+
</Button>
|
|
222
|
+
<Field.Description>Hide the toolbar until you open a new tab.</Field.Description>
|
|
223
|
+
</Field.Root>
|
|
224
|
+
|
|
225
|
+
<Field.Root>
|
|
226
|
+
<Label>Marker click behavior</Label>
|
|
227
|
+
<div data-testid="settings-marker-behavior">
|
|
228
|
+
<SegmentedControl.Root value={settings.markerClickBehavior}>
|
|
229
|
+
<SegmentedControl.Item value="edit" onclick={() => patchSettings({ markerClickBehavior: 'edit' })}>
|
|
230
|
+
Edit
|
|
231
|
+
</SegmentedControl.Item>
|
|
232
|
+
<SegmentedControl.Item value="delete" onclick={() => patchSettings({ markerClickBehavior: 'delete' })}>
|
|
233
|
+
Delete
|
|
234
|
+
</SegmentedControl.Item>
|
|
235
|
+
</SegmentedControl.Root>
|
|
236
|
+
</div>
|
|
237
|
+
<Field.Description>Choose whether marker clicks reopen a note or remove it.</Field.Description>
|
|
238
|
+
</Field.Root>
|
|
239
|
+
|
|
240
|
+
<Field.Root>
|
|
241
|
+
<Label>Theme</Label>
|
|
242
|
+
<div data-testid="settings-theme">
|
|
243
|
+
<SegmentedControl.Root value={settings.theme}>
|
|
244
|
+
<SegmentedControl.Item value="dark" onclick={() => patchSettings({ theme: 'dark' })}>
|
|
245
|
+
Dark
|
|
246
|
+
</SegmentedControl.Item>
|
|
247
|
+
<SegmentedControl.Item value="light" onclick={() => patchSettings({ theme: 'light' })}>
|
|
248
|
+
Light
|
|
249
|
+
</SegmentedControl.Item>
|
|
250
|
+
<SegmentedControl.Item value="system" onclick={() => patchSettings({ theme: 'system' })}>
|
|
251
|
+
System
|
|
252
|
+
</SegmentedControl.Item>
|
|
253
|
+
</SegmentedControl.Root>
|
|
254
|
+
</div>
|
|
255
|
+
<Field.Description>Match the annotation UI to the surrounding application theme.</Field.Description>
|
|
256
|
+
</Field.Root>
|
|
257
|
+
|
|
258
|
+
{#if placementCount > 0 || sectionCount > 0}
|
|
259
|
+
<Field.Root>
|
|
260
|
+
<Label>Layout state</Label>
|
|
261
|
+
<Text size="sm" color="secondary">
|
|
262
|
+
{placementCount} placement{placementCount === 1 ? '' : 's'} and {sectionCount} section{sectionCount === 1 ? '' : 's'} currently staged.
|
|
263
|
+
</Text>
|
|
264
|
+
</Field.Root>
|
|
265
|
+
{/if}
|
|
266
|
+
|
|
267
|
+
<Button
|
|
268
|
+
variant="ghost"
|
|
269
|
+
size="sm"
|
|
270
|
+
data-testid="settings-automations-btn"
|
|
271
|
+
onclick={() => openSettingsPage('automations')}
|
|
272
|
+
style="width: 100%; justify-content: space-between;"
|
|
273
|
+
>
|
|
274
|
+
<span>Manage MCP & Webhooks</span>
|
|
275
|
+
<span aria-hidden="true">→</span>
|
|
276
|
+
</Button>
|
|
277
|
+
|
|
278
|
+
<Stack direction="horizontal" gap="sm" align="center" style="flex-wrap: wrap;">
|
|
279
|
+
<Button variant="outline" size="sm" data-testid="settings-layout-btn" aria-pressed={layoutActive} onclick={props.onLayout}>
|
|
280
|
+
Layout
|
|
281
|
+
</Button>
|
|
282
|
+
<Button variant="outline" size="sm" data-testid="settings-rearrange-btn" aria-pressed={rearrangeActive} onclick={props.onRearrange}>
|
|
283
|
+
Rearrange
|
|
284
|
+
</Button>
|
|
285
|
+
<Button variant="outline" size="sm" data-testid="settings-pause-btn" aria-pressed={paused} onclick={props.onPause}>
|
|
286
|
+
Pause
|
|
287
|
+
</Button>
|
|
288
|
+
</Stack>
|
|
289
|
+
</Stack>
|
|
290
|
+
</section>
|
|
291
|
+
|
|
292
|
+
<section class="settings-page automations-page" aria-label="MCP and webhooks">
|
|
293
|
+
<Stack gap="lg">
|
|
294
|
+
<Button
|
|
295
|
+
variant="ghost"
|
|
296
|
+
size="sm"
|
|
297
|
+
data-testid="settings-back-btn"
|
|
298
|
+
onclick={() => openSettingsPage('main')}
|
|
299
|
+
style="width: 100%; justify-content: space-between;"
|
|
300
|
+
>
|
|
301
|
+
<span aria-hidden="true">←</span>
|
|
302
|
+
<span>Manage MCP & Webhooks</span>
|
|
303
|
+
</Button>
|
|
304
|
+
|
|
305
|
+
<Separator />
|
|
306
|
+
|
|
307
|
+
<Field.Root>
|
|
308
|
+
<Label>MCP Connection</Label>
|
|
309
|
+
<Stack direction="horizontal" gap="sm" align="center">
|
|
310
|
+
<Badge data-testid="settings-connection-status" variant="soft" color={connectionTone}>
|
|
311
|
+
{connectionCopy}
|
|
312
|
+
</Badge>
|
|
313
|
+
{#if sessionId}
|
|
314
|
+
<Text size="sm" color="secondary">{sessionId}</Text>
|
|
315
|
+
{/if}
|
|
316
|
+
</Stack>
|
|
317
|
+
<Field.Description>
|
|
318
|
+
{#if endpoint}
|
|
319
|
+
{endpoint}
|
|
320
|
+
{:else}
|
|
321
|
+
Configure an endpoint to let AI agents receive annotations in real time.
|
|
322
|
+
{/if}
|
|
323
|
+
</Field.Description>
|
|
324
|
+
</Field.Root>
|
|
325
|
+
|
|
326
|
+
<Field.Root>
|
|
327
|
+
<Flex align="center" justify="between" gap="md">
|
|
328
|
+
<Stack gap="sm">
|
|
329
|
+
<Text size="sm" weight="medium">Enable webhooks</Text>
|
|
330
|
+
<Field.Description>
|
|
331
|
+
Turn on automatic submission to your configured endpoint. The toggle is disabled until a webhook URL is set.
|
|
332
|
+
</Field.Description>
|
|
333
|
+
</Stack>
|
|
334
|
+
<Toggle
|
|
335
|
+
data-testid="settings-webhooks-enabled"
|
|
336
|
+
aria-label="Enable webhooks"
|
|
337
|
+
pressed={settings.webhooksEnabled}
|
|
338
|
+
disabled={!settings.webhookUrl}
|
|
339
|
+
onclick={() => {
|
|
340
|
+
if (!settings.webhookUrl) return;
|
|
341
|
+
patchSettings({ webhooksEnabled: !settings.webhooksEnabled });
|
|
342
|
+
}}
|
|
343
|
+
/>
|
|
344
|
+
</Flex>
|
|
345
|
+
</Field.Root>
|
|
346
|
+
|
|
347
|
+
<Field.Root>
|
|
348
|
+
<Label>Webhook URL</Label>
|
|
349
|
+
<Input
|
|
350
|
+
type="url"
|
|
351
|
+
value={settings.webhookUrl}
|
|
352
|
+
data-testid="settings-webhook-url"
|
|
353
|
+
placeholder="https://example.com/webhook"
|
|
354
|
+
oninput={(event) => {
|
|
355
|
+
patchSettings({ webhookUrl: (event.currentTarget as HTMLInputElement).value });
|
|
356
|
+
}}
|
|
357
|
+
/>
|
|
358
|
+
<Field.Description>Used when webhooks are enabled and the feedback package submits output.</Field.Description>
|
|
359
|
+
</Field.Root>
|
|
360
|
+
</Stack>
|
|
361
|
+
</section>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<Separator />
|
|
366
|
+
|
|
367
|
+
<Button variant="ghost" size="sm" data-testid="settings-close-btn" onclick={props.onClose}>
|
|
368
|
+
Close
|
|
369
|
+
</Button>
|
|
370
|
+
</Stack>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<style>
|
|
374
|
+
.settings-surface {
|
|
375
|
+
width: 100%;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.settings-panel-container {
|
|
379
|
+
overflow: hidden;
|
|
380
|
+
position: relative;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.settings-panel-track {
|
|
384
|
+
display: flex;
|
|
385
|
+
width: 200%;
|
|
386
|
+
transition: transform 0.2s ease;
|
|
387
|
+
will-change: transform;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.settings-panel-track.automations {
|
|
391
|
+
transform: translateX(-50%);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.settings-page {
|
|
395
|
+
box-sizing: border-box;
|
|
396
|
+
width: 50%;
|
|
397
|
+
flex-shrink: 0;
|
|
398
|
+
transition:
|
|
399
|
+
opacity 0.2s ease,
|
|
400
|
+
transform 0.2s ease;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.main-page {
|
|
404
|
+
opacity: 1;
|
|
405
|
+
pointer-events: auto;
|
|
406
|
+
transform: translateX(0);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.automations-page {
|
|
410
|
+
opacity: 0;
|
|
411
|
+
pointer-events: none;
|
|
412
|
+
transform: translateX(24px);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.settings-panel-track.automations .main-page {
|
|
416
|
+
opacity: 0;
|
|
417
|
+
pointer-events: none;
|
|
418
|
+
transform: translateX(-24px);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.settings-panel-track.automations .automations-page {
|
|
422
|
+
opacity: 1;
|
|
423
|
+
pointer-events: auto;
|
|
424
|
+
transform: translateX(-24px);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.color-grid {
|
|
428
|
+
gap: 0.75rem;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.color-option {
|
|
432
|
+
display: inline-flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
gap: 0.5rem;
|
|
435
|
+
min-height: 48px;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.swatch {
|
|
439
|
+
width: 0.875rem;
|
|
440
|
+
height: 0.875rem;
|
|
441
|
+
border-radius: 9999px;
|
|
442
|
+
background: var(--swatch);
|
|
443
|
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
|
|
444
|
+
flex: none;
|
|
445
|
+
}
|
|
446
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ConnectionStatus, FeedbackSettings } from '../types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
settings: FeedbackSettings;
|
|
4
|
+
markersVisible?: boolean;
|
|
5
|
+
onChange: (settings: FeedbackSettings) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onPause?: () => void;
|
|
8
|
+
onHide?: () => void;
|
|
9
|
+
onLayout?: () => void;
|
|
10
|
+
onRearrange?: () => void;
|
|
11
|
+
onToggleMarkers?: () => void;
|
|
12
|
+
placementCount?: number;
|
|
13
|
+
sectionCount?: number;
|
|
14
|
+
paused?: boolean;
|
|
15
|
+
hidden?: boolean;
|
|
16
|
+
layoutActive?: boolean;
|
|
17
|
+
rearrangeActive?: boolean;
|
|
18
|
+
connectionStatus?: ConnectionStatus;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
sessionId?: string | null;
|
|
21
|
+
}
|
|
22
|
+
declare const SettingsPanel: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type SettingsPanel = ReturnType<typeof SettingsPanel>;
|
|
24
|
+
export default SettingsPanel;
|