@signalcore/ui 1.0.0-beta.0 → 1.0.0-beta.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 +8 -1
- package/dist/AgentAbout.js +7 -4
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentChat.js +10 -2
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentTasks.js +2 -1
- package/dist/AgentTasks.js.map +1 -1
- package/dist/VoiceCall.js +1 -1
- package/dist/VoiceCall.js.map +1 -1
- package/dist/about.d.ts +34 -144
- package/dist/about.js +6 -8
- package/dist/about.js.map +1 -1
- package/dist/automation/cron.d.ts +1 -1
- package/dist/automation/cron.js.map +1 -1
- package/dist/automation/marks.js +7 -3
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +2 -0
- package/dist/automation/messages.js +2 -0
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/types.d.ts +25 -128
- package/dist/automation/types.js +36 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/automation/when.d.ts +1 -1
- package/dist/automation/when.js +5 -1
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +23 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/call.d.ts +1 -1
- package/dist/call.js.map +1 -1
- package/dist/contract.d.ts +262 -0
- package/dist/credentials.js.map +1 -1
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/es.js +9 -1
- package/dist/es.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -1
- package/dist/kit/AudioPlayer.js +1 -1
- package/dist/kit/AudioPlayer.js.map +1 -1
- package/dist/kit/Choices.js +1 -1
- package/dist/kit/Choices.js.map +1 -1
- package/dist/kit/FileField.js +1 -1
- package/dist/kit/FileField.js.map +1 -1
- package/dist/kit/Popover.js.map +1 -1
- package/dist/kit/Tile.d.ts +1 -1
- package/dist/kit/Tile.js +1 -1
- package/dist/kit/Tile.js.map +1 -1
- package/dist/kit/messages.d.ts +13 -0
- package/dist/kit/messages.js +7 -1
- package/dist/kit/messages.js.map +1 -1
- package/dist/kit/uploads.d.ts +2 -1
- package/dist/kit/uploads.js +1 -1
- package/dist/kit/uploads.js.map +1 -1
- package/dist/kit/waveform.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/marks.js +2 -3
- package/dist/marks.js.map +1 -1
- package/dist/surface/Markdown.js +1 -1
- package/dist/surface/Markdown.js.map +1 -1
- package/dist/surface/Surface.js +43 -4
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/scope.d.ts +1 -1
- package/dist/surface/scope.js.map +1 -1
- package/dist/surface/values.js +1 -1
- package/dist/surface/values.js.map +1 -1
- package/dist/tint.d.ts +1 -1
- package/dist/tint.js +1 -1
- package/dist/tint.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.js.map +1 -1
- package/dist/usePolled.js +1 -1
- package/dist/usePolled.js.map +1 -1
- package/dist/voice/webgl.js +1 -1
- package/dist/voice/webgl.js.map +1 -1
- package/dist/widget.js +14 -14
- package/package.json +1 -1
- package/src/AgentAbout.tsx +10 -3
- package/src/AgentChat.tsx +20 -3
- package/src/AgentTasks.tsx +2 -2
- package/src/VoiceCall.tsx +1 -1
- package/src/about.ts +42 -144
- package/src/automation/cron.ts +1 -1
- package/src/automation/marks.tsx +6 -2
- package/src/automation/messages.ts +4 -0
- package/src/automation/types.ts +58 -118
- package/src/automation/when.ts +3 -1
- package/src/blocks.ts +25 -0
- package/src/browser.ts +4 -4
- package/src/call.ts +1 -1
- package/src/contract.ts +1720 -0
- package/src/credentials.ts +2 -2
- package/src/dev.ts +1 -1
- package/src/es.ts +8 -0
- package/src/index.ts +2 -0
- package/src/kit/AudioPlayer.tsx +1 -1
- package/src/kit/Choices.tsx +1 -1
- package/src/kit/FileField.tsx +1 -1
- package/src/kit/Popover.tsx +1 -1
- package/src/kit/Tile.tsx +1 -1
- package/src/kit/messages.ts +20 -0
- package/src/kit/uploads.ts +2 -2
- package/src/kit/waveform.ts +1 -1
- package/src/markdown.ts +1 -1
- package/src/marks.tsx +3 -123
- package/src/styles.css +24 -0
- package/src/surface/Markdown.tsx +1 -1
- package/src/surface/Surface.tsx +53 -4
- package/src/surface/scope.ts +3 -1
- package/src/surface/values.ts +5 -5
- package/src/tint.ts +1 -1
- package/src/types.ts +8 -0
- package/src/usePolled.ts +2 -2
- package/src/voice/webgl.ts +1 -1
package/src/credentials.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type Credentials =
|
|
|
27
27
|
| Alone<{ publishableKey: string; visitorId?: string | undefined }>;
|
|
28
28
|
|
|
29
29
|
/** What one request carries to say who calls, and how to have it anew once it is refused. */
|
|
30
|
-
|
|
30
|
+
interface Signed {
|
|
31
31
|
headers: Readonly<Record<string, string>>;
|
|
32
32
|
/** Lets go of a token the API refused, for the next request to ask for another; null: none. */
|
|
33
33
|
renew: (() => void) | null;
|
|
@@ -41,7 +41,7 @@ export interface Signed {
|
|
|
41
41
|
export type Speaker = "business" | "person";
|
|
42
42
|
|
|
43
43
|
/** How every request and socket of a client says who calls. */
|
|
44
|
-
|
|
44
|
+
interface Signer {
|
|
45
45
|
who: Speaker;
|
|
46
46
|
/** What `fetch` sends of the browser's own: the cookie on the page's origin, or nothing. */
|
|
47
47
|
credentials: RequestCredentials;
|
package/src/dev.ts
CHANGED
|
@@ -12,7 +12,7 @@ function development(): boolean {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const DEV = development();
|
|
16
16
|
|
|
17
17
|
/** A note for the developer of the page, in development only: what the package let pass, and why. */
|
|
18
18
|
export function note(...said: unknown[]): void {
|
package/src/es.ts
CHANGED
|
@@ -76,6 +76,7 @@ export const AUTOMATION_MESSAGES_ES: AutomationMessages = {
|
|
|
76
76
|
on: (often, when) => `${often}, ${when.charAt(0).toLowerCase()}${when.slice(1)}`,
|
|
77
77
|
custom: "Con un horario propio",
|
|
78
78
|
webhook: "Cuando otro sistema la llama",
|
|
79
|
+
platform: "Cuando llega un mensaje",
|
|
79
80
|
event: (app, event) =>
|
|
80
81
|
event
|
|
81
82
|
? `Cuando pasa «${event}» en ${app ?? "una app"}`
|
|
@@ -114,6 +115,7 @@ export const AUTOMATION_MESSAGES_ES: AutomationMessages = {
|
|
|
114
115
|
schedule: "horario",
|
|
115
116
|
webhook: "webhook",
|
|
116
117
|
event: app ? `evento de ${app}` : "evento",
|
|
118
|
+
platform: "un mensaje",
|
|
117
119
|
agent: "se lo pidieron en el chat",
|
|
118
120
|
test: "prueba",
|
|
119
121
|
})[by],
|
|
@@ -220,6 +222,12 @@ export const KIT_MESSAGES_ES: KitMessages = {
|
|
|
220
222
|
pause: "Pausar",
|
|
221
223
|
position: "Posición",
|
|
222
224
|
automation: AUTOMATION_MESSAGES_ES,
|
|
225
|
+
connect: {
|
|
226
|
+
authorize: (app) => `Autorizar en ${app}`,
|
|
227
|
+
connected: "Conectada",
|
|
228
|
+
failed: "No se ha podido conectar",
|
|
229
|
+
preparing: "Preparando la conexión",
|
|
230
|
+
},
|
|
223
231
|
};
|
|
224
232
|
|
|
225
233
|
export const BROWSER_MESSAGES_ES: BrowserMessages = {
|
package/src/index.ts
CHANGED
|
@@ -94,6 +94,7 @@ export type {
|
|
|
94
94
|
Trigger,
|
|
95
95
|
Use,
|
|
96
96
|
} from "./automation/types";
|
|
97
|
+
export { taskCard } from "./automation/types";
|
|
97
98
|
export { AgentTasks, TASK_MESSAGES, TASKS_POLL_MS, useTasks } from "./AgentTasks";
|
|
98
99
|
export type { AgentTasksProps, TaskMessages, TaskPart } from "./AgentTasks";
|
|
99
100
|
export { AgentCard } from "./AgentCard";
|
|
@@ -250,6 +251,7 @@ export type {
|
|
|
250
251
|
Message,
|
|
251
252
|
Request,
|
|
252
253
|
Step,
|
|
254
|
+
StepApp,
|
|
253
255
|
StepState,
|
|
254
256
|
StoredFile,
|
|
255
257
|
ToolCall,
|
package/src/kit/AudioPlayer.tsx
CHANGED
|
@@ -120,7 +120,7 @@ export function AudioPlayer({ src, label, onError, compact, className }: AudioPl
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/** Seconds as a player says them: 0:07, 1:42, 1:02:05. */
|
|
123
|
-
|
|
123
|
+
function clock(seconds: number): string {
|
|
124
124
|
const whole = Math.max(0, Math.floor(seconds));
|
|
125
125
|
const hours = Math.floor(whole / 3600);
|
|
126
126
|
const minutes = Math.floor((whole % 3600) / 60);
|
package/src/kit/Choices.tsx
CHANGED
|
@@ -42,7 +42,7 @@ export function useRadios(count: number, checked: number, check: (index: number)
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Few short options with no hints sit side by side in one bar; any other few are cards. */
|
|
45
|
-
|
|
45
|
+
function segmented(options: Option[]): boolean {
|
|
46
46
|
const length = options.reduce((sum, option) => sum + option.label.length, 0);
|
|
47
47
|
return options.length <= 4 && length <= 32 && options.every((option) => !option.hint);
|
|
48
48
|
}
|
package/src/kit/FileField.tsx
CHANGED
|
@@ -188,7 +188,7 @@ export interface Uploads {
|
|
|
188
188
|
let picks = 0;
|
|
189
189
|
|
|
190
190
|
/** A file already in, by its id: what `useUploads` starts from. */
|
|
191
|
-
|
|
191
|
+
function uploaded(id: string, known: Attachment | undefined): Upload {
|
|
192
192
|
return {
|
|
193
193
|
key: ++picks,
|
|
194
194
|
name: known?.name ?? "",
|
package/src/kit/Popover.tsx
CHANGED
|
@@ -55,7 +55,7 @@ export function usePopover(
|
|
|
55
55
|
return { ...floating, ...interactions };
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
type PopoverState = ReturnType<typeof usePopover>;
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Where the panel goes instead of the page's end: the host's dialog the trigger sits in (a modal
|
package/src/kit/Tile.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { joined, useParts } from "./context";
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Tiles in a grid that asks its own width, not the window's: two across, one when narrow, three
|
|
7
|
-
* when there is room. What an agent works with,
|
|
7
|
+
* when there is room. What an agent works with, the agents at a glance.
|
|
8
8
|
*/
|
|
9
9
|
export function Tiles({
|
|
10
10
|
label,
|
package/src/kit/messages.ts
CHANGED
|
@@ -95,6 +95,20 @@ export interface KitMessages {
|
|
|
95
95
|
position: string;
|
|
96
96
|
/** An automation and its runs: when it runs, how it stands, what each run did. */
|
|
97
97
|
automation: AutomationMessages;
|
|
98
|
+
/** An app for the person to connect, as its connection stands. */
|
|
99
|
+
connect: ConnectMessages;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The words of an app's card: its button, and what it says where the button isn't. */
|
|
103
|
+
export interface ConnectMessages {
|
|
104
|
+
/** The button that opens the app's consent page, given the app's name. */
|
|
105
|
+
authorize: (app: string) => string;
|
|
106
|
+
/** It works. */
|
|
107
|
+
connected: string;
|
|
108
|
+
/** It failed, or the account was lost: the agent asks for it again. */
|
|
109
|
+
failed: string;
|
|
110
|
+
/** Before the platform has the consent page. */
|
|
111
|
+
preparing: string;
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
const FAMILIES: Readonly<Record<string, string>> = {
|
|
@@ -157,4 +171,10 @@ export const KIT_MESSAGES: KitMessages = {
|
|
|
157
171
|
pause: "Pause",
|
|
158
172
|
position: "Position",
|
|
159
173
|
automation: AUTOMATION_MESSAGES,
|
|
174
|
+
connect: {
|
|
175
|
+
authorize: (app) => `Authorize in ${app}`,
|
|
176
|
+
connected: "Connected",
|
|
177
|
+
failed: "It couldn't be connected",
|
|
178
|
+
preparing: "Getting it ready",
|
|
179
|
+
},
|
|
160
180
|
};
|
package/src/kit/uploads.ts
CHANGED
|
@@ -38,14 +38,14 @@ export interface FileHost {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** A file as picked: its name, its type as the browser gave it ("" when none), its weight. */
|
|
41
|
-
|
|
41
|
+
interface Picked {
|
|
42
42
|
name: string;
|
|
43
43
|
type: string;
|
|
44
44
|
size: number;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/** A name's extension, lowercase, without its dot; "" for one without (`README`, `.env`). */
|
|
48
|
-
|
|
48
|
+
function extensionOf(name: string): string {
|
|
49
49
|
const dot = name.lastIndexOf(".");
|
|
50
50
|
return dot <= 0 ? "" : name.slice(dot + 1).toLowerCase();
|
|
51
51
|
}
|
package/src/kit/waveform.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
|
|
|
4
4
|
const MOST_BYTES = 8 * 1024 * 1024;
|
|
5
5
|
|
|
6
6
|
/** An audio as drawn: its bars' heights, and how long it is (what an Ogg doesn't say upfront). */
|
|
7
|
-
|
|
7
|
+
interface Waveform {
|
|
8
8
|
heights: number[];
|
|
9
9
|
seconds: number;
|
|
10
10
|
}
|
package/src/markdown.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type Inline =
|
|
|
8
8
|
| { kind: "text" | "strong" | "em" | "code"; text: string }
|
|
9
9
|
| { kind: "link"; text: string; href: string };
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
type Node =
|
|
12
12
|
| { kind: "heading"; level: number; inline: Inline[] }
|
|
13
13
|
| { kind: "paragraph"; inline: Inline[] }
|
|
14
14
|
| { kind: "list"; items: Inline[][] };
|
package/src/marks.tsx
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
ActionSource,
|
|
5
|
-
AutomationKind,
|
|
6
|
-
ChannelKind,
|
|
7
|
-
DeviceKind,
|
|
8
|
-
Icon,
|
|
9
|
-
Provider,
|
|
10
|
-
} from "./about";
|
|
3
|
+
import type { ActionSource, ChannelKind, DeviceKind, Provider } from "./about";
|
|
11
4
|
|
|
12
5
|
/**
|
|
13
|
-
* The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the
|
|
14
|
-
*
|
|
15
|
-
* glyph of each app, channel and device. No icon library: a host reads nothing but this package.
|
|
6
|
+
* The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the shipped actions'
|
|
7
|
+
* marks and the simplest faithful glyph of each app, channel and device. No icon library: a host reads nothing but this package.
|
|
16
8
|
*/
|
|
17
9
|
|
|
18
10
|
const STROKE = {
|
|
@@ -49,36 +41,12 @@ const mail = (
|
|
|
49
41
|
<path d="m2.5 5.5 5.5 3.7 5.5-3.7" />
|
|
50
42
|
</>
|
|
51
43
|
);
|
|
52
|
-
const calendar = (
|
|
53
|
-
<>
|
|
54
|
-
<rect x="2.5" y="3.5" width="11" height="10" rx="1.5" />
|
|
55
|
-
<path d="M2.5 7h11M5.5 2v3M10.5 2v3" />
|
|
56
|
-
</>
|
|
57
|
-
);
|
|
58
|
-
const clock = (
|
|
59
|
-
<>
|
|
60
|
-
<circle cx="8" cy="8" r="5.75" />
|
|
61
|
-
<path d="M8 4.75V8l2.2 1.4" />
|
|
62
|
-
</>
|
|
63
|
-
);
|
|
64
44
|
const search = (
|
|
65
45
|
<>
|
|
66
46
|
<circle cx="7" cy="7" r="4.25" />
|
|
67
47
|
<path d="m10.2 10.2 3.3 3.3" />
|
|
68
48
|
</>
|
|
69
49
|
);
|
|
70
|
-
const file = (
|
|
71
|
-
<>
|
|
72
|
-
<path d="M4 2.5h5l3.5 3.5v7.5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1z" />
|
|
73
|
-
<path d="M9 2.5V6h3.5" />
|
|
74
|
-
</>
|
|
75
|
-
);
|
|
76
|
-
const table = (
|
|
77
|
-
<>
|
|
78
|
-
<rect x="2.5" y="3" width="11" height="10" rx="1.5" />
|
|
79
|
-
<path d="M2.5 6.5h11M6.5 6.5V13" />
|
|
80
|
-
</>
|
|
81
|
-
);
|
|
82
50
|
const bell = (
|
|
83
51
|
<>
|
|
84
52
|
<path d="M4 11V7.5a4 4 0 0 1 8 0V11l1 1.5H3z" />
|
|
@@ -91,12 +59,6 @@ const hand = (
|
|
|
91
59
|
<path d="M5.5 8.2 4.4 7a1 1 0 0 0-1.6 1.2L5 11.6a3.6 3.6 0 0 0 3.1 1.9H9a2.5 2.5 0 0 0 2.5-2.5V9.1" />
|
|
92
60
|
</>
|
|
93
61
|
);
|
|
94
|
-
const user = (
|
|
95
|
-
<>
|
|
96
|
-
<circle cx="8" cy="5.5" r="2.75" />
|
|
97
|
-
<path d="M3 13.5a5 5 0 0 1 10 0" />
|
|
98
|
-
</>
|
|
99
|
-
);
|
|
100
62
|
const globe = (
|
|
101
63
|
<>
|
|
102
64
|
<circle cx="8" cy="8" r="5.75" />
|
|
@@ -104,71 +66,6 @@ const globe = (
|
|
|
104
66
|
</>
|
|
105
67
|
);
|
|
106
68
|
const code = <path d="m5.5 5-3 3 3 3M10.5 5l3 3-3 3M9.3 3.5 6.7 12.5" />;
|
|
107
|
-
const lock = (
|
|
108
|
-
<>
|
|
109
|
-
<rect x="3.5" y="7" width="9" height="6.5" rx="1.5" />
|
|
110
|
-
<path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2" />
|
|
111
|
-
</>
|
|
112
|
-
);
|
|
113
|
-
const cart = (
|
|
114
|
-
<>
|
|
115
|
-
<path d="M2 3h1.8l1.4 7h7.3l1.3-5H4.4" />
|
|
116
|
-
<circle cx="6" cy="12.8" r=".9" fill="currentColor" stroke="none" />
|
|
117
|
-
<circle cx="11.5" cy="12.8" r=".9" fill="currentColor" stroke="none" />
|
|
118
|
-
</>
|
|
119
|
-
);
|
|
120
|
-
const card = (
|
|
121
|
-
<>
|
|
122
|
-
<rect x="2" y="3.5" width="12" height="9" rx="1.5" />
|
|
123
|
-
<path d="M2 6.5h12M4.5 10h3" />
|
|
124
|
-
</>
|
|
125
|
-
);
|
|
126
|
-
const map = (
|
|
127
|
-
<>
|
|
128
|
-
<path d="m2.5 4.5 3.5-1.5 4 1.5 3.5-1.5v9l-3.5 1.5-4-1.5-3.5 1.5z" />
|
|
129
|
-
<path d="M6 3v9M10 4.5v9" />
|
|
130
|
-
</>
|
|
131
|
-
);
|
|
132
|
-
const check = <path d="m3.5 8.5 3 3 6-7" />;
|
|
133
|
-
const warning = (
|
|
134
|
-
<>
|
|
135
|
-
<path d="M8 2.5 14 13H2z" />
|
|
136
|
-
<path d="M8 6.5v3" />
|
|
137
|
-
<circle cx="8" cy="11.3" r=".8" fill="currentColor" stroke="none" />
|
|
138
|
-
</>
|
|
139
|
-
);
|
|
140
|
-
const sparkle = (
|
|
141
|
-
<>
|
|
142
|
-
<path d="M7 2.5c.4 2.9 1.5 4 4.4 4.4-2.9.4-4 1.5-4.4 4.4-.4-2.9-1.5-4-4.4-4.4C5.5 6.5 6.6 5.4 7 2.5z" />
|
|
143
|
-
<path d="M12.3 10.5c.2 1 .5 1.3 1.5 1.5-1 .2-1.3.5-1.5 1.5-.2-1-.5-1.3-1.5-1.5 1-.2 1.3-.5 1.5-1.5z" />
|
|
144
|
-
</>
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const ICONS: Readonly<Record<Icon, ReactNode>> = {
|
|
148
|
-
message,
|
|
149
|
-
phone,
|
|
150
|
-
mail,
|
|
151
|
-
calendar,
|
|
152
|
-
clock,
|
|
153
|
-
search,
|
|
154
|
-
file,
|
|
155
|
-
table,
|
|
156
|
-
bell,
|
|
157
|
-
hand,
|
|
158
|
-
user,
|
|
159
|
-
globe,
|
|
160
|
-
code,
|
|
161
|
-
lock,
|
|
162
|
-
cart,
|
|
163
|
-
card,
|
|
164
|
-
map,
|
|
165
|
-
check,
|
|
166
|
-
warning,
|
|
167
|
-
sparkle,
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/** The mark of a generated line. */
|
|
171
|
-
export const iconOf = (icon: Icon): ReactNode => <Glyph>{ICONS[icon]}</Glyph>;
|
|
172
69
|
|
|
173
70
|
const pane = (
|
|
174
71
|
<>
|
|
@@ -211,23 +108,6 @@ export const actionMark = (name: string, source: ActionSource): ReactNode => (
|
|
|
211
108
|
<Glyph>{source === "own" ? code : (ACTIONS[name] ?? bolt)}</Glyph>
|
|
212
109
|
);
|
|
213
110
|
|
|
214
|
-
const hook = (
|
|
215
|
-
<>
|
|
216
|
-
<path d="M2.5 8h7M7 5.5 9.5 8 7 10.5" />
|
|
217
|
-
<path d="M11 3.5h1.5a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H11" />
|
|
218
|
-
</>
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
const AUTOMATIONS: Readonly<Record<AutomationKind, ReactNode>> = {
|
|
222
|
-
schedule: clock,
|
|
223
|
-
webhook: hook,
|
|
224
|
-
event: bolt,
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
export const automationMark = (kind: AutomationKind): ReactNode => (
|
|
228
|
-
<Glyph>{AUTOMATIONS[kind]}</Glyph>
|
|
229
|
-
);
|
|
230
|
-
|
|
231
111
|
/** A letter alone: the simplest monogram of an app. */
|
|
232
112
|
function Monogram({ letter }: { letter: string }) {
|
|
233
113
|
return (
|
package/src/styles.css
CHANGED
|
@@ -501,6 +501,15 @@
|
|
|
501
501
|
height: 16px;
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
/* A connected app's logo, in the same square as a glyph: its own colours, softly cornered. */
|
|
505
|
+
.sc-chat__mark > img {
|
|
506
|
+
display: block;
|
|
507
|
+
width: 16px;
|
|
508
|
+
height: 16px;
|
|
509
|
+
border-radius: 4px;
|
|
510
|
+
object-fit: contain;
|
|
511
|
+
}
|
|
512
|
+
|
|
504
513
|
.sc-chat__step--waiting .sc-chat__mark {
|
|
505
514
|
color: var(--_accent);
|
|
506
515
|
}
|
|
@@ -5148,6 +5157,21 @@ button.sc-attachment__main:focus-visible {
|
|
|
5148
5157
|
font-size: var(--_font-size-sm);
|
|
5149
5158
|
}
|
|
5150
5159
|
|
|
5160
|
+
/* An app's card, where its button isn't: a quiet word (on its way, failed); once it works, in
|
|
5161
|
+
the ink, after a check. */
|
|
5162
|
+
.sc-surface__connect {
|
|
5163
|
+
display: flex;
|
|
5164
|
+
align-items: center;
|
|
5165
|
+
gap: 6px;
|
|
5166
|
+
margin: 0;
|
|
5167
|
+
color: var(--_muted);
|
|
5168
|
+
font-size: var(--_font-size-sm);
|
|
5169
|
+
}
|
|
5170
|
+
|
|
5171
|
+
.sc-surface__connect--done {
|
|
5172
|
+
color: var(--_fg);
|
|
5173
|
+
}
|
|
5174
|
+
|
|
5151
5175
|
/* An automation that is not there any more: a quiet line in a dashed box where it would be. */
|
|
5152
5176
|
.sc-automation--gone {
|
|
5153
5177
|
margin: 0;
|
package/src/surface/Markdown.tsx
CHANGED
|
@@ -33,7 +33,7 @@ export function Markdown({ text }: { text: string }) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/** One line's words, its bold, italic, code and links drawn; a link opens apart, never in place. */
|
|
36
|
-
|
|
36
|
+
function Words({ inline }: { inline: Inline[] }) {
|
|
37
37
|
return inline.map((part, index) => {
|
|
38
38
|
switch (part.kind) {
|
|
39
39
|
case "text":
|
package/src/surface/Surface.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
Block,
|
|
8
8
|
ButtonBlock,
|
|
9
9
|
ChoiceBlock,
|
|
10
|
+
ConnectBlock,
|
|
10
11
|
FieldsBlock,
|
|
11
12
|
ImageBlock,
|
|
12
13
|
ListBlock,
|
|
@@ -75,6 +76,7 @@ const BLOCKS: ReadonlySet<string> = new Set([
|
|
|
75
76
|
"image",
|
|
76
77
|
"automation",
|
|
77
78
|
"file",
|
|
79
|
+
"connect",
|
|
78
80
|
"button",
|
|
79
81
|
"choice",
|
|
80
82
|
"form",
|
|
@@ -90,12 +92,15 @@ export function known(blocks: Block[]): boolean {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/**
|
|
93
|
-
* Whether a screen draws its own edge: one that is a single card (or an automation,
|
|
94
|
-
* its own) needs no panel around it where it is shown.
|
|
95
|
+
* Whether a screen draws its own edge: one that is a single card (or an automation, or an app
|
|
96
|
+
* to connect, each a card of its own) needs no panel around it where it is shown.
|
|
95
97
|
*/
|
|
96
98
|
export function framesItself(surface: Shown): boolean {
|
|
97
99
|
const [only, ...more] = surface.blocks;
|
|
98
|
-
return
|
|
100
|
+
return (
|
|
101
|
+
more.length === 0 &&
|
|
102
|
+
(only?.type === "card" || only?.type === "automation" || only?.type === "connect")
|
|
103
|
+
);
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
/**
|
|
@@ -214,6 +219,8 @@ function Draw({ block }: { block: Block }) {
|
|
|
214
219
|
return <AutomationShown block={block} />;
|
|
215
220
|
case "file":
|
|
216
221
|
return <FileShown block={block} />;
|
|
222
|
+
case "connect":
|
|
223
|
+
return <Connect block={block} />;
|
|
217
224
|
case "button":
|
|
218
225
|
return <Pressable block={block} />;
|
|
219
226
|
case "choice":
|
|
@@ -268,7 +275,7 @@ function FileShown({ block }: { block: FileBlock }) {
|
|
|
268
275
|
type PlayedKind = "image" | "video" | "audio";
|
|
269
276
|
|
|
270
277
|
/** How a file plays in the page: a picture, a video, a sound; null for any other kind. */
|
|
271
|
-
|
|
278
|
+
function playedAs(mediaType: string): PlayedKind | null {
|
|
272
279
|
const family = mediaType.split("/")[0];
|
|
273
280
|
return family === "image" || family === "video" || family === "audio" ? family : null;
|
|
274
281
|
}
|
|
@@ -452,6 +459,48 @@ function AutomationShown({ block }: { block: AutomationBlock }) {
|
|
|
452
459
|
return <Automation automation={block.view} runs={block.view.runs} />;
|
|
453
460
|
}
|
|
454
461
|
|
|
462
|
+
/**
|
|
463
|
+
* An app for the person to connect, as its connection stands: its logo and name over the line
|
|
464
|
+
* the agent wrote of what it will do with it; while it waits for them, the button that opens
|
|
465
|
+
* its consent page apart; once it works, or if it failed, a quiet word in its place. Before
|
|
466
|
+
* the API has asked for it, only the app as the agent named it.
|
|
467
|
+
*/
|
|
468
|
+
function Connect({ block }: { block: ConnectBlock }) {
|
|
469
|
+
const part = useSurfaceParts();
|
|
470
|
+
const { messages } = useKit();
|
|
471
|
+
const { connection } = block;
|
|
472
|
+
const title = connection?.title ?? block.app;
|
|
473
|
+
const pending = connection?.status === "pending" ? connection.url : null;
|
|
474
|
+
const url = pending !== null && https(pending) ? pending : null;
|
|
475
|
+
const working = connection?.status === "active";
|
|
476
|
+
const failed = connection !== null && !working && connection.status !== "pending";
|
|
477
|
+
return (
|
|
478
|
+
<Card title={title} subtitle={block.purpose} image={connection?.icon ?? undefined}>
|
|
479
|
+
{url !== null ? (
|
|
480
|
+
<Button tone="primary" href={url}>
|
|
481
|
+
{messages.connect.authorize(title)}
|
|
482
|
+
</Button>
|
|
483
|
+
) : (
|
|
484
|
+
<p
|
|
485
|
+
className={part(
|
|
486
|
+
"sc-surface__connect",
|
|
487
|
+
...(working ? (["sc-surface__connect--done"] as const) : []),
|
|
488
|
+
)}
|
|
489
|
+
>
|
|
490
|
+
{working && <CheckIcon aria-hidden weight="bold" />}
|
|
491
|
+
{connectWords(messages.connect, working, failed)}
|
|
492
|
+
</p>
|
|
493
|
+
)}
|
|
494
|
+
</Card>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** The quiet word an app's card says where its button isn't: working, failed, or on its way. */
|
|
499
|
+
function connectWords(words: KitMessages["connect"], working: boolean, failed: boolean): string {
|
|
500
|
+
if (working) return words.connected;
|
|
501
|
+
return failed ? words.failed : words.preparing;
|
|
502
|
+
}
|
|
503
|
+
|
|
455
504
|
/**
|
|
456
505
|
* A button that sends its action, marked once it was the one pressed; or a link that opens its
|
|
457
506
|
* page apart (it stays usable on an older turn: it changes nothing here).
|
package/src/surface/scope.ts
CHANGED
|
@@ -28,7 +28,9 @@ export type SurfacePart =
|
|
|
28
28
|
| "sc-surface__form"
|
|
29
29
|
| "sc-surface__submit"
|
|
30
30
|
| "sc-surface__foot"
|
|
31
|
-
| "sc-surface__sent"
|
|
31
|
+
| "sc-surface__sent"
|
|
32
|
+
| "sc-surface__connect"
|
|
33
|
+
| "sc-surface__connect--done";
|
|
32
34
|
|
|
33
35
|
export type SurfaceClassNames = Partial<Record<SurfacePart | KitPart, string>> | undefined;
|
|
34
36
|
|
package/src/surface/values.ts
CHANGED
|
@@ -63,7 +63,7 @@ export const FEW_OPTIONS = 5;
|
|
|
63
63
|
/** Up to this many, a multiselect is chips; more go in a list to search. */
|
|
64
64
|
export const FEW_CHIPS = 8;
|
|
65
65
|
/** What a secret reads as, anywhere it is shown. */
|
|
66
|
-
|
|
66
|
+
const MASK = "••••••••";
|
|
67
67
|
/** The longest answer label the API takes. */
|
|
68
68
|
const LABEL_MAX = 2000;
|
|
69
69
|
|
|
@@ -139,7 +139,7 @@ export function startValues(fields: FormField[], answered?: Record<string, unkno
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/** A field with nothing given: unticked, none picked, a slider at its least, else nothing. */
|
|
142
|
-
|
|
142
|
+
function emptyOf(field: FormField): FieldValue {
|
|
143
143
|
switch (field.kind) {
|
|
144
144
|
case "checkbox":
|
|
145
145
|
return false;
|
|
@@ -154,7 +154,7 @@ export function emptyOf(field: FormField): FieldValue {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
/** A value read as the field's kind holds it, or null when it does not fit. */
|
|
157
|
-
|
|
157
|
+
function fit(field: FormField, raw: unknown): FieldValue {
|
|
158
158
|
switch (field.kind) {
|
|
159
159
|
case "checkbox":
|
|
160
160
|
return raw === true || raw === "true"
|
|
@@ -310,7 +310,7 @@ export function problemOf(
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
/** Whether nothing was given: no words, no pick, an unticked box, no stars. */
|
|
313
|
-
|
|
313
|
+
function isEmpty(field: FormField, value: FieldValue): boolean {
|
|
314
314
|
if (value === null) return true;
|
|
315
315
|
if (field.kind === "checkbox") return value !== true;
|
|
316
316
|
if (typeof value === "string") return value.trim() === "";
|
|
@@ -352,7 +352,7 @@ function outOfBounds(
|
|
|
352
352
|
return null;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
function optionOf(field: FormField, id: string): Option | undefined {
|
|
356
356
|
return field.options?.find((option) => option.id === id);
|
|
357
357
|
}
|
|
358
358
|
|
package/src/tint.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type AgentTint = "sky" | "rose" | "sand";
|
|
|
4
4
|
const TINTS: readonly AgentTint[] = ["sky", "rose", "sand"];
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* The tint an agent keeps wherever it appears (
|
|
7
|
+
* The tint an agent keeps wherever it appears (the agents' day, its profile, its card): picked from
|
|
8
8
|
* its id, so a rename changes nothing, and spread evenly over the three.
|
|
9
9
|
*/
|
|
10
10
|
export function agentTint(id: string): AgentTint {
|
package/src/types.ts
CHANGED
|
@@ -253,6 +253,14 @@ export interface Step {
|
|
|
253
253
|
awaits: boolean;
|
|
254
254
|
/** Set while it waits to be allowed: by the person (the turn stopped), or by the business. */
|
|
255
255
|
request: Request | null;
|
|
256
|
+
/** A connected app's action: the app, its logo an https address; absent for the platform's. */
|
|
257
|
+
app?: StepApp | null | undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The connected app a step's action belongs to: its name and logo. */
|
|
261
|
+
export interface StepApp {
|
|
262
|
+
name: string;
|
|
263
|
+
icon: string | null;
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
/**
|
package/src/usePolled.ts
CHANGED
|
@@ -3,14 +3,14 @@ import { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { ApiError } from "./client";
|
|
4
4
|
|
|
5
5
|
/** What a read gave: the value once it came, why it failed, or that it is no longer there. */
|
|
6
|
-
|
|
6
|
+
interface Polled<T> {
|
|
7
7
|
value: T | null;
|
|
8
8
|
problem: unknown;
|
|
9
9
|
gone: boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/** How often something still moving (a run under way) is read again. */
|
|
13
|
-
|
|
13
|
+
const POLL_MS = 3000;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Reads `load` when `key` changes, and again every `POLL_MS` while `moving` says what it gave is
|
package/src/voice/webgl.ts
CHANGED
|
@@ -77,7 +77,7 @@ export function inkOver(color: Rgb, surface: Rgb): Rgb {
|
|
|
77
77
|
let swatch: CanvasRenderingContext2D | null | undefined;
|
|
78
78
|
|
|
79
79
|
/** A CSS colour as red, green, blue and alpha, each 0 to 1; null where it can't be read. */
|
|
80
|
-
|
|
80
|
+
function rgbaOf(css: string): readonly [number, number, number, number] | null {
|
|
81
81
|
if (swatch === undefined) {
|
|
82
82
|
swatch = document.createElement("canvas").getContext("2d", { willReadFrequently: true });
|
|
83
83
|
}
|