@viamrobotics/test-widgets 0.10.2 → 0.11.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.
@@ -10,17 +10,17 @@
10
10
 
11
11
  <dl class="font-roboto-mono flex flex-col gap-y-2 text-sm">
12
12
  <div class="flex flex-col gap-0.5">
13
- <dt class="text-default w-50 min-w-50 pr-2 font-medium">Supported Codecs</dt>
13
+ <dt class="text-default pr-2 font-medium">Supported Codecs</dt>
14
14
  <dd class="text-subtle-1">
15
15
  {supportedCodecs.length > 0 ? supportedCodecs.join(', ') : 'None'}
16
16
  </dd>
17
17
  </div>
18
18
  <div class="flex flex-col gap-0.5">
19
- <dt class="text-default w-50 min-w-50 pr-2 font-medium">Sample Rate</dt>
19
+ <dt class="text-default pr-2 font-medium">Sample Rate</dt>
20
20
  <dd class="text-subtle-1">{sampleRateHz} Hz</dd>
21
21
  </div>
22
22
  <div class="flex flex-col gap-0.5">
23
- <dt class="text-default w-50 min-w-50 pr-2 font-medium">Channels</dt>
23
+ <dt class="text-default pr-2 font-medium">Channels</dt>
24
24
  <dd class="text-subtle-1">{numChannels}</dd>
25
25
  </div>
26
26
  </dl>
@@ -78,9 +78,7 @@
78
78
  }}>{isErrorExpanded ? 'Hide' : 'Show'} error</button
79
79
  >
