@xemahq/ui-kernel 0.9.1 → 0.9.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/README.md +2 -2
- package/dist/lib/biome-host/biome-builders.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-builders.js +3 -1
- package/dist/lib/biome-host/biome-builders.js.map +1 -1
- package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-registry.js.map +1 -1
- package/dist/lib/biome-host/biome-scope-hooks.d.ts +11 -0
- package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-scope-hooks.js +25 -0
- package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -0
- package/dist/lib/biome-host/biome-scope.d.ts +1 -10
- package/dist/lib/biome-host/biome-scope.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scope.js +1 -24
- package/dist/lib/biome-host/biome-scope.js.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.d.ts +1 -1
- package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.js +1 -1
- package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
- package/dist/lib/biome-host/create-biome-orval-config.js +2 -2
- package/dist/lib/biome-host/create-biome-orval-config.js.map +1 -1
- package/dist/lib/biome-host/errors.js.map +1 -1
- package/dist/lib/biome-host/index.d.ts +1 -1
- package/dist/lib/biome-host/index.d.ts.map +1 -1
- package/dist/lib/biome-host/index.js +1 -1
- package/dist/lib/biome-host/index.js.map +1 -1
- package/dist/lib/biome-host/session-profiles.d.ts.map +1 -1
- package/dist/lib/biome-host/session-profiles.js +4 -1
- package/dist/lib/biome-host/session-profiles.js.map +1 -1
- package/dist/lib/capabilities/capability-context.d.ts +9 -0
- package/dist/lib/capabilities/capability-context.d.ts.map +1 -0
- package/dist/lib/capabilities/capability-context.js +13 -0
- package/dist/lib/capabilities/capability-context.js.map +1 -0
- package/dist/lib/capabilities/capability-provider.d.ts +2 -8
- package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
- package/dist/lib/capabilities/capability-provider.js +1 -12
- package/dist/lib/capabilities/capability-provider.js.map +1 -1
- package/dist/lib/capabilities/index.d.ts +1 -0
- package/dist/lib/capabilities/index.d.ts.map +1 -1
- package/dist/lib/capabilities/index.js +1 -0
- package/dist/lib/capabilities/index.js.map +1 -1
- package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
- package/dist/lib/capabilities/use-capability.js +1 -1
- package/dist/lib/capabilities/use-capability.js.map +1 -1
- package/dist/lib/system-bus/deeplink.js.map +1 -1
- package/dist/lib/system-bus/host-ports.d.ts +2 -1
- package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.js.map +1 -1
- package/dist/offline/index.d.ts +2 -1
- package/dist/offline/index.d.ts.map +1 -1
- package/dist/offline/index.js +2 -1
- package/dist/offline/index.js.map +1 -1
- package/dist/offline/offline-status-badge.js +1 -1
- package/dist/offline/offline-status-badge.js.map +1 -1
- package/dist/offline/outbox.js.map +1 -1
- package/dist/offline/react-hooks.d.ts +7 -0
- package/dist/offline/react-hooks.d.ts.map +1 -0
- package/dist/offline/react-hooks.js +25 -0
- package/dist/offline/react-hooks.js.map +1 -0
- package/dist/offline/react.d.ts +0 -4
- package/dist/offline/react.d.ts.map +1 -1
- package/dist/offline/react.js +2 -24
- package/dist/offline/react.js.map +1 -1
- package/dist/offline/storage.d.ts.map +1 -1
- package/dist/offline/storage.js +8 -5
- package/dist/offline/storage.js.map +1 -1
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -0
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.js +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
- package/dist/registry/lib/session-context-provider.d.ts +1 -3
- package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
- package/dist/registry/lib/session-context-provider.js +2 -15
- package/dist/registry/lib/session-context-provider.js.map +1 -1
- package/dist/registry/lib/session-context.d.ts +5 -0
- package/dist/registry/lib/session-context.d.ts.map +1 -0
- package/dist/registry/lib/session-context.js +15 -0
- package/dist/registry/lib/session-context.js.map +1 -0
- package/dist/registry/lib/session-selectors.d.ts.map +1 -1
- package/dist/registry/lib/session-selectors.js +3 -1
- package/dist/registry/lib/session-selectors.js.map +1 -1
- package/dist/ui/branding/app-branding-context.d.ts +9 -0
- package/dist/ui/branding/app-branding-context.d.ts.map +1 -0
- package/dist/ui/branding/app-branding-context.js +10 -0
- package/dist/ui/branding/app-branding-context.js.map +1 -0
- package/dist/ui/branding/app-branding-provider.d.ts +0 -6
- package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
- package/dist/ui/branding/app-branding-provider.js +1 -8
- package/dist/ui/branding/app-branding-provider.js.map +1 -1
- package/dist/ui/capture/schema-field-input.js +1 -1
- package/dist/ui/capture/schema-field-input.js.map +1 -1
- package/dist/ui/capture/use-prefill.d.ts.map +1 -1
- package/dist/ui/capture/use-prefill.js +3 -1
- package/dist/ui/capture/use-prefill.js.map +1 -1
- package/dist/ui/capture/use-wizard.d.ts +1 -1
- package/dist/ui/capture/use-wizard.d.ts.map +1 -1
- package/dist/ui/capture/use-wizard.js +1 -1
- package/dist/ui/capture/use-wizard.js.map +1 -1
- package/dist/ui/capture/wizard.d.ts.map +1 -1
- package/dist/ui/capture/wizard.js.map +1 -1
- package/dist/ui/index.d.ts +5 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +5 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/primitives/alert-dialog.js +1 -1
- package/dist/ui/primitives/alert-dialog.js.map +1 -1
- package/dist/ui/primitives/badge-variants.d.ts +4 -0
- package/dist/ui/primitives/badge-variants.d.ts.map +1 -0
- package/dist/ui/primitives/badge-variants.js +15 -0
- package/dist/ui/primitives/badge-variants.js.map +1 -0
- package/dist/ui/primitives/badge.d.ts +3 -5
- package/dist/ui/primitives/badge.d.ts.map +1 -1
- package/dist/ui/primitives/badge.js +2 -15
- package/dist/ui/primitives/badge.js.map +1 -1
- package/dist/ui/primitives/button-variants.d.ts +5 -0
- package/dist/ui/primitives/button-variants.d.ts.map +1 -0
- package/dist/ui/primitives/button-variants.js +24 -0
- package/dist/ui/primitives/button-variants.js.map +1 -0
- package/dist/ui/primitives/button.d.ts +3 -6
- package/dist/ui/primitives/button.d.ts.map +1 -1
- package/dist/ui/primitives/button.js +2 -24
- package/dist/ui/primitives/button.js.map +1 -1
- package/dist/ui/primitives/form-stepper.d.ts +1 -13
- package/dist/ui/primitives/form-stepper.d.ts.map +1 -1
- package/dist/ui/primitives/form-stepper.js +1 -6
- package/dist/ui/primitives/form-stepper.js.map +1 -1
- package/dist/ui/primitives/form-stepper.types.d.ts +14 -0
- package/dist/ui/primitives/form-stepper.types.d.ts.map +1 -0
- package/dist/ui/primitives/form-stepper.types.js +7 -0
- package/dist/ui/primitives/form-stepper.types.js.map +1 -0
- package/dist/ui/primitives/preview-device-frame.js +1 -1
- package/dist/ui/primitives/preview-device-frame.js.map +1 -1
- package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
- package/dist/ui/primitives/tag-multi-select.js +1 -1
- package/dist/ui/primitives/tag-multi-select.js.map +1 -1
- package/dist/vista/index.d.ts +4 -0
- package/dist/vista/index.d.ts.map +1 -1
- package/dist/vista/index.js +4 -0
- package/dist/vista/index.js.map +1 -1
- package/dist/vista/surfaces/FilesSurface.d.ts +1 -12
- package/dist/vista/surfaces/FilesSurface.d.ts.map +1 -1
- package/dist/vista/surfaces/FilesSurface.js +2 -46
- package/dist/vista/surfaces/FilesSurface.js.map +1 -1
- package/dist/vista/surfaces/FormSurface.d.ts.map +1 -1
- package/dist/vista/surfaces/FormSurface.js +39 -12
- package/dist/vista/surfaces/FormSurface.js.map +1 -1
- package/dist/vista/surfaces/elicitation-transport.d.ts +12 -3
- package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -1
- package/dist/vista/surfaces/elicitation-transport.js +96 -3
- package/dist/vista/surfaces/elicitation-transport.js.map +1 -1
- package/dist/vista/surfaces/file-surface-utils.d.ts +13 -0
- package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -0
- package/dist/vista/surfaces/file-surface-utils.js +46 -0
- package/dist/vista/surfaces/file-surface-utils.js.map +1 -0
- package/dist/vista/surfaces/index.d.ts +4 -3
- package/dist/vista/surfaces/index.d.ts.map +1 -1
- package/dist/vista/surfaces/index.js +4 -81
- package/dist/vista/surfaces/index.js.map +1 -1
- package/dist/vista/surfaces/register-surfaces.d.ts +2 -0
- package/dist/vista/surfaces/register-surfaces.d.ts.map +1 -0
- package/dist/vista/surfaces/register-surfaces.js +80 -0
- package/dist/vista/surfaces/register-surfaces.js.map +1 -0
- package/dist/vista/surfaces/text-surfaces.js +1 -1
- package/dist/vista/surfaces/text-surfaces.js.map +1 -1
- package/dist/vista/vista-code-renderer-context.d.ts +9 -0
- package/dist/vista/vista-code-renderer-context.d.ts.map +1 -0
- package/dist/vista/vista-code-renderer-context.js +6 -0
- package/dist/vista/vista-code-renderer-context.js.map +1 -0
- package/dist/vista/vista-code-renderer.d.ts +1 -6
- package/dist/vista/vista-code-renderer.d.ts.map +1 -1
- package/dist/vista/vista-code-renderer.js +1 -5
- package/dist/vista/vista-code-renderer.js.map +1 -1
- package/dist/vista/vista-file-context.d.ts +8 -0
- package/dist/vista/vista-file-context.d.ts.map +1 -0
- package/dist/vista/vista-file-context.js +6 -0
- package/dist/vista/vista-file-context.js.map +1 -0
- package/dist/vista/vista-file-source.d.ts +1 -6
- package/dist/vista/vista-file-source.d.ts.map +1 -1
- package/dist/vista/vista-file-source.js +2 -5
- package/dist/vista/vista-file-source.js.map +1 -1
- package/dist/vista/vista-form-context.d.ts +13 -0
- package/dist/vista/vista-form-context.d.ts.map +1 -0
- package/dist/vista/vista-form-context.js +6 -0
- package/dist/vista/vista-form-context.js.map +1 -0
- package/dist/vista/vista-form-host.d.ts +1 -10
- package/dist/vista/vista-form-host.d.ts.map +1 -1
- package/dist/vista/vista-form-host.js +1 -5
- package/dist/vista/vista-form-host.js.map +1 -1
- package/dist/vista/vista-open-context.d.ts +1 -3
- package/dist/vista/vista-open-context.d.ts.map +1 -1
- package/dist/vista/vista-open-context.js +1 -5
- package/dist/vista/vista-open-context.js.map +1 -1
- package/dist/vista/vista-open-state.d.ts +5 -0
- package/dist/vista/vista-open-state.d.ts.map +1 -0
- package/dist/vista/vista-open-state.js +6 -0
- package/dist/vista/vista-open-state.js.map +1 -0
- package/dist/vista/vista-payloads.d.ts +2 -3
- package/dist/vista/vista-payloads.d.ts.map +1 -1
- package/dist/vista/vista-payloads.js +18 -21
- package/dist/vista/vista-payloads.js.map +1 -1
- package/dist/vista/vista-tools.d.ts.map +1 -1
- package/dist/vista/vista-tools.js.map +1 -1
- package/package.json +3 -3
- package/src/lib/biome-host/biome-builders.ts +4 -1
- package/src/lib/biome-host/biome-registry.ts +0 -1
- package/src/lib/biome-host/biome-scope-hooks.ts +39 -0
- package/src/lib/biome-host/biome-scope.tsx +1 -68
- package/src/lib/biome-host/biome-scoped-query.ts +4 -1
- package/src/lib/biome-host/create-biome-orval-config.ts +2 -2
- package/src/lib/biome-host/errors.ts +1 -1
- package/src/lib/biome-host/index.ts +1 -1
- package/src/lib/biome-host/session-profiles.ts +7 -1
- package/src/lib/capabilities/capability-context.ts +25 -0
- package/src/lib/capabilities/capability-provider.tsx +5 -34
- package/src/lib/capabilities/index.ts +1 -0
- package/src/lib/capabilities/use-capability.ts +4 -1
- package/src/lib/system-bus/deeplink.ts +1 -1
- package/src/lib/system-bus/host-ports.ts +2 -1
- package/src/lib/system-bus/intent-registry.ts +1 -2
- package/src/offline/index.ts +4 -2
- package/src/offline/offline-status-badge.tsx +1 -1
- package/src/offline/outbox.test.ts +23 -16
- package/src/offline/outbox.ts +1 -1
- package/src/offline/react-hooks.ts +37 -0
- package/src/offline/react.tsx +1 -40
- package/src/offline/storage.ts +11 -6
- package/src/registry/index.ts +1 -0
- package/src/registry/lib/primitives/SessionEventCard.tsx +4 -2
- package/src/registry/lib/session-context-provider.tsx +8 -25
- package/src/registry/lib/session-context.ts +21 -0
- package/src/registry/lib/session-selectors.ts +3 -1
- package/src/ui/branding/app-branding-context.ts +20 -0
- package/src/ui/branding/app-branding-provider.tsx +5 -27
- package/src/ui/capture/schema-field-input.tsx +7 -1
- package/src/ui/capture/use-prefill.ts +4 -1
- package/src/ui/capture/use-wizard.ts +2 -2
- package/src/ui/capture/wizard.tsx +2 -1
- package/src/ui/index.ts +7 -2
- package/src/ui/primitives/alert-dialog.tsx +1 -1
- package/src/ui/primitives/badge-variants.ts +21 -0
- package/src/ui/primitives/badge.tsx +3 -19
- package/src/ui/primitives/button-variants.ts +31 -0
- package/src/ui/primitives/button.tsx +3 -28
- package/src/ui/primitives/form-stepper.tsx +1 -16
- package/src/ui/primitives/form-stepper.types.ts +15 -0
- package/src/ui/primitives/preview-device-frame.tsx +1 -1
- package/src/ui/primitives/tag-multi-select.tsx +3 -4
- package/src/vista/form-payload.spec.ts +71 -25
- package/src/vista/index.ts +4 -0
- package/src/vista/surfaces/FilesSurface.tsx +2 -68
- package/src/vista/surfaces/FormSurface.tsx +125 -26
- package/src/vista/surfaces/elicitation-transport.spec.ts +205 -23
- package/src/vista/surfaces/elicitation-transport.ts +192 -14
- package/src/vista/surfaces/file-surface-utils.ts +62 -0
- package/src/vista/surfaces/index.ts +24 -0
- package/src/vista/surfaces/{index.tsx → register-surfaces.tsx} +1 -21
- package/src/vista/surfaces/text-surfaces.tsx +1 -1
- package/src/vista/vista-code-renderer-context.ts +16 -0
- package/src/vista/vista-code-renderer.tsx +4 -16
- package/src/vista/vista-file-context.ts +14 -0
- package/src/vista/vista-file-source.tsx +5 -28
- package/src/vista/vista-form-context.ts +20 -0
- package/src/vista/vista-form-host.tsx +11 -36
- package/src/vista/vista-open-context.tsx +4 -14
- package/src/vista/vista-open-state.ts +11 -0
- package/src/vista/vista-payloads.ts +44 -46
- package/src/vista/vista-tools.ts +2 -2
- package/dist/lib/biome-host/response-envelope.d.ts +0 -3
- package/dist/lib/biome-host/response-envelope.d.ts.map +0 -1
- package/dist/lib/biome-host/response-envelope.js +0 -21
- package/dist/lib/biome-host/response-envelope.js.map +0 -1
- package/src/lib/biome-host/response-envelope.ts +0 -69
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
-
import {
|
|
2
|
+
import type { VariantProps } from "class-variance-authority";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
5
|
import { cn } from '../cn';
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-body-1 font-medium ring-offset-background transition-[background-color,color,border-color,box-shadow] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
-
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
14
|
-
outline: "border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
15
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
16
|
-
ghost: "text-ink-2 hover:bg-paper-elev hover:text-ink",
|
|
17
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
18
|
-
},
|
|
19
|
-
size: {
|
|
20
|
-
default: "h-10 px-4 py-2",
|
|
21
|
-
sm: "h-9 rounded-md px-3",
|
|
22
|
-
lg: "h-11 rounded-md px-8",
|
|
23
|
-
icon: "h-10 w-10",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
defaultVariants: {
|
|
27
|
-
variant: "default",
|
|
28
|
-
size: "default",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
);
|
|
6
|
+
import { buttonVariants } from './button-variants';
|
|
32
7
|
|
|
33
8
|
export interface ButtonProps
|
|
34
9
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
@@ -44,4 +19,4 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
44
19
|
);
|
|
45
20
|
Button.displayName = "Button";
|
|
46
21
|
|
|
47
|
-
export { Button
|
|
22
|
+
export { Button };
|
|
@@ -1,22 +1,7 @@
|
|
|
1
1
|
import { Check } from 'lucide-react';
|
|
2
2
|
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
|
|
5
3
|
import { cn } from '../cn';
|
|
6
|
-
|
|
7
|
-
export enum FormStepStatus {
|
|
8
|
-
PENDING = 'pending',
|
|
9
|
-
ACTIVE = 'active',
|
|
10
|
-
DONE = 'done',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface FormStep {
|
|
14
|
-
readonly key: string;
|
|
15
|
-
readonly label: string;
|
|
16
|
-
readonly hint?: string;
|
|
17
|
-
readonly status: FormStepStatus;
|
|
18
|
-
readonly children: ReactNode;
|
|
19
|
-
}
|
|
4
|
+
import { FormStepStatus, type FormStep } from './form-stepper.types';
|
|
20
5
|
|
|
21
6
|
interface FormStepperProps {
|
|
22
7
|
readonly steps: readonly FormStep[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export enum FormStepStatus {
|
|
4
|
+
PENDING = 'pending',
|
|
5
|
+
ACTIVE = 'active',
|
|
6
|
+
DONE = 'done',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface FormStep {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly label: string;
|
|
12
|
+
readonly hint?: string;
|
|
13
|
+
readonly status: FormStepStatus;
|
|
14
|
+
readonly children: ReactNode;
|
|
15
|
+
}
|
|
@@ -181,7 +181,7 @@ function ResponsiveViewport({
|
|
|
181
181
|
const observer = new ResizeObserver(compute);
|
|
182
182
|
observer.observe(el);
|
|
183
183
|
return () => observer.disconnect();
|
|
184
|
-
}, [bezel.width, bezel.height]);
|
|
184
|
+
}, [aspectRatio, bezel.width, bezel.height]);
|
|
185
185
|
|
|
186
186
|
return (
|
|
187
187
|
<div
|
|
@@ -138,9 +138,8 @@ export function TagMultiSelect({
|
|
|
138
138
|
className={cn('text-body-1 gap-1 pr-1', chipClass)}
|
|
139
139
|
>
|
|
140
140
|
<span className="truncate max-w-[160px]">{opt?.label ?? val}</span>
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
tabIndex={-1}
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
144
143
|
onClick={(e) => remove(val, e)}
|
|
145
144
|
onKeyDown={(e) => {
|
|
146
145
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
@@ -153,7 +152,7 @@ export function TagMultiSelect({
|
|
|
153
152
|
aria-label={`Remove ${opt?.label ?? val}`}
|
|
154
153
|
>
|
|
155
154
|
<X className="h-4 w-4" />
|
|
156
|
-
</
|
|
155
|
+
</button>
|
|
157
156
|
</Badge>
|
|
158
157
|
);
|
|
159
158
|
})}
|
|
@@ -2,22 +2,17 @@
|
|
|
2
2
|
* Guards `validateFormPayload` — the structural half of "the value never
|
|
3
3
|
* reaches the agent".
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
7
|
-
* URL is accepted, and a rejected payload draws the typed fallback card rather
|
|
8
|
-
* than a form.
|
|
5
|
+
* The payload has no network authority: it carries only a base64url opaque ref,
|
|
6
|
+
* while the trusted service base URL comes from `VistaFormHost`.
|
|
9
7
|
*/
|
|
10
8
|
import { describe, expect, it } from 'vitest';
|
|
11
9
|
|
|
12
|
-
import { CaptureFieldType } from '../ui/capture/field-spec';
|
|
13
|
-
|
|
14
10
|
import { validateFormPayload } from './vista-payloads';
|
|
11
|
+
import { CaptureFieldType } from '../ui/capture/field-spec';
|
|
15
12
|
|
|
16
13
|
const BASE = {
|
|
17
14
|
ref: 'ref-1',
|
|
18
15
|
purpose: 'Connect Acme so I can read your tickets.',
|
|
19
|
-
submitUrl: 'https://connector-gateway.example.test/credential-elicitations/ref-1/submit',
|
|
20
|
-
statusUrl: 'https://connector-gateway.example.test/credential-elicitations/ref-1',
|
|
21
16
|
fields: [
|
|
22
17
|
{
|
|
23
18
|
key: 'apiKey',
|
|
@@ -27,7 +22,13 @@ const BASE = {
|
|
|
27
22
|
isSecret: true,
|
|
28
23
|
transform: 'trim',
|
|
29
24
|
},
|
|
30
|
-
{
|
|
25
|
+
{
|
|
26
|
+
key: 'baseUrl',
|
|
27
|
+
label: 'Base URL',
|
|
28
|
+
type: CaptureFieldType.Url,
|
|
29
|
+
required: true,
|
|
30
|
+
isSecret: false,
|
|
31
|
+
},
|
|
31
32
|
],
|
|
32
33
|
};
|
|
33
34
|
|
|
@@ -41,11 +42,17 @@ describe('validateFormPayload', () => {
|
|
|
41
42
|
expect(payload?.fields[0]?.transform).toBe('trim');
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
it('
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
it('rejects old or malicious payload-selected submit/status authorities', () => {
|
|
46
|
+
for (const target of [
|
|
47
|
+
'https://evil.example/steal',
|
|
48
|
+
'https://trusted.example@evil.example/steal',
|
|
49
|
+
'//evil.example/steal',
|
|
50
|
+
'/credential-elicitations/ref-1/submit',
|
|
51
|
+
'javascript:alert(1)',
|
|
52
|
+
]) {
|
|
53
|
+
expect(validateFormPayload({ ...BASE, submitUrl: target })).toBeNull();
|
|
54
|
+
expect(validateFormPayload({ ...BASE, statusUrl: target })).toBeNull();
|
|
55
|
+
}
|
|
49
56
|
});
|
|
50
57
|
|
|
51
58
|
it('accepts an EMPTY field schema — the service is the authority, not the client', () => {
|
|
@@ -55,39 +62,78 @@ describe('validateFormPayload', () => {
|
|
|
55
62
|
});
|
|
56
63
|
|
|
57
64
|
it('rejects a malformed field spec rather than half-rendering it', () => {
|
|
58
|
-
expect(validateFormPayload({ ...BASE, fields: [{ key: 'a', label: 'A', type: 'nope' }] })).toBeNull();
|
|
59
|
-
expect(validateFormPayload({ ...BASE, fields: [{ key: 'a', label: 'A' }] })).toBeNull();
|
|
60
65
|
expect(
|
|
61
66
|
validateFormPayload({
|
|
62
67
|
...BASE,
|
|
63
|
-
fields: [{ key: 'a', label: 'A', type:
|
|
68
|
+
fields: [{ key: 'a', label: 'A', type: 'nope' }],
|
|
69
|
+
}),
|
|
70
|
+
).toBeNull();
|
|
71
|
+
expect(
|
|
72
|
+
validateFormPayload({ ...BASE, fields: [{ key: 'a', label: 'A' }] }),
|
|
73
|
+
).toBeNull();
|
|
74
|
+
expect(
|
|
75
|
+
validateFormPayload({
|
|
76
|
+
...BASE,
|
|
77
|
+
fields: [
|
|
78
|
+
{
|
|
79
|
+
key: 'a',
|
|
80
|
+
label: 'A',
|
|
81
|
+
type: CaptureFieldType.String,
|
|
82
|
+
isSecret: 'yes',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
64
85
|
}),
|
|
65
86
|
).toBeNull();
|
|
66
87
|
expect(validateFormPayload({ ...BASE, fields: {} })).toBeNull();
|
|
67
88
|
});
|
|
68
89
|
|
|
69
|
-
it('accepts a launch
|
|
90
|
+
it('accepts a launch payload with a root-relative trusted-service path', () => {
|
|
70
91
|
const payload = validateFormPayload({
|
|
71
92
|
...BASE,
|
|
72
93
|
fields: [],
|
|
73
|
-
launch: {
|
|
94
|
+
launch: {
|
|
95
|
+
path: '/oauth/authorize?x=1',
|
|
96
|
+
label: 'Continue to Acme',
|
|
97
|
+
hint: 'h',
|
|
98
|
+
},
|
|
74
99
|
});
|
|
75
100
|
expect(payload?.launch).toEqual({
|
|
76
|
-
|
|
101
|
+
path: '/oauth/authorize?x=1',
|
|
77
102
|
label: 'Continue to Acme',
|
|
78
103
|
hint: 'h',
|
|
79
104
|
});
|
|
80
105
|
});
|
|
81
106
|
|
|
82
|
-
it('rejects
|
|
83
|
-
|
|
107
|
+
it('rejects launch authority/scheme confusion and malformed paths', () => {
|
|
108
|
+
for (const path of [
|
|
109
|
+
'authorize',
|
|
110
|
+
'//evil.example/authorize',
|
|
111
|
+
'https://evil.example/authorize',
|
|
112
|
+
'javascript:alert(1)',
|
|
113
|
+
'\\\\evil.example\\authorize',
|
|
114
|
+
]) {
|
|
115
|
+
expect(
|
|
116
|
+
validateFormPayload({ ...BASE, launch: { path, label: 'Go' } }),
|
|
117
|
+
).toBeNull();
|
|
118
|
+
}
|
|
119
|
+
expect(
|
|
120
|
+
validateFormPayload({
|
|
121
|
+
...BASE,
|
|
122
|
+
launch: { url: 'https://evil.example/a', label: 'Go' },
|
|
123
|
+
}),
|
|
124
|
+
).toBeNull();
|
|
84
125
|
expect(
|
|
85
|
-
validateFormPayload({
|
|
126
|
+
validateFormPayload({
|
|
127
|
+
...BASE,
|
|
128
|
+
launch: { path: '/authorize', label: '' },
|
|
129
|
+
}),
|
|
86
130
|
).toBeNull();
|
|
87
131
|
});
|
|
88
132
|
|
|
89
|
-
it('rejects
|
|
90
|
-
|
|
133
|
+
it('rejects missing refs and path-traversal/encoding input', () => {
|
|
134
|
+
for (const ref of ['', '.', '..', '../steal', 'a/b', '%2e%2e', 'réf']) {
|
|
135
|
+
expect(validateFormPayload({ ...BASE, ref })).toBeNull();
|
|
136
|
+
}
|
|
91
137
|
expect(validateFormPayload({ ...BASE, ref: undefined })).toBeNull();
|
|
92
138
|
});
|
|
93
139
|
});
|
package/src/vista/index.ts
CHANGED
|
@@ -36,9 +36,13 @@ export * from './vista-model';
|
|
|
36
36
|
export * from './vista-payloads';
|
|
37
37
|
export * from './vista-tabs-manifest';
|
|
38
38
|
export * from './vista-file-source';
|
|
39
|
+
export * from './vista-file-context';
|
|
39
40
|
export * from './vista-form-host';
|
|
41
|
+
export * from './vista-form-context';
|
|
40
42
|
export * from './vista-code-renderer';
|
|
43
|
+
export * from './vista-code-renderer-context';
|
|
41
44
|
export * from './vista-open-context';
|
|
45
|
+
export * from './vista-open-state';
|
|
42
46
|
export * from './use-vista-tabs';
|
|
43
47
|
export * from './VistaPanel';
|
|
44
48
|
export * from './vista-tools';
|
|
@@ -13,78 +13,12 @@ import { ChevronRight, Folder } from 'lucide-react';
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
import { fileSurface } from '../vista-model';
|
|
16
|
-
import { useVistaOpen } from '../vista-open-
|
|
16
|
+
import { useVistaOpen } from '../vista-open-state';
|
|
17
|
+
import { fileExtMeta, groupFilesByFolder } from './file-surface-utils';
|
|
17
18
|
|
|
18
19
|
import type { VistaFileEntry, VistaFilesPayload } from '../vista-payloads';
|
|
19
20
|
import type { ReactElement } from 'react';
|
|
20
21
|
|
|
21
|
-
/** Extension → colour-coded tile metadata. */
|
|
22
|
-
interface FileExtMeta {
|
|
23
|
-
readonly color: string;
|
|
24
|
-
readonly tag: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const EXT_META: Readonly<Record<string, FileExtMeta>> = {
|
|
28
|
-
ts: { color: 'hsl(var(--info))', tag: 'TS' },
|
|
29
|
-
tsx: { color: 'hsl(var(--info))', tag: 'TSX' },
|
|
30
|
-
js: { color: 'hsl(var(--warning))', tag: 'JS' },
|
|
31
|
-
jsx: { color: 'hsl(var(--warning))', tag: 'JSX' },
|
|
32
|
-
json: { color: 'hsl(var(--ink-3))', tag: 'JSON' },
|
|
33
|
-
md: { color: 'hsl(var(--p-people))', tag: 'MD' },
|
|
34
|
-
css: { color: 'hsl(var(--p-sales))', tag: 'CSS' },
|
|
35
|
-
html: { color: 'hsl(var(--p-sales))', tag: 'HTML' },
|
|
36
|
-
yaml: { color: 'hsl(var(--ink-3))', tag: 'YAML' },
|
|
37
|
-
yml: { color: 'hsl(var(--ink-3))', tag: 'YAML' },
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Resolve the colour-coded tile metadata for a filename. */
|
|
41
|
-
export function fileExtMeta(name: string): FileExtMeta {
|
|
42
|
-
if (/\.test\.[tj]sx?$/.test(name) || /\.spec\.[tj]sx?$/.test(name)) {
|
|
43
|
-
return { color: 'hsl(var(--success))', tag: 'TEST' };
|
|
44
|
-
}
|
|
45
|
-
const ext = name.split('.').pop() ?? '';
|
|
46
|
-
return (
|
|
47
|
-
EXT_META[ext] ?? {
|
|
48
|
-
color: 'hsl(var(--ink-4))',
|
|
49
|
-
tag: (ext || 'FILE').toUpperCase().slice(0, 4),
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** A group of files under one folder caption. */
|
|
55
|
-
export interface VistaFileGroup {
|
|
56
|
-
/** Folder path (`''` = root). */
|
|
57
|
-
readonly dir: string;
|
|
58
|
-
readonly files: readonly VistaFileEntry[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Order folder captions: root first, then alphabetical. */
|
|
62
|
-
function compareDirs(a: string, b: string): number {
|
|
63
|
-
if (a === '') return -1;
|
|
64
|
-
if (b === '') return 1;
|
|
65
|
-
return a.localeCompare(b);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Group a flat file list by folder, root group first, paths sorted. */
|
|
69
|
-
export function groupFilesByFolder(
|
|
70
|
-
files: readonly VistaFileEntry[],
|
|
71
|
-
): VistaFileGroup[] {
|
|
72
|
-
const groups = new Map<string, VistaFileEntry[]>();
|
|
73
|
-
for (const file of files) {
|
|
74
|
-
const slash = file.path.lastIndexOf('/');
|
|
75
|
-
const dir = slash < 0 ? '' : file.path.slice(0, slash);
|
|
76
|
-
const bucket = groups.get(dir);
|
|
77
|
-
if (bucket) bucket.push(file);
|
|
78
|
-
else groups.set(dir, [file]);
|
|
79
|
-
}
|
|
80
|
-
return [...groups.keys()].sort(compareDirs).map((dir) => ({
|
|
81
|
-
dir,
|
|
82
|
-
files: [...(groups.get(dir) ?? [])].sort((a, b) =>
|
|
83
|
-
a.path.localeCompare(b.path),
|
|
84
|
-
),
|
|
85
|
-
}));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
22
|
export function FilesSurface({
|
|
89
23
|
files,
|
|
90
24
|
}: Readonly<VistaFilesPayload>): ReactElement {
|
|
@@ -13,25 +13,45 @@
|
|
|
13
13
|
* • the values live in this component's `useState`; the single reader is
|
|
14
14
|
* `submitElicitationFields`, which takes them as a parameter and returns an
|
|
15
15
|
* outcome — the values are not reachable from any return or any throw;
|
|
16
|
-
* • the
|
|
17
|
-
*
|
|
18
|
-
* the chat transport;
|
|
16
|
+
* • the payload has NO URL authority. The exact endpoints are derived from
|
|
17
|
+
* the trusted elicitation API base URL supplied by `VistaFormHost`;
|
|
19
18
|
* • secret fields are DECLARED (`spec.isSecret`) and render write-only; the
|
|
20
19
|
* surface holds no cache and no read path repopulates them.
|
|
21
20
|
*
|
|
22
21
|
* Nothing here is provider-aware. Whether the flow is a field form or an OAuth
|
|
23
22
|
* handoff is decided by the payload the owning service produced.
|
|
24
23
|
*/
|
|
25
|
-
import {
|
|
26
|
-
|
|
24
|
+
import {
|
|
25
|
+
AlertTriangle,
|
|
26
|
+
CheckCircle2,
|
|
27
|
+
ExternalLink,
|
|
28
|
+
Loader2,
|
|
29
|
+
ShieldCheck,
|
|
30
|
+
} from 'lucide-react';
|
|
31
|
+
import {
|
|
32
|
+
useCallback,
|
|
33
|
+
useEffect,
|
|
34
|
+
useState,
|
|
35
|
+
type ReactElement,
|
|
36
|
+
type ReactNode,
|
|
37
|
+
} from 'react';
|
|
27
38
|
|
|
28
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
readElicitationStatus,
|
|
41
|
+
resolveTrustedElicitationEndpoints,
|
|
42
|
+
resolveTrustedElicitationLaunchUrl,
|
|
43
|
+
submitElicitationFields,
|
|
44
|
+
VistaFormSubmitError,
|
|
45
|
+
} from './elicitation-transport';
|
|
29
46
|
import { SchemaForm } from '../../ui/capture/schema-form';
|
|
30
47
|
import { captureValuesToStringMap } from '../../ui/capture/submit-values';
|
|
31
48
|
import { useSchemaValidation } from '../../ui/capture/use-schema-validation';
|
|
32
49
|
import { cn } from '../../ui/cn';
|
|
33
50
|
import { Button } from '../../ui/primitives/button';
|
|
34
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
useVistaFormHost,
|
|
53
|
+
type VistaFormOutcome,
|
|
54
|
+
} from '../vista-form-context';
|
|
35
55
|
|
|
36
56
|
import type { CaptureValues } from '../../ui/capture/field-spec';
|
|
37
57
|
import type { VistaFormPayload } from '../vista-payloads';
|
|
@@ -51,6 +71,29 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
51
71
|
const [showFieldErrors, setShowFieldErrors] = useState(false);
|
|
52
72
|
const [outcome, setOutcome] = useState<VistaFormOutcome | null>(null);
|
|
53
73
|
const { errors, isValid } = useSchemaValidation(payload.fields, values);
|
|
74
|
+
let transportConfigurationError: string | null = null;
|
|
75
|
+
let launchUrl: string | undefined;
|
|
76
|
+
if (host !== null) {
|
|
77
|
+
try {
|
|
78
|
+
// Validate the trust anchor before retrieving a bearer token or accepting
|
|
79
|
+
// a submit action. The transport repeats this check at the fetch boundary.
|
|
80
|
+
resolveTrustedElicitationEndpoints(
|
|
81
|
+
host.elicitationApiBaseUrl,
|
|
82
|
+
payload.ref,
|
|
83
|
+
);
|
|
84
|
+
if (payload.launch !== undefined) {
|
|
85
|
+
launchUrl = resolveTrustedElicitationLaunchUrl(
|
|
86
|
+
host.elicitationApiBaseUrl,
|
|
87
|
+
payload.launch.path,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
transportConfigurationError =
|
|
92
|
+
error instanceof Error
|
|
93
|
+
? error.message
|
|
94
|
+
: 'The trusted elicitation service is not configured safely.';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
54
97
|
|
|
55
98
|
const settle = useCallback(
|
|
56
99
|
(next: VistaFormOutcome): void => {
|
|
@@ -85,14 +128,14 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
85
128
|
.getAccessToken()
|
|
86
129
|
.then((accessToken) =>
|
|
87
130
|
submitElicitationFields({
|
|
88
|
-
|
|
131
|
+
apiBaseUrl: host.elicitationApiBaseUrl,
|
|
89
132
|
ref: payload.ref,
|
|
90
133
|
accessToken,
|
|
91
134
|
fields: captureValuesToStringMap(payload.fields, values),
|
|
92
135
|
}),
|
|
93
136
|
)
|
|
94
137
|
.then(settle, failed);
|
|
95
|
-
}, [host, isValid, payload.fields, payload.ref,
|
|
138
|
+
}, [host, isValid, payload.fields, payload.ref, settle, failed, values]);
|
|
96
139
|
|
|
97
140
|
const handleCheckStatus = useCallback((): void => {
|
|
98
141
|
if (host === null) return;
|
|
@@ -101,17 +144,23 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
101
144
|
void host
|
|
102
145
|
.getAccessToken()
|
|
103
146
|
.then((accessToken) =>
|
|
104
|
-
readElicitationStatus({
|
|
147
|
+
readElicitationStatus({
|
|
148
|
+
apiBaseUrl: host.elicitationApiBaseUrl,
|
|
149
|
+
ref: payload.ref,
|
|
150
|
+
accessToken,
|
|
151
|
+
}),
|
|
105
152
|
)
|
|
106
153
|
.then((next) => {
|
|
107
154
|
if (next === null) {
|
|
108
|
-
setServiceError(
|
|
155
|
+
setServiceError(
|
|
156
|
+
'Not completed yet — finish the steps in the other window, then check again.',
|
|
157
|
+
);
|
|
109
158
|
setPhase(FormPhase.Editing);
|
|
110
159
|
return;
|
|
111
160
|
}
|
|
112
161
|
settle(next);
|
|
113
162
|
}, failed);
|
|
114
|
-
}, [host, payload.ref,
|
|
163
|
+
}, [host, payload.ref, settle, failed]);
|
|
115
164
|
|
|
116
165
|
// The consent/install window is a separate browsing context, so the only
|
|
117
166
|
// signal this tab gets is the user returning to it. Re-checking on focus is
|
|
@@ -137,6 +186,19 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
137
186
|
);
|
|
138
187
|
}
|
|
139
188
|
|
|
189
|
+
if (transportConfigurationError !== null) {
|
|
190
|
+
return (
|
|
191
|
+
<Shell heading={payload.heading} purpose={payload.purpose}>
|
|
192
|
+
<Notice
|
|
193
|
+
tone="error"
|
|
194
|
+
title="The secure service is not configured safely"
|
|
195
|
+
>
|
|
196
|
+
{transportConfigurationError}
|
|
197
|
+
</Notice>
|
|
198
|
+
</Shell>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
140
202
|
if (phase === FormPhase.Settled && outcome !== null) {
|
|
141
203
|
return (
|
|
142
204
|
<Shell heading={payload.heading} purpose={payload.purpose}>
|
|
@@ -146,7 +208,10 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
146
208
|
<p className="text-body-1 font-medium text-ink">Submitted.</p>
|
|
147
209
|
<p className="text-caption text-ink-3">
|
|
148
210
|
The value went straight to the service and was never shown to the
|
|
149
|
-
agent
|
|
211
|
+
agent
|
|
212
|
+
{outcome.connectionId === null
|
|
213
|
+
? '.'
|
|
214
|
+
: `. Connection ${outcome.connectionId} is ready.`}
|
|
150
215
|
</p>
|
|
151
216
|
</div>
|
|
152
217
|
</div>
|
|
@@ -166,7 +231,9 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
166
231
|
<div className="flex flex-wrap items-center gap-2">
|
|
167
232
|
<Button
|
|
168
233
|
onClick={() => {
|
|
169
|
-
|
|
234
|
+
// `launchUrl` was derived from the host trust anchor above; the
|
|
235
|
+
// payload contributes only a validated same-origin path.
|
|
236
|
+
globalThis.open(launchUrl, '_blank', 'noopener,noreferrer');
|
|
170
237
|
}}
|
|
171
238
|
className="gap-1.5"
|
|
172
239
|
disabled={working}
|
|
@@ -174,7 +241,11 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
174
241
|
<ExternalLink className="h-4 w-4" />
|
|
175
242
|
{payload.launch.label}
|
|
176
243
|
</Button>
|
|
177
|
-
<Button
|
|
244
|
+
<Button
|
|
245
|
+
variant="outline"
|
|
246
|
+
onClick={handleCheckStatus}
|
|
247
|
+
disabled={working}
|
|
248
|
+
>
|
|
178
249
|
{working && <Loader2 className="mr-1 h-4 w-4 animate-spin" />}
|
|
179
250
|
I've finished — check
|
|
180
251
|
</Button>
|
|
@@ -184,8 +255,8 @@ export function FormSurface(payload: Readonly<VistaFormPayload>): ReactElement {
|
|
|
184
255
|
<>
|
|
185
256
|
{payload.fields.length === 0 && (
|
|
186
257
|
<Notice tone="warn" title="No form fields were declared">
|
|
187
|
-
This connector declares no onboarding form. Submitting will ask
|
|
188
|
-
service, which is the authority on whether that is allowed.
|
|
258
|
+
This connector declares no onboarding form. Submitting will ask
|
|
259
|
+
the service, which is the authority on whether that is allowed.
|
|
189
260
|
</Notice>
|
|
190
261
|
)}
|
|
191
262
|
<SchemaForm
|
|
@@ -217,16 +288,25 @@ function Shell({
|
|
|
217
288
|
heading,
|
|
218
289
|
purpose,
|
|
219
290
|
children,
|
|
220
|
-
}: Readonly<{
|
|
291
|
+
}: Readonly<{
|
|
292
|
+
heading?: string;
|
|
293
|
+
purpose: string;
|
|
294
|
+
children: ReactNode;
|
|
295
|
+
}>): ReactElement {
|
|
221
296
|
return (
|
|
222
297
|
<div className="h-full overflow-auto">
|
|
223
298
|
<div className="mx-auto max-w-2xl space-y-4 p-6">
|
|
224
299
|
<div className="space-y-1">
|
|
225
|
-
<h2 className="text-subtitle font-semibold text-ink">
|
|
226
|
-
|
|
300
|
+
<h2 className="text-subtitle font-semibold text-ink">
|
|
301
|
+
{heading ?? 'Secure request'}
|
|
302
|
+
</h2>
|
|
303
|
+
{purpose.length > 0 && (
|
|
304
|
+
<p className="text-body-1 text-ink-2">{purpose}</p>
|
|
305
|
+
)}
|
|
227
306
|
<p className="flex items-center gap-1.5 text-caption text-ink-3">
|
|
228
307
|
<ShieldCheck className="h-3.5 w-3.5" />
|
|
229
|
-
Sent straight to the service with your own sign-in. The agent never
|
|
308
|
+
Sent straight to the service with your own sign-in. The agent never
|
|
309
|
+
sees what you type.
|
|
230
310
|
</p>
|
|
231
311
|
</div>
|
|
232
312
|
{children}
|
|
@@ -239,23 +319,42 @@ function Notice({
|
|
|
239
319
|
tone,
|
|
240
320
|
title,
|
|
241
321
|
children,
|
|
242
|
-
}: Readonly<{
|
|
322
|
+
}: Readonly<{
|
|
323
|
+
tone: 'error' | 'warn';
|
|
324
|
+
title: string;
|
|
325
|
+
children: ReactNode;
|
|
326
|
+
}>): ReactElement {
|
|
243
327
|
return (
|
|
244
328
|
<div
|
|
245
329
|
role="alert"
|
|
246
330
|
className={cn(
|
|
247
331
|
'flex items-start gap-2 rounded-md border p-3',
|
|
248
|
-
tone === 'error'
|
|
332
|
+
tone === 'error'
|
|
333
|
+
? 'border-destructive/40 bg-destructive/10'
|
|
334
|
+
: 'border-rule bg-paper-elev',
|
|
249
335
|
)}
|
|
250
336
|
>
|
|
251
337
|
<AlertTriangle
|
|
252
|
-
className={cn(
|
|
338
|
+
className={cn(
|
|
339
|
+
'mt-0.5 h-4 w-4 shrink-0',
|
|
340
|
+
tone === 'error' ? 'text-destructive' : 'text-ink-3',
|
|
341
|
+
)}
|
|
253
342
|
/>
|
|
254
343
|
<div className="space-y-0.5">
|
|
255
|
-
<p
|
|
344
|
+
<p
|
|
345
|
+
className={cn(
|
|
346
|
+
'text-body-1 font-medium',
|
|
347
|
+
tone === 'error' ? 'text-destructive' : 'text-ink',
|
|
348
|
+
)}
|
|
349
|
+
>
|
|
256
350
|
{title}
|
|
257
351
|
</p>
|
|
258
|
-
<p
|
|
352
|
+
<p
|
|
353
|
+
className={cn(
|
|
354
|
+
'text-body-1',
|
|
355
|
+
tone === 'error' ? 'text-destructive/90' : 'text-ink-3',
|
|
356
|
+
)}
|
|
357
|
+
>
|
|
259
358
|
{children}
|
|
260
359
|
</p>
|
|
261
360
|
</div>
|