@opengeni/react 3.8.0-canary.0 → 4.0.1-canary.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.
- package/README.md +11 -24
- package/dist/artifacts.js +7 -7
- package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
- package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
- package/dist/chunk-5M6VTFJ5.js +171 -0
- package/dist/chunk-5M6VTFJ5.js.map +1 -0
- package/dist/{chunk-IOLVN74D.js → chunk-6J56T3MG.js} +15 -1
- package/dist/chunk-6J56T3MG.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-3HIF5IFC.js → chunk-HO6X4FHG.js} +2597 -1030
- package/dist/chunk-HO6X4FHG.js.map +1 -0
- package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
- package/dist/chunk-HUMIRRYI.js.map +1 -0
- package/dist/{chunk-ZFOFY5ST.js → chunk-NLTS7JHI.js} +16 -31
- package/dist/chunk-NLTS7JHI.js.map +1 -0
- package/dist/chunk-NOSYBS2D.js +208 -0
- package/dist/chunk-NOSYBS2D.js.map +1 -0
- package/dist/chunk-YBM6CQRU.js +292 -0
- package/dist/chunk-YBM6CQRU.js.map +1 -0
- package/dist/{chunk-BQGIXRKV.js → chunk-Z7WBDB4E.js} +416 -290
- package/dist/chunk-Z7WBDB4E.js.map +1 -0
- package/dist/chunk-ZYRGEKWS.js +216 -0
- package/dist/chunk-ZYRGEKWS.js.map +1 -0
- package/dist/components/composer.d.ts +4 -2
- package/dist/components/human-input-form.d.ts +3 -1
- package/dist/components/human-input-surface.d.ts +3 -2
- package/dist/components/message-timeline.d.ts +9 -2
- package/dist/components/session-conversation.d.ts +2 -0
- package/dist/components/timeline-annotation-chrome.d.ts +25 -0
- package/dist/components/timeline-annotation-layout.d.ts +42 -0
- package/dist/components/timeline-annotation-markers.d.ts +8 -0
- package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
- package/dist/components/timeline-annotation-shared.d.ts +31 -0
- package/dist/components/timeline-annotations-dialog.d.ts +1 -1
- package/dist/components/timeline-annotations.d.ts +8 -2
- package/dist/composer.js +3 -4
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-codex-accounts.d.ts +11 -1
- package/dist/hooks/use-composer.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +50 -37
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/machines.js +1 -1
- package/dist/older-history.d.ts +3 -2
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
- package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
- package/dist/{queue-surface-implementation-XL2A6PBM.js → queue-surface-implementation-LK3S4BIW.js} +5 -2
- package/dist/{queue-surface-implementation-XL2A6PBM.js.map → queue-surface-implementation-LK3S4BIW.js.map} +1 -1
- package/dist/realtime.js +3 -3
- package/dist/session-ui.js +11 -11
- package/dist/session.js +2 -2
- package/dist/timeline/types.d.ts +3 -0
- package/dist/timeline-annotation-markers-DRSTLEM6.js +165 -0
- package/dist/timeline-annotation-markers-DRSTLEM6.js.map +1 -0
- package/dist/timeline-annotation-selection-IHLUGVND.js +236 -0
- package/dist/timeline-annotation-selection-IHLUGVND.js.map +1 -0
- package/dist/timeline-annotations-dialog-6Q53N5FG.js +262 -0
- package/dist/timeline-annotations-dialog-6Q53N5FG.js.map +1 -0
- package/package.json +2 -6
- package/src/components/chat-composer.tsx +5 -2
- package/src/components/composer.tsx +40 -5
- package/src/components/copy-button.tsx +1 -0
- package/src/components/human-input-form.tsx +185 -53
- package/src/components/human-input-surface.tsx +3 -0
- package/src/components/machines-dashboard.tsx +1 -16
- package/src/components/message-timeline.tsx +397 -302
- package/src/components/session-commands-panel.tsx +7 -1
- package/src/components/session-conversation.tsx +4 -1
- package/src/components/timeline-annotation-chrome.tsx +183 -0
- package/src/components/timeline-annotation-layout.ts +268 -0
- package/src/components/timeline-annotation-markers.tsx +185 -0
- package/src/components/timeline-annotation-reveal-context.ts +9 -0
- package/src/components/timeline-annotation-selection.tsx +99 -54
- package/src/components/timeline-annotation-shared.ts +245 -0
- package/src/components/timeline-annotations-dialog.tsx +172 -128
- package/src/components/timeline-annotations.tsx +178 -45
- package/src/hooks/use-codex-accounts.ts +32 -5
- package/src/hooks/use-composer.ts +17 -0
- package/src/hooks/use-session-events.ts +16 -277
- package/src/index.ts +0 -1
- package/src/older-history.ts +22 -4
- package/src/timeline/platform-activity-row.tsx +4 -1
- package/src/timeline/projection.ts +99 -9
- package/src/timeline/types.ts +3 -0
- package/styles/compiled.css +86 -22
- package/dist/chat.d.ts +0 -37
- package/dist/chat.js +0 -315
- package/dist/chat.js.map +0 -1
- package/dist/chunk-3HIF5IFC.js.map +0 -1
- package/dist/chunk-BQGIXRKV.js.map +0 -1
- package/dist/chunk-BXJOQFCD.js.map +0 -1
- package/dist/chunk-F2CFHN3Y.js +0 -1415
- package/dist/chunk-F2CFHN3Y.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-IOLVN74D.js.map +0 -1
- package/dist/chunk-W3OUAH6M.js +0 -170
- package/dist/chunk-W3OUAH6M.js.map +0 -1
- package/dist/chunk-ZFOFY5ST.js.map +0 -1
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
- package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
- package/src/chat.tsx +0 -393
- /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
- /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
HumanInputAnswer,
|
|
3
3
|
HumanInputQuestion,
|
|
4
|
+
SkillRecord,
|
|
4
5
|
SessionHumanInputRequest,
|
|
5
6
|
SubmitHumanInputResponseRequest,
|
|
6
7
|
} from "@opengeni/sdk";
|
|
@@ -64,6 +65,10 @@ export const defaultHumanInputFormMessages: HumanInputFormMessages = {
|
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
export type HumanInputFormProps = {
|
|
68
|
+
/** Scoped immutable revision reader. Saving a Skill fails closed without its full preview. */
|
|
69
|
+
loadSkillReview?:
|
|
70
|
+
| ((reference: NonNullable<HumanInputQuestion["skillReview"]>) => Promise<SkillRecord>)
|
|
71
|
+
| undefined;
|
|
67
72
|
request: Pick<SessionHumanInputRequest, "id" | "questions" | "allowSkip" | "expiresAt">;
|
|
68
73
|
onSubmit: (response: SubmitHumanInputResponseRequest) => void | Promise<void>;
|
|
69
74
|
submitting?: boolean | undefined;
|
|
@@ -98,6 +103,7 @@ export function HumanInputForm(props: HumanInputFormProps) {
|
|
|
98
103
|
function HumanInputRequestForm({
|
|
99
104
|
request,
|
|
100
105
|
onSubmit,
|
|
106
|
+
loadSkillReview,
|
|
101
107
|
submitting = false,
|
|
102
108
|
error,
|
|
103
109
|
title,
|
|
@@ -142,6 +148,116 @@ function HumanInputRequestForm({
|
|
|
142
148
|
const submissionInFlight = useRef(false);
|
|
143
149
|
const submissionGeneration = useRef(0);
|
|
144
150
|
const busy = submitting || submittingInternally;
|
|
151
|
+
const reviewIdentity = JSON.stringify(
|
|
152
|
+
request.questions
|
|
153
|
+
.filter((question) => question.skillReview)
|
|
154
|
+
.map((question) => ({
|
|
155
|
+
id: question.id,
|
|
156
|
+
reference: {
|
|
157
|
+
sourceOperationId: question.skillReview!.sourceOperationId,
|
|
158
|
+
skillId: question.skillReview!.skillId,
|
|
159
|
+
revisionId: question.skillReview!.revisionId,
|
|
160
|
+
expectedRevisionId: question.skillReview!.expectedRevisionId,
|
|
161
|
+
expectedScopeVersion: question.skillReview!.expectedScopeVersion,
|
|
162
|
+
},
|
|
163
|
+
})),
|
|
164
|
+
);
|
|
165
|
+
const [reviewReload, setReviewReload] = useState(0);
|
|
166
|
+
const [reviews, setReviews] = useState<{
|
|
167
|
+
loader: typeof loadSkillReview;
|
|
168
|
+
identity: string;
|
|
169
|
+
records: Record<string, SkillRecord>;
|
|
170
|
+
error: string | null;
|
|
171
|
+
} | null>(null);
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
let current = true;
|
|
174
|
+
const questions = JSON.parse(reviewIdentity) as Array<{
|
|
175
|
+
id: string;
|
|
176
|
+
reference: NonNullable<HumanInputQuestion["skillReview"]>;
|
|
177
|
+
}>;
|
|
178
|
+
if (!questions.length) return;
|
|
179
|
+
setReviews(null);
|
|
180
|
+
void Promise.all(
|
|
181
|
+
questions.map(async (question) => {
|
|
182
|
+
if (!loadSkillReview)
|
|
183
|
+
throw new Error(
|
|
184
|
+
"This client cannot preview Skill files. Open this request in OpenGeni to review it.",
|
|
185
|
+
);
|
|
186
|
+
const reference = question.reference;
|
|
187
|
+
const record = await loadSkillReview(reference);
|
|
188
|
+
if (
|
|
189
|
+
record.id !== reference.skillId ||
|
|
190
|
+
record.revisionId !== reference.revisionId ||
|
|
191
|
+
!record.files.some((file) => file.path === "SKILL.md")
|
|
192
|
+
) {
|
|
193
|
+
throw new Error("The requested Skill revision could not be verified.");
|
|
194
|
+
}
|
|
195
|
+
return [question.id, record] as const;
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
198
|
+
.then((records) => {
|
|
199
|
+
if (current)
|
|
200
|
+
setReviews({
|
|
201
|
+
loader: loadSkillReview,
|
|
202
|
+
identity: reviewIdentity,
|
|
203
|
+
records: Object.fromEntries(records),
|
|
204
|
+
error: null,
|
|
205
|
+
});
|
|
206
|
+
})
|
|
207
|
+
.catch((cause) => {
|
|
208
|
+
if (current)
|
|
209
|
+
setReviews({
|
|
210
|
+
loader: loadSkillReview,
|
|
211
|
+
identity: reviewIdentity,
|
|
212
|
+
records: {},
|
|
213
|
+
error: cause instanceof Error ? cause.message : "Could not load the Skill files.",
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
return () => {
|
|
217
|
+
current = false;
|
|
218
|
+
};
|
|
219
|
+
}, [loadSkillReview, reviewIdentity, reviewReload]);
|
|
220
|
+
const visibleReviews =
|
|
221
|
+
reviews?.loader === loadSkillReview && reviews?.identity === reviewIdentity ? reviews : null;
|
|
222
|
+
const preview = (question: HumanInputQuestion) => {
|
|
223
|
+
if (!question.skillReview) return null;
|
|
224
|
+
const record = visibleReviews?.records[question.id];
|
|
225
|
+
return (
|
|
226
|
+
<div className="mb-3 min-w-0 space-y-2" data-skill-review="">
|
|
227
|
+
{record ? (
|
|
228
|
+
<>
|
|
229
|
+
<p className="text-og-sm">
|
|
230
|
+
{record.title ?? "Skill"} · {record.scope} · {record.files.length} files
|
|
231
|
+
</p>
|
|
232
|
+
<p className="text-og-xs text-og-fg-muted">
|
|
233
|
+
Saving activates these exact files. No additional review is required.
|
|
234
|
+
</p>
|
|
235
|
+
{record.files.map((file) => (
|
|
236
|
+
<details key={file.path} open={file.path === "SKILL.md"}>
|
|
237
|
+
<summary className="cursor-pointer break-all text-og-sm">{file.path}</summary>
|
|
238
|
+
<pre className="max-h-64 overflow-auto whitespace-pre-wrap break-words rounded-og-md bg-og-surface-1 p-2 text-og-xs">
|
|
239
|
+
{file.content}
|
|
240
|
+
</pre>
|
|
241
|
+
</details>
|
|
242
|
+
))}
|
|
243
|
+
</>
|
|
244
|
+
) : visibleReviews?.error ? (
|
|
245
|
+
<>
|
|
246
|
+
<p role="alert" className="text-og-sm">
|
|
247
|
+
{visibleReviews.error}
|
|
248
|
+
</p>
|
|
249
|
+
<button type="button" onClick={() => setReviewReload((value) => value + 1)}>
|
|
250
|
+
Retry preview
|
|
251
|
+
</button>
|
|
252
|
+
</>
|
|
253
|
+
) : (
|
|
254
|
+
<p role="status" className="text-og-sm">
|
|
255
|
+
Loading the exact Skill files…
|
|
256
|
+
</p>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
145
261
|
|
|
146
262
|
useEffect(() => {
|
|
147
263
|
if (collapsed) {
|
|
@@ -232,6 +348,18 @@ function HumanInputRequestForm({
|
|
|
232
348
|
}
|
|
233
349
|
return;
|
|
234
350
|
}
|
|
351
|
+
const unreviewedSave = request.questions.find(
|
|
352
|
+
(question) =>
|
|
353
|
+
question.skillReview &&
|
|
354
|
+
!visibleReviews?.records[question.id] &&
|
|
355
|
+
result.answers.some(
|
|
356
|
+
(answer) => answer.questionId === question.id && answer.values.includes("save"),
|
|
357
|
+
),
|
|
358
|
+
);
|
|
359
|
+
if (unreviewedSave) {
|
|
360
|
+
setValidationErrors({ [unreviewedSave.id]: "Load the exact Skill files before saving." });
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
235
363
|
await submitResponse({ outcome: "answered", answers: result.answers });
|
|
236
364
|
};
|
|
237
365
|
|
|
@@ -365,6 +493,7 @@ function HumanInputRequestForm({
|
|
|
365
493
|
// Title already carries the question; only render the control + help extras.
|
|
366
494
|
return (
|
|
367
495
|
<div key={question.id} data-human-input-question={question.id}>
|
|
496
|
+
{preview(question)}
|
|
368
497
|
<QuestionControls
|
|
369
498
|
question={question}
|
|
370
499
|
questionNumber={null}
|
|
@@ -389,6 +518,7 @@ function HumanInputRequestForm({
|
|
|
389
518
|
data-human-input-question={question.id}
|
|
390
519
|
className="flex flex-col gap-1.5"
|
|
391
520
|
>
|
|
521
|
+
{preview(question)}
|
|
392
522
|
<QuestionControls
|
|
393
523
|
question={question}
|
|
394
524
|
questionNumber={index + 1}
|
|
@@ -643,62 +773,64 @@ function QuestionControls({
|
|
|
643
773
|
</label>
|
|
644
774
|
);
|
|
645
775
|
})}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
id={otherChoiceId}
|
|
656
|
-
type={question.kind === "single_select" ? "radio" : "checkbox"}
|
|
657
|
-
name={question.kind === "single_select" ? fieldId : undefined}
|
|
658
|
-
aria-labelledby={otherLabelId}
|
|
659
|
-
checked={draft.otherSelected}
|
|
660
|
-
autoFocus={autoFocus && firstOption && question.options.length === 0}
|
|
661
|
-
onChange={(event) =>
|
|
662
|
-
onUpdate((current) => ({
|
|
663
|
-
...current,
|
|
664
|
-
otherSelected: event.target.checked,
|
|
665
|
-
...(question.kind === "single_select" && event.target.checked
|
|
666
|
-
? { values: [] }
|
|
667
|
-
: {}),
|
|
668
|
-
}))
|
|
669
|
-
}
|
|
670
|
-
className="mt-2 accent-og-accent"
|
|
671
|
-
/>
|
|
672
|
-
<span className="min-w-0 flex-1">
|
|
673
|
-
<label
|
|
674
|
-
id={otherLabelId}
|
|
675
|
-
htmlFor={otherChoiceId}
|
|
676
|
-
className="block text-og-sm font-medium"
|
|
677
|
-
>
|
|
678
|
-
{messages.other}
|
|
679
|
-
</label>
|
|
680
|
-
<label htmlFor={otherTextId} className="sr-only">
|
|
681
|
-
{messages.other} answer for {visibleLabel}
|
|
682
|
-
</label>
|
|
776
|
+
{!question.skillReview ? (
|
|
777
|
+
<div
|
|
778
|
+
className={cn(
|
|
779
|
+
"flex items-start gap-2.5 rounded-og-md px-2.5 py-2 transition-colors",
|
|
780
|
+
draft.otherSelected
|
|
781
|
+
? "bg-og-status-waiting/12 text-og-fg"
|
|
782
|
+
: "text-og-fg hover:bg-og-surface-1/80",
|
|
783
|
+
)}
|
|
784
|
+
>
|
|
683
785
|
<input
|
|
684
|
-
id={
|
|
685
|
-
type="
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
onChange={(event) => {
|
|
692
|
-
const other = event.target.value;
|
|
786
|
+
id={otherChoiceId}
|
|
787
|
+
type={question.kind === "single_select" ? "radio" : "checkbox"}
|
|
788
|
+
name={question.kind === "single_select" ? fieldId : undefined}
|
|
789
|
+
aria-labelledby={otherLabelId}
|
|
790
|
+
checked={draft.otherSelected}
|
|
791
|
+
autoFocus={autoFocus && firstOption && question.options.length === 0}
|
|
792
|
+
onChange={(event) =>
|
|
693
793
|
onUpdate((current) => ({
|
|
694
|
-
...
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
794
|
+
...current,
|
|
795
|
+
otherSelected: event.target.checked,
|
|
796
|
+
...(question.kind === "single_select" && event.target.checked
|
|
797
|
+
? { values: [] }
|
|
798
|
+
: {}),
|
|
799
|
+
}))
|
|
800
|
+
}
|
|
801
|
+
className="mt-2 accent-og-accent"
|
|
699
802
|
/>
|
|
700
|
-
|
|
701
|
-
|
|
803
|
+
<span className="min-w-0 flex-1">
|
|
804
|
+
<label
|
|
805
|
+
id={otherLabelId}
|
|
806
|
+
htmlFor={otherChoiceId}
|
|
807
|
+
className="block text-og-sm font-medium"
|
|
808
|
+
>
|
|
809
|
+
{messages.other}
|
|
810
|
+
</label>
|
|
811
|
+
<label htmlFor={otherTextId} className="sr-only">
|
|
812
|
+
{messages.other} answer for {visibleLabel}
|
|
813
|
+
</label>
|
|
814
|
+
<input
|
|
815
|
+
id={otherTextId}
|
|
816
|
+
type="text"
|
|
817
|
+
value={draft.other}
|
|
818
|
+
disabled={busy}
|
|
819
|
+
placeholder="Type a value…"
|
|
820
|
+
onClick={() => onUpdate(selectOtherDraft)}
|
|
821
|
+
onFocus={() => onUpdate(selectOtherDraft)}
|
|
822
|
+
onChange={(event) => {
|
|
823
|
+
const other = event.target.value;
|
|
824
|
+
onUpdate((current) => ({
|
|
825
|
+
...selectOtherDraft(current),
|
|
826
|
+
other,
|
|
827
|
+
}));
|
|
828
|
+
}}
|
|
829
|
+
className="mt-1.5 w-full rounded-og-sm border border-og-border bg-og-surface-1 px-2 py-1.5 text-og-sm text-og-fg outline-hidden focus:border-og-accent disabled:opacity-50"
|
|
830
|
+
/>
|
|
831
|
+
</span>
|
|
832
|
+
</div>
|
|
833
|
+
) : null}
|
|
702
834
|
</div>
|
|
703
835
|
)}
|
|
704
836
|
{error ? (
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "./human-input-form";
|
|
10
10
|
|
|
11
11
|
export type HumanInputSurfaceProps = {
|
|
12
|
+
loadSkillReview?: HumanInputFormProps["loadSkillReview"];
|
|
12
13
|
requests: SessionHumanInputRequest[];
|
|
13
14
|
onSubmit: (requestId: string, response: SubmitHumanInputResponseRequest) => void | Promise<void>;
|
|
14
15
|
respondingRequestId?: string | null | undefined;
|
|
@@ -28,6 +29,7 @@ export type HumanInputSurfaceProps = {
|
|
|
28
29
|
export function HumanInputSurface({
|
|
29
30
|
requests,
|
|
30
31
|
onSubmit,
|
|
32
|
+
loadSkillReview,
|
|
31
33
|
respondingRequestId = null,
|
|
32
34
|
error,
|
|
33
35
|
messages,
|
|
@@ -68,6 +70,7 @@ export function HumanInputSurface({
|
|
|
68
70
|
|
|
69
71
|
const formProps: HumanInputFormProps = {
|
|
70
72
|
request: active,
|
|
73
|
+
loadSkillReview,
|
|
71
74
|
submitting: respondingRequestId !== null,
|
|
72
75
|
error: error ?? null,
|
|
73
76
|
progressLabel,
|
|
@@ -62,13 +62,9 @@ function EmptyState({ onEnroll }: { onEnroll?: (() => void) | undefined }) {
|
|
|
62
62
|
function Header({
|
|
63
63
|
count,
|
|
64
64
|
onEnroll,
|
|
65
|
-
onRefresh,
|
|
66
|
-
loading,
|
|
67
65
|
}: {
|
|
68
66
|
count: number;
|
|
69
67
|
onEnroll?: (() => void) | undefined;
|
|
70
|
-
onRefresh?: (() => void) | undefined;
|
|
71
|
-
loading?: boolean | undefined;
|
|
72
68
|
}): ReactNode {
|
|
73
69
|
return (
|
|
74
70
|
<div className="flex items-center justify-between gap-3">
|
|
@@ -77,17 +73,6 @@ function Header({
|
|
|
77
73
|
<span className="font-og-mono text-og-xs text-og-fg-subtle">{count}</span>
|
|
78
74
|
</div>
|
|
79
75
|
<div className="flex items-center gap-1.5">
|
|
80
|
-
{onRefresh ? (
|
|
81
|
-
<button
|
|
82
|
-
type="button"
|
|
83
|
-
data-refresh
|
|
84
|
-
onClick={onRefresh}
|
|
85
|
-
title="Refresh"
|
|
86
|
-
className="rounded-og-sm p-1.5 text-og-fg-subtle transition-colors hover:bg-og-surface-2 hover:text-og-fg"
|
|
87
|
-
>
|
|
88
|
-
<RefreshCwIcon className={cn("size-3.5", loading && "animate-og-spin")} aria-hidden />
|
|
89
|
-
</button>
|
|
90
|
-
) : null}
|
|
91
76
|
{onEnroll ? (
|
|
92
77
|
<button
|
|
93
78
|
type="button"
|
|
@@ -131,7 +116,7 @@ export function MachinesDashboard({
|
|
|
131
116
|
|
|
132
117
|
return (
|
|
133
118
|
<section data-machines-dashboard className={cn("og-root flex flex-col gap-3", className)}>
|
|
134
|
-
<Header count={machines.length} onEnroll={onEnroll}
|
|
119
|
+
<Header count={machines.length} onEnroll={onEnroll} />
|
|
135
120
|
|
|
136
121
|
{error ? (
|
|
137
122
|
<div
|