80
80
  {#if isErrorExpanded}
81
- <pre class="font-mono text-xs text-wrap">
82
- {error}
83
- </pre>
81
+ <pre class="font-mono text-xs text-wrap">{error}</pre>
84
82
  {/if}
85
83
  </div>
86
84
  {/snippet}
@@ -39,18 +39,23 @@
39
39
  <div class="flex items-center justify-between gap-1">
40
40
  <p
41
41
  {id}
42
- class={twMerge('font-roboto-mono text-danger-dark text-xs', className)}
42
+ class={twMerge(
43
+ 'font-roboto-mono text-danger-dark min-w-0 overflow-auto text-xs wrap-break-word',
44
+ className
45
+ )}
43
46
  >
44
47
  {errorText}
45
48
  </p>
46
- <Tooltip let:tooltipID>
47
- <IconButton
48
- aria-describedby={tooltipID}
49
- icon={showCopySuccess ? 'check' : 'content-copy'}
50
- label="Copy error"
51
- on:click={copyErrorToClipboard}
52
- />
53
- <div slot="description">Copy error</div>
54
- </Tooltip>
49
+ <div class="shrink-0">
50
+ <Tooltip let:tooltipID>
51
+ <IconButton
52
+ aria-describedby={tooltipID}
53
+ icon={showCopySuccess ? 'check' : 'content-copy'}
54
+ label="Copy error"
55
+ on:click={copyErrorToClipboard}
56
+ />
57
+ <div slot="description">Copy error</div>
58
+ </Tooltip>
59
+ </div>
55
60
  </div>
56
61
  {/if}
@@ -39,35 +39,35 @@
39
39
  <div slot="description">Copy JSON</div>
40
40
  </Tooltip>
41
41
  </div>
42
- <dl class="font-roboto-mono flex flex-col gap-y-2">
43
- {#each sortedData as [key, value] (key)}
44
- {@const valueString = JSON.stringify(value, null, 2)}
45
- <div class="flex flex-row">
46
- <dt
47
- class="text-default w-50 min-w-50 truncate pr-2 font-medium"
48
- title={key}
49
- >
50
- {key}
51
- </dt>
52
- <dd class="text-subtle-1 min-w-50">
53
- {#if typeof value === 'object'}
54
- <div class="border-light bg-extralight min-w-50 border px-3 py-2">
55
- <pre class="whitespace-pre-wrap">
56
- <code>{valueString}</code>
57
- </pre>
58
- </div>
59
- {:else if typeof value === 'string' && value.startsWith('data:image/')}
60
- <img
61
- class="max-h-80"
62
- src={value}
63
- alt={key}
64
- />
65
- {:else}
66
- {truncate(valueString, { length: 300 })}
67
- {/if}
68
- </dd>
69
- </div>
70
- {/each}
71
- </dl>
42
+ <div class="@container">
43
+ <dl class="font-roboto-mono flex flex-col gap-y-2">
44
+ {#each sortedData as [key, value] (key)}
45
+ {@const valueString = JSON.stringify(value, null, 2)}
46
+ <div class="flex flex-col gap-y-1 @lg:flex-row @lg:gap-y-0">
47
+ <dt
48
+ class="text-default truncate pr-2 font-medium @lg:w-50 @lg:min-w-50"
49
+ title={key}
50
+ >
51
+ {key}
52
+ </dt>
53
+ <dd class="text-subtle-1 min-w-0 grow wrap-break-word">
54
+ {#if value !== null && typeof value === 'object'}
55
+ <div class="border-light bg-extralight border px-3 py-2">
56
+ <pre class="whitespace-pre-wrap"><code>{valueString}</code></pre>
57
+ </div>
58
+ {:else if typeof value === 'string' && value.startsWith('data:image/')}
59
+ <img
60
+ class="max-h-80 max-w-full"
61
+ src={value}
62
+ alt={key}
63
+ />
64
+ {:else}
65
+ {truncate(valueString, { length: 300 })}
66
+ {/if}
67
+ </dd>
68
+ </div>
69
+ {/each}
70
+ </dl>
71
+ </div>
72
72
  </div>
73
73
  {/if}
@@ -91,8 +91,8 @@
91
91
  })
92
92
  </script>
93
93
 
94
- <div class="text-default flex flex-row gap-2 text-xs">
95
- <div class="w-50">
94
+ <div class="text-default flex flex-wrap gap-2 text-xs">
95
+ <div class="w-50 max-w-full">
96
96
  <Select on:change={onChange}>
97
97
  {#each refetchOptions as option (option)}
98
98
  <option selected={selectedOption === option}>{option}</option>
@@ -58,97 +58,99 @@
58
58
  />
59
59
  </div>
60
60
 
61
- <div class="flex flex-row divide-x">
62
- <div class="flex w-full flex-col divide-y">
63
- <MutationSection
64
- title="GetAudio"
65
- api="rdk:component:audio_input"
66
- description="Capture audio from the device"
67
- lastError={capture.error}
68
- >
69
- <div class="flex flex-col gap-2">
70
- <Label cx="gap-1 text-xs">
71
- Codec
72
- <Select
73
- slot="input"
74
- value={selectedCodec}
75
- on:change={(e) => {
76
- captureCodec = (e.target as HTMLSelectElement).value
77
- }}
78
- >
79
- {#each availableCodecs as codec (codec)}
80
- <option value={codec}>{codec}</option>
81
- {/each}
82
- </Select>
83
- </Label>
84
- <Label cx="gap-1 text-xs">
85
- Duration (seconds, 0 = stream until stopped)
86
- <NumericInput
87
- slot="input"
88
- value={captureDuration}
89
- on:change={(e) => {
90
- captureDuration = numberValueFromEvent(e) ?? 3
91
- }}
92
- />
93
- </Label>
94
- </div>
61
+ <div class="@container">
62
+ <div class="flex flex-col divide-y @2xl:flex-row @2xl:divide-x @2xl:divide-y-0">
63
+ <div class="flex w-full flex-col divide-y">
64
+ <MutationSection
65
+ title="GetAudio"
66
+ api="rdk:component:audio_input"
67
+ description="Capture audio from the device"
68
+ lastError={capture.error}
69
+ >
70
+ <div class="flex flex-col gap-2">
71
+ <Label cx="gap-1 text-xs">
72
+ Codec
73
+ <Select
74
+ slot="input"
75
+ value={selectedCodec}
76
+ on:change={(e) => {
77
+ captureCodec = (e.target as HTMLSelectElement).value
78
+ }}
79
+ >
80
+ {#each availableCodecs as codec (codec)}
81
+ <option value={codec}>{codec}</option>
82
+ {/each}
83
+ </Select>
84
+ </Label>
85
+ <Label cx="gap-1 text-xs">
86
+ Duration (seconds, 0 = stream until stopped)
87
+ <NumericInput
88
+ slot="input"
89
+ value={captureDuration}
90
+ on:change={(e) => {
91
+ captureDuration = numberValueFromEvent(e) ?? 3
92
+ }}
93
+ />
94
+ </Label>
95
+ </div>
95
96
 
96
- <div class="mt-auto flex flex-col items-start gap-2">
97
- {#if capture.status === 'recording'}
98
- <p class="font-roboto-mono text-subtle-1 text-xs">
99
- {(capture.totalBytes / 1024).toFixed(1)} kB captured
100
- </p>
101
- <Button
102
- icon="stop-circle-outline"
103
- onclick={capture.stop}
104
- >
105
- Stop
106
- </Button>
107
- {:else}
108
- {#if capture.downloadUrl}
97
+ <div class="mt-auto flex flex-col items-start gap-2">
98
+ {#if capture.status === 'recording'}
109
99
  <p class="font-roboto-mono text-subtle-1 text-xs">
110
100
  {(capture.totalBytes / 1024).toFixed(1)} kB captured
111
101
  </p>
112
- <a
113
- href={capture.downloadUrl}
114
- download="audio-capture.{selectedCodec}"
115
- rel="external"
102
+ <Button
103
+ icon="stop-circle-outline"
104
+ onclick={capture.stop}
105
+ >
106
+ Stop
107
+ </Button>
108
+ {:else}
109
+ {#if capture.downloadUrl}
110
+ <p class="font-roboto-mono text-subtle-1 text-xs">
111
+ {(capture.totalBytes / 1024).toFixed(1)} kB captured
112
+ </p>
113
+ <a
114
+ href={capture.downloadUrl}
115
+ download="audio-capture.{selectedCodec}"
116
+ rel="external"
117
+ >
118
+ <Button icon="download">Download</Button>
119
+ </a>
120
+ {/if}
121
+ <Button
122
+ icon="play-circle-outline"
123
+ onclick={() => capture.start(selectedCodec, captureDuration)}
124
+ disabled={!client.current}
116
125
  >
117
- <Button icon="download">Download</Button>
118
- </a>
126
+ {capture.status === 'done' ? 'Capture Again' : 'Start Capture'}
127
+ </Button>
119
128
  {/if}
120
- <Button
121
- icon="play-circle-outline"
122
- onclick={() => capture.start(selectedCodec, captureDuration)}
123
- disabled={!client.current}
124
- >
125
- {capture.status === 'done' ? 'Capture Again' : 'Start Capture'}
126
- </Button>
127
- {/if}
128
- </div>
129
- </MutationSection>
130
- </div>
129
+ </div>
130
+ </MutationSection>
131
+ </div>
131
132
 
132
- <div class="ml-auto flex w-full max-w-1/2 flex-col divide-y sm:max-w-1/3">
133
- <ApiSection
134
- title="GetProperties"
135
- api="rdk:component:audio_input"
136
- description="Audio input properties"
137
- class="relative"
138
- >
139
- <Query
140
- query={propertiesQuery}
141
- contentCx="h-6"
133
+ <div class="flex w-full flex-col divide-y @2xl:ml-auto @2xl:max-w-1/2 @4xl:max-w-1/3">
134
+ <ApiSection
135
+ title="GetProperties"
136
+ api="rdk:component:audio_input"
137
+ description="Audio input properties"
138
+ class="relative"
142
139
  >
143
- {#if propertiesQuery.data !== undefined}
144
- <Properties
145
- supportedCodecs={propertiesQuery.data.supportedCodecs}
146
- sampleRateHz={propertiesQuery.data.sampleRateHz}
147
- numChannels={propertiesQuery.data.numChannels}
148
- />
149
- {/if}
150
- </Query>
151
- </ApiSection>
140
+ <Query
141
+ query={propertiesQuery}
142
+ contentCx="h-6"
143
+ >
144
+ {#if propertiesQuery.data !== undefined}
145
+ <Properties
146
+ supportedCodecs={propertiesQuery.data.supportedCodecs}
147
+ sampleRateHz={propertiesQuery.data.sampleRateHz}
148
+ numChannels={propertiesQuery.data.numChannels}
149
+ />
150
+ {/if}
151
+ </Query>
152
+ </ApiSection>
153
+ </div>
152
154
  </div>
153
155
  </div>
154
156
  {/snippet}
@@ -103,99 +103,101 @@
103
103
  />
104
104
  </div>
105
105
 
106
- <div class="flex flex-row divide-x">
107
- <div class="flex w-full flex-col divide-y">
108
- <MutationSection
109
- title="Play"
110
- api="rdk:component:audio_output"
111
- description="Send audio data to the device"
112
- lastError={playContext.error}
113
- >
114
- <div class="flex flex-col gap-2">
115
- <Label cx="gap-1 text-xs">
116
- Audio file
117
- <input
118
- slot="input"
119
- type="file"
120
- accept={availableCodecs.map((c) => `.${c}`).join(',')}
121
- class="text-xs"
122
- onchange={handleFileChange}
123
- />
124
- </Label>
125
- {#if fileInputError}
126
- <p class="text-xs text-red-500">{fileInputError}</p>
127
- {/if}
128
- {#if selectedFile}
129
- <p class="font-roboto-mono text-subtle-1 text-xs">{selectedFile.name}</p>
130
- {/if}
131
- <Label cx="gap-1 text-xs">
132
- Codec
133
- <Select
134
- slot="input"
135
- value={selectedCodec}
136
- on:change={(e) => {
137
- playCodec = (e.target as HTMLSelectElement).value
138
- }}
106
+ <div class="@container">
107
+ <div class="flex flex-col divide-y @2xl:flex-row @2xl:divide-x @2xl:divide-y-0">
108
+ <div class="flex w-full flex-col divide-y">
109
+ <MutationSection
110
+ title="Play"
111
+ api="rdk:component:audio_output"
112
+ description="Send audio data to the device"
113
+ lastError={playContext.error}
114
+ >
115
+ <div class="flex flex-col gap-2">
116
+ <Label cx="gap-1 text-xs">
117
+ Audio file
118
+ <input
119
+ slot="input"
120
+ type="file"
121
+ accept={availableCodecs.map((c) => `.${c}`).join(',')}
122
+ class="text-xs"
123
+ onchange={handleFileChange}
124
+ />
125
+ </Label>
126
+ {#if fileInputError}
127
+ <p class="text-xs text-red-500">{fileInputError}</p>
128
+ {/if}
129
+ {#if selectedFile}
130
+ <p class="font-roboto-mono text-subtle-1 text-xs">{selectedFile.name}</p>
131
+ {/if}
132
+ <Label cx="gap-1 text-xs">
133
+ Codec
134
+ <Select
135
+ slot="input"
136
+ value={selectedCodec}
137
+ on:change={(e) => {
138
+ playCodec = (e.target as HTMLSelectElement).value
139
+ }}
140
+ >
141
+ {#each availableCodecs as codec (codec)}
142
+ <option value={codec}>{codec}</option>
143
+ {/each}
144
+ </Select>
145
+ </Label>
146
+ <Label cx="gap-1 text-xs">
147
+ Sample rate (Hz)
148
+ <NumericInput
149
+ slot="input"
150
+ value={selectedSampleRateHz}
151
+ on:change={(e) => {
152
+ playSampleRateHz = numberValueFromEvent(e) ?? null
153
+ }}
154
+ />
155
+ </Label>
156
+ <Label cx="gap-1 text-xs">
157
+ Channels
158
+ <NumericInput
159
+ slot="input"
160
+ value={selectedNumChannels}
161
+ on:change={(e) => {
162
+ playNumChannels = numberValueFromEvent(e) ?? null
163
+ }}
164
+ />
165
+ </Label>
166
+ </div>
167
+
168
+ <div class="mt-auto">
169
+ <Button
170
+ icon="play-circle-outline"
171
+ onclick={play}
172
+ disabled={!client.current || playContext.status === 'playing'}
139
173
  >
140
- {#each availableCodecs as codec (codec)}
141
- <option value={codec}>{codec}</option>
142
- {/each}
143
- </Select>
144
- </Label>
145
- <Label cx="gap-1 text-xs">
146
- Sample rate (Hz)
147
- <NumericInput
148
- slot="input"
149
- value={selectedSampleRateHz}
150
- on:change={(e) => {
151
- playSampleRateHz = numberValueFromEvent(e) ?? null
152
- }}
153
- />
154
- </Label>
155
- <Label cx="gap-1 text-xs">
156
- Channels
157
- <NumericInput
158
- slot="input"
159
- value={selectedNumChannels}
160
- on:change={(e) => {
161
- playNumChannels = numberValueFromEvent(e) ?? null
162
- }}
163
- />
164
- </Label>
165
- </div>
166
-
167
- <div class="mt-auto">
168
- <Button
169
- icon="play-circle-outline"
170
- onclick={play}
171
- disabled={!client.current || playContext.status === 'playing'}
172
- >
173
- {playButtonLabel}
174
- </Button>
175
- </div>
176
- </MutationSection>
177
- </div>
178
-
179
- <div class="ml-auto flex w-full max-w-1/2 flex-col divide-y sm:max-w-1/3">
180
- <ApiSection
181
- title="GetProperties"
182
- api="rdk:component:audio_output"
183
- description="Audio output properties"
184
- class="relative"
185
- >
186
- <Query
187
- query={propertiesQuery}
188
- contentCx="h-6"
174
+ {playButtonLabel}
175
+ </Button>
176
+ </div>
177
+ </MutationSection>
178
+ </div>
179
+
180
+ <div class="flex w-full flex-col divide-y @2xl:ml-auto @2xl:max-w-1/2 @4xl:max-w-1/3">
181
+ <ApiSection
182
+ title="GetProperties"
183
+ api="rdk:component:audio_output"
184
+ description="Audio output properties"
185
+ class="relative"
189
186
  >
190
- {#if propertiesQuery.data !== undefined}
191
- <Properties
192
- supportedCodecs={propertiesQuery.data.supportedCodecs}
193
- sampleRateHz={propertiesQuery.data.sampleRateHz}
194
- numChannels={propertiesQuery.data.numChannels}
195
- />
196
- {/if}
197
- </Query>
198
- </ApiSection>
187
+ <Query
188
+ query={propertiesQuery}
189
+ contentCx="h-6"
190
+ >
191
+ {#if propertiesQuery.data !== undefined}
192
+ <Properties
193
+ supportedCodecs={propertiesQuery.data.supportedCodecs}
194
+ sampleRateHz={propertiesQuery.data.sampleRateHz}
195
+ numChannels={propertiesQuery.data.numChannels}
196
+ />
197
+ {/if}
198
+ </Query>
199
+ </ApiSection>
200
+ </div>
199
201
  </div>
200
202
  </div>
201
203
  {/snippet}
@@ -31,7 +31,7 @@
31
31
  }
32
32
  </script>
33
33
 
34
- <div class="flex flex-col gap-4">
34
+ <div class="flex shrink-0 flex-col gap-4">
35
35
  <Label>
36
36
  Pin
37
37
 
@@ -33,7 +33,7 @@
33
33
  </script>
34
34
 
35
35
  <div class="flex w-full flex-col gap-6">
36
- <div class="flex w-full gap-16">
36
+ <div class="flex w-full flex-wrap gap-x-16 gap-y-6">
37
37
  <PinSelector
38
38
  value={pinSelection}
39
39
  onChange={handleChangedPinSelection}
@@ -181,7 +181,7 @@
181
181
  </Label>
182
182
  </div>
183
183
  {#if isPlaying}
184
- <div class="flex gap-4 p-4 pb-3">
184
+ <div class="flex flex-wrap gap-4 p-4 pb-3">
185
185
  <RefetchController
186
186
  {refetchInterval}
187
187
  allowLive
@@ -216,8 +216,8 @@
216
216
  {/if}
217
217
  </div>
218
218
  {/if}
219
- <div class="flex h-full w-full gap-4 p-4">
220
- <div class="grow">
219
+ <div class="flex h-full w-full flex-wrap gap-4 p-4">
220
+ <div class="min-w-0 grow basis-80">
221
221
  {#if isPlaying}
222
222
  {#if displayAs360}
223
223
  {#if isLive}
@@ -242,6 +242,7 @@
242
242
  {partID}
243
243
  {resourceName}
244
244
  showResolutionOptions
245
+ videoClass="h-auto max-w-full"
245
246
  {isLive}
246
247
  data={imageQuery.data}
247
248
  error={imageQuery.error}
@@ -252,7 +253,7 @@
252
253
  />
253
254
  {/if}
254
255
  {:else}
255
- <div class="bg-medium flex h-64 w-80 items-center justify-center">
256
+ <div class="bg-medium flex h-64 w-full max-w-80 items-center justify-center">
256
257
  <Button
257
258
  icon="play-circle-outline"
258
259
  variant="dark"
@@ -266,7 +267,7 @@
266
267
  {/if}
267
268
  </div>
268
269
  {#if isPlaying}
269
- <div class="flex flex-col items-start gap-2">
270
+ <div class="flex shrink-0 flex-col items-start gap-2">
270
271
  <ExportScreenshot
271
272
  name={client.current?.name ?? ''}
272
273
  sourceName={selectedSource}
@@ -339,7 +339,7 @@
339
339
  {#if lastError}
340
340
  <ContentRect
341
341
  {contentRect}
342
- cx="bg-medium/50 absolute flex h-64 w-80 items-center justify-center"
342
+ cx="bg-medium/50 absolute flex h-64 w-80 max-w-full items-center justify-center"
343
343
  >
344
344
  <ErrorDisplay
345
345
  class="pb-4"
@@ -349,7 +349,7 @@
349
349
  {:else if isLive ? isStreamLoading : isLoading}
350
350
  <ContentRect
351
351
  {contentRect}
352
- cx="absolute h-64 w-80"
352
+ cx="absolute h-64 w-80 max-w-full"
353
353
  >
354
354
  <Progress />
355
355
  </ContentRect>
@@ -66,44 +66,46 @@
66
66
  <div slot="description">Copy JSON</div>
67
67
  </Tooltip>
68
68
  </div>
69
- <dl class="font-roboto-mono flex flex-col gap-y-4">
70
- {#each sortedData as value (value)}
71
- {@const valueString = JSON.stringify(value, null, 2)}
72
- {@const preview = previews[value.name]}
73
- <div class="flex flex-col gap-y-2">
74
- <div class="flex flex-row">
75
- <dt class="text-default flex w-50 min-w-50 flex-col gap-2 pr-2 font-medium">
76
- <span class="truncate">{value.name}</span>
77
- {#if onAddComponent}
69
+ <div class="@container">
70
+ <dl class="font-roboto-mono flex flex-col gap-y-4">
71
+ {#each sortedData as value (value)}
72
+ {@const valueString = JSON.stringify(value, null, 2)}
73
+ {@const preview = previews[value.name]}
74
+ <div class="flex flex-col gap-y-2">
75
+ <div class="flex flex-col gap-y-2 @lg:flex-row @lg:gap-y-0">
76
+ <dt class="text-default flex flex-col gap-2 pr-2 font-medium @lg:w-50 @lg:min-w-50">
77
+ <span class="truncate">{value.name}</span>
78
+ {#if onAddComponent}
79
+ <Button
80
+ class="w-fit"
81
+ icon="plus"
82
+ onclick={() => onAddComponent(parseComponentConfig(value))}
83
+ >
84
+ Add component
85
+ </Button>
86
+ {/if}
78
87
  <Button
79
88
  class="w-fit"
80
- icon="plus"
81
- onclick={() => onAddComponent(parseComponentConfig(value))}
89
+ icon="content-copy"
90
+ onclick={async () => copyAttributes(value)}
82
91
  >
83
- Add component
92
+ Copy attributes
84
93
  </Button>
85
- {/if}
86
- <Button
87
- class="w-fit"
88
- icon="content-copy"
89
- onclick={async () => copyAttributes(value)}
90
- >
91
- Copy attributes
92
- </Button>
93
- </dt>
94
- <dd class="text-subtle-1 min-w-50">
95
- <div class="border-light bg-extralight relative min-w-50 border px-3 py-2">
96
- <pre class="whitespace-pre-wrap"><code>{valueString}</code></pre>
94
+ </dt>
95
+ <dd class="text-subtle-1 min-w-0 grow">
96
+ <div class="border-light bg-extralight relative border px-3 py-2">
97
+ <pre class="wrap-break-word whitespace-pre-wrap"><code>{valueString}</code></pre>
98
+ </div>
99
+ </dd>
100
+ </div>
101
+ {#if preview}
102
+ <div class="flex w-full @lg:pl-50">
103
+ {@render componentPreview?.(preview)}
97
104
  </div>
98
- </dd>
105
+ {/if}
99
106
  </div>
100
- {#if preview}
101
- <div class="flex w-full pl-50">
102
- {@render componentPreview?.(preview)}
103
- </div>
104
- {/if}
105
- </div>
106
- {/each}
107
- </dl>
107
+ {/each}
108
+ </dl>
109
+ </div>
108
110
  </div>
109
111
  {/if}
@@ -79,51 +79,54 @@
79
79
  {@render header({ input: displayInput, setInput })}
80
80
  </div>
81
81
  {/if}
82
- <div class="flex flex-row items-center justify-between">
83
- <div class="flex w-[45%] flex-col gap-2 border-r py-2">
84
- <span class="text-gray-9 px-4 text-sm font-medium">Input</span>
85
- <CodeEditor
86
- label="input"
87
- language="json"
88
- value={displayInput}
89
- onChange={(nextInput: string) => {
90
- updateInput(nextInput)
91
- }}
92
- class="h-56 overflow-y-auto"
93
- errorMessageID={lastErr ? uid : undefined}
94
- />
95
- </div>
82
+ <div class="@container">
83
+ <div class="flex flex-col @2xl:flex-row">
84
+ <div class="flex min-w-0 flex-col gap-2 py-2 @2xl:flex-1">
85
+ <span class="text-gray-9 px-4 text-sm font-medium">Input</span>
86
+ <CodeEditor
87
+ label="input"
88
+ language="json"
89
+ value={displayInput}
90
+ onChange={(nextInput: string) => {
91
+ updateInput(nextInput)
92
+ }}
93
+ class="h-40 overflow-y-auto @2xl:h-56"
94
+ errorMessageID={lastErr ? uid : undefined}
95
+ />
96
+ </div>
97
+
98
+ <div
99
+ class="flex shrink-0 items-center justify-center border-y px-4 py-2 @2xl:border-x @2xl:border-y-0 @2xl:py-0"
100
+ >
101
+ <Button onclick={execute}>Execute</Button>
102
+ </div>
96
103
 
97
- <Button
98
- class="m-auto"
99
- onclick={execute}>Execute</Button
100
- >
101
-
102
- <div class="flex w-[45%] flex-col gap-2 border-l py-2">
103
- <div class="flex flex-row items-center">
104
- <span class="text-gray-9 px-4 text-sm font-medium">Output</span>
105
- {#if doCommandMutation.isPending}
106
- <Progress
107
- size="medium"
108
- variant="dark"
104
+ <div class="flex min-w-0 flex-col gap-2 py-2 @2xl:flex-1">
105
+ <div class="flex flex-row items-center">
106
+ <span class="text-gray-9 px-4 text-sm font-medium">Output</span>
107
+ {#if doCommandMutation.isPending}
108
+ <Progress
109
+ size="medium"
110
+ variant="dark"
111
+ />
112
+ {/if}
113
+ </div>
114
+ {#if !lastErr}
115
+ <CodeEditor
116
+ label="output"
117
+ language="json"
118
+ value={output}
119
+ readonly
120
+ class="h-40 overflow-y-auto @2xl:h-56"
121
+ />
122
+ {:else}
123
+ <ErrorDisplay
124
+ id={uid}
125
+ class="h-40 px-4 @2xl:h-56"
126
+ lastError={lastErr}
109
127
  />
110
128
  {/if}
111
129
  </div>
112
- {#if !lastErr}
113
- <CodeEditor
114
- label="output"
115
- language="json"
116
- value={output}
117
- readonly
118
- class="h-56 overflow-y-auto"
119
- />
120
- {:else}
121
- <ErrorDisplay
122
- id={uid}
123
- class="h-56 px-4"
124
- lastError={lastErr}
125
- />
126
- {/if}
127
130
  </div>
128
131
  </div>
129
132
  {:else}
@@ -29,51 +29,53 @@
29
29
 
30
30
  <ConnectionStatus {partID}>
31
31
  {#snippet connected()}
32
- <div class="flex flex-row divide-x">
33
- <span class="flex grow flex-row gap-4">
34
- <ApiSection
35
- title="Open"
36
- api="rdk:component:gripper"
37
- class="grow-0 gap-3 pr-0"
38
- >
39
- <Open
32
+ <div class="@container">
33
+ <div class="flex flex-col divide-y @lg:flex-row @lg:divide-x @lg:divide-y-0">
34
+ <span class="flex grow flex-wrap gap-4">
35
+ <ApiSection
36
+ title="Open"
37
+ api="rdk:component:gripper"
38
+ class="grow-0 gap-3 @xs:pr-0"
39
+ >
40
+ <Open
41
+ {partID}
42
+ {resourceName}
43
+ />
44
+ </ApiSection>
45
+ <ApiSection
46
+ title="Grab"
47
+ api="rdk:component:gripper"
48
+ class="grow-0 gap-3 @xs:pl-0"
49
+ >
50
+ <Grab
51
+ {partID}
52
+ {resourceName}
53
+ />
54
+ </ApiSection>
55
+ </span>
56
+ <div class="flex flex-col divide-y">
57
+ <ApiSection
58
+ title="Stop"
59
+ api="rdk:component:gripper"
60
+ >
61
+ <StopButton
62
+ error={stopMutation.error}
63
+ onStop={() => {
64
+ stopMutation.mutate([])
65
+ }}
66
+ />
67
+ </ApiSection>
68
+ <IsMoving
69
+ client={GripperClient}
70
+ api="rdk:component:gripper"
40
71
  {partID}
41
72
  {resourceName}
42
73
  />
43
- </ApiSection>
44
- <ApiSection
45
- title="Grab"
46
- api="rdk:component:gripper"
47
- class="grow-0 gap-3 pl-0"
48
- >
49
- <Grab
74
+ <IsHoldingSomething
50
75
  {partID}
51
76
  {resourceName}
52
77
  />
53
- </ApiSection>
54
- </span>
55
- <div class="flex flex-col divide-y">
56
- <ApiSection
57
- title="Stop"
58
- api="rdk:component:gripper"
59
- >
60
- <StopButton
61
- error={stopMutation.error}
62
- onStop={() => {
63
- stopMutation.mutate([])
64
- }}
65
- />
66
- </ApiSection>
67
- <IsMoving
68
- client={GripperClient}
69
- api="rdk:component:gripper"
70
- {partID}
71
- {resourceName}
72
- />
73
- <IsHoldingSomething
74
- {partID}
75
- {resourceName}
76
- />
78
+ </div>
77
79
  </div>
78
80
  </div>
79
81
  {/snippet}
@@ -40,7 +40,7 @@
40
40
  }
41
41
  </script>
42
42
 
43
- <div class="flex w-1/3 flex-col gap-2">
43
+ <div class="flex flex-col gap-2 @2xl:w-1/3">
44
44
  <Label>
45
45
  Point size
46
46
 
@@ -24,22 +24,24 @@
24
24
  }
25
25
  </script>
26
26
 
27
- <div class="flex h-120 gap-4">
28
- <Inputs
29
- {pointSize}
30
- {up}
31
- {data}
32
- onPointSizeChange={handlePointSizeChange}
33
- onUpChange={handleUpChange}
34
- />
35
-
36
- <div class="relative h-full w-2/3 overflow-hidden border">
37
- <Canvas>
38
- <Scene
39
- {data}
40
- {up}
41
- {pointSize}
42
- />
43
- </Canvas>
27
+ <div class="@container">
28
+ <div class="flex flex-col gap-4 @2xl:h-120 @2xl:flex-row">
29
+ <Inputs
30
+ {pointSize}
31
+ {up}
32
+ {data}
33
+ onPointSizeChange={handlePointSizeChange}
34
+ onUpChange={handleUpChange}
35
+ />
36
+
37
+ <div class="relative h-80 overflow-hidden border @2xl:h-full @2xl:w-2/3">
38
+ <Canvas>
39
+ <Scene
40
+ {data}
41
+ {up}
42
+ {pointSize}
43
+ />
44
+ </Canvas>
45
+ </div>
44
46
  </div>
45
47
  </div>
@@ -48,71 +48,76 @@
48
48
 
49
49
  <ConnectionStatus {partID}>
50
50
  {#snippet connected()}
51
- <div class="flex flex-row divide-x">
52
- <div class="grid grow grid-cols-3 divide-x">
53
- <ApiSection
54
- title="GetPosition"
55
- api="rdk:component:servo"
56
- bottomText="Updates automatically"
57
- >
58
- <Query query={positionQuery}>
59
- {#if positionQuery.data !== undefined}
60
- <!-- span required to get unit closer to position reading -->
61
- <span class="flex flex-row gap-1">
62
- <span class="font-roboto-mono font-normal">{formatNumeric(positionQuery.data)}</span
63
- >
64
- <abbr class="text-subtle-2">º</abbr>
65
- </span>
66
- {/if}
67
- </Query>
68
- </ApiSection>
69
- <ApiSection
70
- title="Move"
71
- api="rdk:component:servo"
72
- >
73
- <Query query={positionQuery}>
74
- {#if positionQuery.data !== undefined}
75
- <Move
76
- currentPosition={positionQuery.data}
77
- {moveTo}
78
- lastError={moveMutation.error}
79
- />
80
- {/if}
81
- </Query>
82
- </ApiSection>
83
- <ApiSection
84
- title="Quick move"
85
- bottomText="Press a button to execute"
86
- >
87
- <Query query={positionQuery}>
88
- {#if positionQuery.data !== undefined}
89
- <QuickMove
90
- currentPosition={positionQuery.data}
91
- moveTo={quickMoveTo}
92
- lastError={quickMoveMutation.error}
93
- />
94
- {/if}
95
- </Query>
96
- </ApiSection>
97
- </div>
98
- <div class="ml-auto flex w-full max-w-40 flex-col divide-y">
99
- <ApiSection
100
- title="Stop"
101
- api="rdk:component:servo"
102
- >
103
- <StopButton
104
- error={stopMutation.error}
105
- onStop={() => {
106
- stopMutation.mutate([])
107
- }}
51
+ <div class="@container">
52
+ <div class="flex flex-col divide-y @4xl:flex-row @4xl:divide-x @4xl:divide-y-0">
53
+ <div class="@container grow">
54
+ <div class="grid grid-cols-1 divide-y @2xl:grid-cols-3 @2xl:divide-x @2xl:divide-y-0">
55
+ <ApiSection
56
+ title="GetPosition"
57
+ api="rdk:component:servo"
58
+ bottomText="Updates automatically"
59
+ >
60
+ <Query query={positionQuery}>
61
+ {#if positionQuery.data !== undefined}
62
+ <!-- span required to get unit closer to position reading -->
63
+ <span class="flex flex-row gap-1">
64
+ <span class="font-roboto-mono font-normal"
65
+ >{formatNumeric(positionQuery.data)}</span
66
+ >
67
+ <abbr class="text-subtle-2">º</abbr>
68
+ </span>
69
+ {/if}
70
+ </Query>
71
+ </ApiSection>
72
+ <ApiSection
73
+ title="Move"
74
+ api="rdk:component:servo"
75
+ >
76
+ <Query query={positionQuery}>
77
+ {#if positionQuery.data !== undefined}
78
+ <Move
79
+ currentPosition={positionQuery.data}
80
+ {moveTo}
81
+ lastError={moveMutation.error}
82
+ />
83
+ {/if}
84
+ </Query>
85
+ </ApiSection>
86
+ <ApiSection
87
+ title="Quick move"
88
+ bottomText="Press a button to execute"
89
+ >
90
+ <Query query={positionQuery}>
91
+ {#if positionQuery.data !== undefined}
92
+ <QuickMove
93
+ currentPosition={positionQuery.data}
94
+ moveTo={quickMoveTo}
95
+ lastError={quickMoveMutation.error}
96
+ />
97
+ {/if}
98
+ </Query>
99
+ </ApiSection>
100
+ </div>
101
+ </div>
102
+ <div class="flex flex-col divide-y @4xl:ml-auto @4xl:w-full @4xl:max-w-40">
103
+ <ApiSection
104
+ title="Stop"
105
+ api="rdk:component:servo"
106
+ >
107
+ <StopButton
108
+ error={stopMutation.error}
109
+ onStop={() => {
110
+ stopMutation.mutate([])
111
+ }}
112
+ />
113
+ </ApiSection>
114
+ <IsMoving
115
+ client={ServoClient}
116
+ api="rdk:component:servo"
117
+ {partID}
118
+ {resourceName}
108
119
  />
109
- </ApiSection>
110
- <IsMoving
111
- client={ServoClient}
112
- api="rdk:component:servo"
113
- {partID}
114
- {resourceName}
115
- />
120
+ </div>
116
121
  </div>
117
122
  </div>
118
123
  {/snippet}
@@ -70,6 +70,9 @@
70
70
 
71
71
  let container = $state<HTMLElement>()
72
72
  let size = $state<Size>()
73
+ let isStacked = $state(false)
74
+
75
+ const STACK_WIDTH_PX = 672
73
76
 
74
77
  useResizeObserver(
75
78
  () => container,
@@ -80,6 +83,7 @@
80
83
 
81
84
  const setSize = () => {
82
85
  if (container) {
86
+ isStacked = container.clientWidth < STACK_WIDTH_PX
83
87
  size = getImageSize(img, container)
84
88
  }
85
89
  }
@@ -101,7 +105,10 @@
101
105
  </script>
102
106
 
103
107
  <div
104
- class="flex h-full min-h-0 w-full min-w-0 gap-2 py-2 pl-2"
108
+ class={[
109
+ 'flex min-h-0 w-full min-w-0 gap-2 py-2 pl-2',
110
+ isStacked ? 'flex-col' : 'h-full flex-row',
111
+ ]}
105
112
  bind:this={container}
106
113
  >
107
114
  {#if !size}
@@ -129,8 +136,8 @@
129
136
  </Canvas>
130
137
  </div>
131
138
  <div
132
- class="flex grow flex-col pr-2"
133
- style:height={`${size.height.toString()}px`}
139
+ class={['flex grow flex-col pr-2', isStacked && 'min-h-40']}
140
+ style:height={isStacked ? undefined : `${size.height.toString()}px`}
134
141
  >
135
142
  <Legend
136
143
  classifications={data?.classifications}
@@ -61,7 +61,7 @@
61
61
  </script>
62
62
 
63
63
  <div class="relative h-[600px] max-h-[80vh] bg-white">
64
- <div class="relative z-10 h-full overflow-y-scroll p-4">
64
+ <div class="relative z-10 h-full overflow-x-auto overflow-y-scroll p-4">
65
65
  <div class="grid grid-cols-[repeat(auto-fit,320px)] justify-center gap-4">
66
66
  {#each items as item (item.id)}
67
67
  <div class="w-[320px] shadow-sm">
@@ -122,8 +122,8 @@
122
122
  <ConnectionStatus {partID}>
123
123
  {#snippet connected()}
124
124
  <div class="flex flex-col gap-2 px-4 pt-4 pb-3">
125
- <div class="flex flex-row gap-4">
126
- <div class="w-50">
125
+ <div class="flex flex-wrap gap-4">
126
+ <div class="w-50 max-w-full">
127
127
  <Label position="top">
128
128
  Camera
129
129
  <Select
@@ -160,7 +160,7 @@
160
160
 
161
161
  <h6 class="text-subtle-1 font-semibold">Detections/Classifications</h6>
162
162
 
163
- <div class="flex gap-4">
163
+ <div class="flex flex-wrap gap-4">
164
164
  <RefetchController
165
165
  allowLive
166
166
  {refetchInterval}
@@ -218,8 +218,8 @@
218
218
  </Queries>
219
219
 
220
220
  <h6 class="text-subtle-1 mt-4 font-semibold">Object point clouds</h6>
221
- <div class="flex items-center gap-4">
222
- <div class="w-50">
221
+ <div class="flex flex-wrap items-center gap-4">
222
+ <div class="w-50 max-w-full">
223
223
  <Label position="left">
224
224
  Show object point clouds
225
225
  <Switch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/test-widgets",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "wireit": {