rift-ds 1.7.0 → 1.8.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 +28 -5
- package/bin/rift-ds.mjs +142 -29
- package/bin/templates/chat/.env.chat.example +21 -0
- package/bin/templates/chat/app/api/chat/feedback/route.ts +12 -0
- package/bin/templates/chat/app/api/chat/followups/route.ts +23 -0
- package/bin/templates/chat/app/api/chat/guardrails.ts +18 -0
- package/bin/templates/chat/app/api/chat/route.ts +31 -0
- package/bin/templates/chat/components/chat/ChatMount.tsx +52 -0
- package/bin/templates/chat/components/chat/models.ts +29 -0
- package/chat/AssistantTurn.d.ts +5 -0
- package/chat/AssistantTurn.js +82 -0
- package/chat/ChatAgentPane.css +56 -0
- package/chat/ChatAgentPane.d.ts +21 -0
- package/chat/ChatAgentPane.js +25 -0
- package/chat/ChatLauncher.d.ts +64 -0
- package/chat/ChatLauncher.js +347 -0
- package/chat/ChatProvider.d.ts +53 -0
- package/chat/ChatProvider.js +181 -0
- package/chat/ChatTabs.d.ts +13 -0
- package/chat/ChatTabs.js +24 -0
- package/chat/ChatThreads.d.ts +26 -0
- package/chat/ChatThreads.js +55 -0
- package/chat/ChatWidget.css +1072 -0
- package/chat/ChatWidget.d.ts +113 -0
- package/chat/ChatWidget.js +495 -0
- package/chat/DemoWelcome.d.ts +13 -0
- package/chat/DemoWelcome.js +131 -0
- package/chat/FollowupSuggestions.d.ts +23 -0
- package/chat/FollowupSuggestions.js +34 -0
- package/chat/ResponseActions.d.ts +18 -0
- package/chat/ResponseActions.js +79 -0
- package/chat/TurnContent.d.ts +9 -0
- package/chat/TurnContent.js +12 -0
- package/chat/context.d.ts +65 -0
- package/chat/context.js +27 -0
- package/chat/demo-agent-panes.d.ts +5 -0
- package/chat/demo-agent-panes.js +49 -0
- package/chat/demo-agent.d.ts +3 -0
- package/chat/demo-agent.js +28 -0
- package/chat/demo-card-components.d.ts +46 -0
- package/chat/demo-card-components.js +59 -0
- package/chat/demo-cards.d.ts +32 -0
- package/chat/demo-cards.js +59 -0
- package/chat/demo-fixtures.d.ts +20 -0
- package/chat/demo-fixtures.js +113 -0
- package/chat/demo-script.d.ts +6 -0
- package/chat/demo-script.js +215 -0
- package/chat/demo.css +46 -0
- package/chat/demo.d.ts +9 -0
- package/chat/demo.js +24 -0
- package/chat/followups.d.ts +41 -0
- package/chat/followups.js +55 -0
- package/chat/greeting.d.ts +11 -0
- package/chat/greeting.js +21 -0
- package/chat/index.d.ts +34 -0
- package/chat/index.js +80 -0
- package/chat/placement.d.ts +48 -0
- package/chat/placement.js +39 -0
- package/chat/queries.d.ts +9 -0
- package/chat/queries.js +22 -0
- package/chat/registry.d.ts +25 -0
- package/chat/registry.js +9 -0
- package/chat/registry.json.d.ts +69 -0
- package/chat/registry.json.js +11 -0
- package/chat/server.d.ts +217 -0
- package/chat/server.js +488 -0
- package/chat/sim.d.ts +71 -0
- package/chat/sim.js +125 -0
- package/chat/starters.d.ts +15 -0
- package/chat/starters.js +38 -0
- package/chat/suggestions.d.ts +22 -0
- package/chat/suggestions.js +9 -0
- package/chat/threads.d.ts +73 -0
- package/chat/threads.js +270 -0
- package/chat/transport.d.ts +49 -0
- package/chat/transport.js +105 -0
- package/chat/types.d.ts +210 -0
- package/chat/types.js +16 -0
- package/chat/upstash.d.ts +63 -0
- package/chat/upstash.js +147 -0
- package/chat/useChat.d.ts +64 -0
- package/chat/useChat.js +241 -0
- package/cli/templates/chat/app/api/chat/feedback/route.d.ts +3 -0
- package/cli/templates/chat/app/api/chat/followups/route.d.ts +3 -0
- package/cli/templates/chat/app/api/chat/guardrails.d.ts +10 -0
- package/cli/templates/chat/app/api/chat/route.d.ts +4 -0
- package/cli/templates/chat/components/chat/ChatMount.d.ts +1 -0
- package/cli/templates/chat/components/chat/models.d.ts +11 -0
- package/components/ShaderField/ShaderField.d.ts +3 -2
- package/components/ShaderField/useShaderField.d.ts +2 -1
- package/components/ShaderField/useShaderField.js +6 -6
- package/fonts/material-symbols.css +2 -1
- package/package.json +39 -2
- package/tokens/presets/coral.css +18 -2
- package/tokens/presets/forest.css +18 -2
- package/tokens/presets/gold.css +18 -2
- package/tokens/presets/mono.css +18 -2
- package/tokens/presets/pink.css +18 -2
- package/tokens/presets/terminal.css +18 -2
- package/tokens/presets/violet.css +18 -2
- package/tokens/presets/warm.css +18 -2
- package/tokens/presets/zest.css +18 -2
- package/tokens/registry.json +6 -0
- package/tokens/registry.json.d.ts +6 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +10 -0
- package/tokens/tokens-light.css +10 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { AgentPlan } from "../components/AgentPlan/AgentPlan.js";
|
|
4
|
+
import { Button } from "../components/Button/Button.js";
|
|
5
|
+
import { CodeDiff } from "../components/CodeDiff/CodeDiff.js";
|
|
6
|
+
import { MessageCard } from "../components/MessageCard/MessageCard.js";
|
|
7
|
+
import { SourceChip } from "../components/SourceChip/SourceChip.js";
|
|
8
|
+
import { DemoStack, DemoInterrupt, DemoToolCall, DemoChart } from "./demo-card-components.js";
|
|
9
|
+
import "./demo.css";
|
|
10
|
+
const TONES = ["one", "two", "three"];
|
|
11
|
+
const DEMO_RENDERERS = {
|
|
12
|
+
"product-card": (data) => {
|
|
13
|
+
const card = data;
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
MessageCard,
|
|
16
|
+
{
|
|
17
|
+
media: /* @__PURE__ */ jsx("div", { className: "ds-chat-demo__card-media", "aria-hidden": "true" }),
|
|
18
|
+
title: card.title,
|
|
19
|
+
description: card.description,
|
|
20
|
+
meta: card.meta,
|
|
21
|
+
actions: card.actions?.map((action) => /* @__PURE__ */ jsx(Button, { label: action.label, variant: action.variant, size: "compact" }, action.label))
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
},
|
|
25
|
+
chart: (data) => /* @__PURE__ */ jsx(DemoChart, { card: data }),
|
|
26
|
+
sources: (data) => {
|
|
27
|
+
const card = data;
|
|
28
|
+
return /* @__PURE__ */ jsx("div", { className: "ds-chat-demo__source-row", children: card.items.map((item, index) => /* @__PURE__ */ jsx(
|
|
29
|
+
SourceChip,
|
|
30
|
+
{
|
|
31
|
+
title: item.title,
|
|
32
|
+
index: item.index,
|
|
33
|
+
logo: item.letter ? /* @__PURE__ */ jsx(
|
|
34
|
+
"span",
|
|
35
|
+
{
|
|
36
|
+
className: `ds-chat-demo__logo ds-chat-demo__logo--${TONES[index % TONES.length]}`,
|
|
37
|
+
"aria-hidden": "true",
|
|
38
|
+
children: item.letter
|
|
39
|
+
}
|
|
40
|
+
) : void 0,
|
|
41
|
+
source: item.source,
|
|
42
|
+
meta: item.meta,
|
|
43
|
+
excerpt: item.excerpt
|
|
44
|
+
},
|
|
45
|
+
item.title
|
|
46
|
+
)) });
|
|
47
|
+
},
|
|
48
|
+
"tool-call": (data) => /* @__PURE__ */ jsx(DemoToolCall, { card: data }),
|
|
49
|
+
diff: (data) => {
|
|
50
|
+
const card = data;
|
|
51
|
+
return /* @__PURE__ */ jsx(CodeDiff, { diff: card.diff, filename: card.filename });
|
|
52
|
+
},
|
|
53
|
+
plan: (data) => /* @__PURE__ */ jsx(AgentPlan, { steps: data.steps }),
|
|
54
|
+
interrupt: (data) => /* @__PURE__ */ jsx(DemoInterrupt, { card: data }),
|
|
55
|
+
stack: (data) => /* @__PURE__ */ jsx(DemoStack, { card: data, renderers: DEMO_RENDERERS })
|
|
56
|
+
};
|
|
57
|
+
export {
|
|
58
|
+
DEMO_RENDERERS
|
|
59
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MentionSource } from '../components/Mention/Mention';
|
|
2
|
+
import { ModelPickerModel } from '../components/ModelPicker/ModelPicker';
|
|
3
|
+
import { ThreadPanelGroup, ThreadPanelProject } from '../components/ThreadPanel/ThreadPanel';
|
|
4
|
+
import { ThreadsOptions } from './threads';
|
|
5
|
+
/** `@` reaches for a person or a file, `/` for a skill. */
|
|
6
|
+
export declare const DEMO_MENTIONS: MentionSource[];
|
|
7
|
+
/** A mock model list for the picker: set dressing, nothing routes on it. */
|
|
8
|
+
export declare const DEMO_MODELS: ModelPickerModel[];
|
|
9
|
+
export declare const DEMO_THREAD_GROUPS: ThreadPanelGroup[];
|
|
10
|
+
export declare const DEMO_PROJECTS: ThreadPanelProject[];
|
|
11
|
+
/** The names the demo's "title generation" hands out, in order. */
|
|
12
|
+
export declare const DEMO_THREAD_NAMES: string[];
|
|
13
|
+
/** Ready-made options for `ChatProvider`'s `threads`. */
|
|
14
|
+
export declare const DEMO_THREADS: ThreadsOptions;
|
|
15
|
+
/** What sits under the composer on the demo's welcome screen. */
|
|
16
|
+
export type DemoWelcomeVariant = 'starters' | 'links' | 'cards' | 'dashboard' | 'none';
|
|
17
|
+
export declare const DEMO_WELCOME_OPTIONS: {
|
|
18
|
+
value: DemoWelcomeVariant;
|
|
19
|
+
label: string;
|
|
20
|
+
}[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const DEMO_MENTIONS = [
|
|
2
|
+
{
|
|
3
|
+
trigger: "@",
|
|
4
|
+
label: "People and files",
|
|
5
|
+
items: [
|
|
6
|
+
{ id: "mira", label: "Mira Castellan", description: "Design lead", icon: "person" },
|
|
7
|
+
{ id: "tobias", label: "Tobias Renner", description: "Platform", icon: "person" },
|
|
8
|
+
{ id: "ines", label: "Ines Okonjo", description: "Research", icon: "person" },
|
|
9
|
+
{ id: "roadmap", label: "roadmap.md", description: "Docs", icon: "description" },
|
|
10
|
+
{ id: "pricing", label: "pricing.csv", description: "Finance", icon: "description" },
|
|
11
|
+
{ id: "onboarding", label: "onboarding.pdf", description: "Docs", icon: "description" }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
trigger: "/",
|
|
16
|
+
label: "Skills",
|
|
17
|
+
items: [
|
|
18
|
+
{ id: "summarize", label: "summarize", description: "The thread so far, in brief" },
|
|
19
|
+
{ id: "draft", label: "draft", description: "Write a first version" },
|
|
20
|
+
{ id: "review", label: "review", description: "Read it for problems" },
|
|
21
|
+
{ id: "translate", label: "translate", description: "Into another language" },
|
|
22
|
+
{ id: "schedule", label: "schedule", description: "Run this later" },
|
|
23
|
+
{ id: "invite", label: "invite", description: "Add a teammate to the workspace" },
|
|
24
|
+
{ id: "export", label: "export", description: "Save the thread as a file" },
|
|
25
|
+
{ id: "status", label: "status", description: "Where things stand" },
|
|
26
|
+
{ id: "share", label: "share", description: "Send a link to this thread" },
|
|
27
|
+
{ id: "search", label: "search", description: "Look across the workspace" }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
const DEMO_MODELS = [
|
|
32
|
+
{ label: "Sonnet 5", value: "sonnet-5", description: "Fast and capable, the everyday pick.", badge: "New" },
|
|
33
|
+
{ label: "Fable 5", value: "fable-5", description: "The deepest reasoning for hard questions." },
|
|
34
|
+
{ label: "Haiku 4.5", value: "haiku-4-5", description: "Instant answers for quick lookups." }
|
|
35
|
+
];
|
|
36
|
+
const DEMO_THREAD_GROUPS = [
|
|
37
|
+
{
|
|
38
|
+
label: "Pinned",
|
|
39
|
+
threads: [
|
|
40
|
+
{
|
|
41
|
+
id: "pricing",
|
|
42
|
+
title: "Compare the plan limits",
|
|
43
|
+
description: "Annual pricing options",
|
|
44
|
+
status: "idle",
|
|
45
|
+
pinned: true,
|
|
46
|
+
meta: "2d"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: "This week",
|
|
52
|
+
threads: [
|
|
53
|
+
{
|
|
54
|
+
id: "launch",
|
|
55
|
+
title: "Plan the launch week",
|
|
56
|
+
description: "Waiting on the beta invite list",
|
|
57
|
+
status: "waiting",
|
|
58
|
+
icon: "cloud",
|
|
59
|
+
meta: "32m"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "onboarding",
|
|
63
|
+
title: "Rework the onboarding email",
|
|
64
|
+
description: "Drafting the welcome sequence",
|
|
65
|
+
status: "working",
|
|
66
|
+
icon: "cloud",
|
|
67
|
+
meta: "2h"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: "Earlier",
|
|
73
|
+
threads: [
|
|
74
|
+
{ id: "import", title: "Import last year's invoices", meta: "4d" },
|
|
75
|
+
{ id: "roles", title: "Set up roles for the team", status: "error", meta: "5d" },
|
|
76
|
+
{ id: "notify", title: "Quiet the mention notifications", status: "unread", meta: "1w" }
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
const DEMO_PROJECTS = [
|
|
81
|
+
{ id: "workspace-revamp", label: "Workspace revamp", meta: "1m" },
|
|
82
|
+
{ id: "billing-cleanup", label: "Billing cleanup", meta: "3d" }
|
|
83
|
+
];
|
|
84
|
+
const DEMO_THREAD_NAMES = [
|
|
85
|
+
"Plan the beta invite list",
|
|
86
|
+
"Summarize the weekly standup",
|
|
87
|
+
"Draft the pricing FAQ",
|
|
88
|
+
"Tidy up the workspace roles",
|
|
89
|
+
"Sketch the renewal email"
|
|
90
|
+
];
|
|
91
|
+
const DEMO_THREADS = {
|
|
92
|
+
groups: DEMO_THREAD_GROUPS,
|
|
93
|
+
projects: DEMO_PROJECTS,
|
|
94
|
+
pinnedGroup: "Pinned",
|
|
95
|
+
homeGroup: "This week",
|
|
96
|
+
nameThread: (index) => DEMO_THREAD_NAMES[index % DEMO_THREAD_NAMES.length]
|
|
97
|
+
};
|
|
98
|
+
const DEMO_WELCOME_OPTIONS = [
|
|
99
|
+
{ value: "starters", label: "Starters" },
|
|
100
|
+
{ value: "links", label: "Links" },
|
|
101
|
+
{ value: "cards", label: "Cards" },
|
|
102
|
+
{ value: "dashboard", label: "Dashboard" },
|
|
103
|
+
{ value: "none", label: "None" }
|
|
104
|
+
];
|
|
105
|
+
export {
|
|
106
|
+
DEMO_MENTIONS,
|
|
107
|
+
DEMO_MODELS,
|
|
108
|
+
DEMO_PROJECTS,
|
|
109
|
+
DEMO_THREADS,
|
|
110
|
+
DEMO_THREAD_GROUPS,
|
|
111
|
+
DEMO_THREAD_NAMES,
|
|
112
|
+
DEMO_WELCOME_OPTIONS
|
|
113
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ChatScript } from './sim';
|
|
2
|
+
import { Starter } from './types';
|
|
3
|
+
/** The three starters the demo welcome screen offers. */
|
|
4
|
+
export declare const DEMO_STARTERS: Starter[];
|
|
5
|
+
/** The scripted conversation, for `createSimTransport` or inspection. */
|
|
6
|
+
export declare const DEMO_SCRIPT: ChatScript;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { scriptChat } from "./sim.js";
|
|
2
|
+
const TRIAL_SIGNUPS = [
|
|
3
|
+
{ week: "W1", signups: 320 },
|
|
4
|
+
{ week: "W2", signups: 410 },
|
|
5
|
+
{ week: "W3", signups: 380 },
|
|
6
|
+
{ week: "W4", signups: 520 },
|
|
7
|
+
{ week: "W5", signups: 610 },
|
|
8
|
+
{ week: "W6", signups: 720 }
|
|
9
|
+
];
|
|
10
|
+
const LAUNCH_BUDGET = [
|
|
11
|
+
{ phase: "Beta", spend: 12 },
|
|
12
|
+
{ phase: "Launch week", spend: 34 },
|
|
13
|
+
{ phase: "Follow-up", spend: 18 }
|
|
14
|
+
];
|
|
15
|
+
const PLAN_STEPS = [
|
|
16
|
+
{ label: "Check the workspace quota", status: "completed" },
|
|
17
|
+
{ label: "Create the shared project", status: "completed", detail: "acme-design / launch" },
|
|
18
|
+
{ label: "Invite the three teammates", status: "active" },
|
|
19
|
+
{ label: "Post the welcome note", status: "pending" }
|
|
20
|
+
];
|
|
21
|
+
const card = (kind, data) => ({ kind, data });
|
|
22
|
+
const DEMO_STARTERS = [
|
|
23
|
+
{ id: "start", label: "How do I get started?" },
|
|
24
|
+
{ id: "plans", label: "What do the plans include?" },
|
|
25
|
+
{ id: "invite", label: "Invite my team to a workspace" }
|
|
26
|
+
];
|
|
27
|
+
const DEMO_SCRIPT = scriptChat().user("How do I get started?").assistant(
|
|
28
|
+
"Create a workspace from the dashboard, then invite your team from Settings. Everyone you add sees the shared projects the moment they join. If you would rather not click through it, I can set the whole thing up for you.",
|
|
29
|
+
{
|
|
30
|
+
steps: [
|
|
31
|
+
{ status: "Reading the first-run flow", point: "The question maps to the first-run flow." },
|
|
32
|
+
{ status: "Drafting the answer", point: "Lead with the workspace, then the invite step." }
|
|
33
|
+
],
|
|
34
|
+
followups: ["Set it all up for me", "What do the plans include?", "How is the trial going?"]
|
|
35
|
+
}
|
|
36
|
+
).user("Set it all up for me").assistant("On it. Two steps are done already, and the invites are going out now.", {
|
|
37
|
+
steps: [
|
|
38
|
+
{ status: "Planning the setup", point: "Four steps, from quota check to welcome note." },
|
|
39
|
+
{ status: "Creating the workspace", point: "The shared project comes first." }
|
|
40
|
+
],
|
|
41
|
+
card: card("plan", { steps: PLAN_STEPS }),
|
|
42
|
+
followups: ["Find a time we can all meet", "Take us up to twelve seats", "How is the trial going?"]
|
|
43
|
+
}).user("What do the plans include?").assistant(
|
|
44
|
+
"Every plan includes unlimited projects. The Team plan adds shared workspaces, guest seats, and a 90 day version history.",
|
|
45
|
+
{
|
|
46
|
+
steps: [
|
|
47
|
+
{ status: "Comparing the plans", point: "Three plans differ on collaboration, not on limits." },
|
|
48
|
+
{ status: "Drafting the answer", point: "Name what the Team plan adds over the base." }
|
|
49
|
+
],
|
|
50
|
+
followups: ["Which plan fits a team of five?", "Where is that written down?", "How is the trial going?"]
|
|
51
|
+
}
|
|
52
|
+
).user("Which plan fits a team of five?").assistant("The Team plan fits what you described: shared workspaces for the whole team, and guest seats stay free.", {
|
|
53
|
+
steps: [
|
|
54
|
+
{ status: "Matching plans to the team", point: "The visitor needs shared workspaces and guest seats." },
|
|
55
|
+
{ status: "Drafting the answer", point: "The Team plan covers both; lead with the card." }
|
|
56
|
+
],
|
|
57
|
+
card: card("product-card", {
|
|
58
|
+
title: "Team plan",
|
|
59
|
+
description: "Shared workspaces, guest seats, and version history for the whole team.",
|
|
60
|
+
meta: "Billed monthly, cancel any time",
|
|
61
|
+
actions: [
|
|
62
|
+
{ label: "Choose plan", variant: "secondary" },
|
|
63
|
+
{ label: "Compare plans", variant: "tertiary" }
|
|
64
|
+
]
|
|
65
|
+
}),
|
|
66
|
+
followups: ["Where is that written down?", "Invite my team to a workspace", "How is the trial going?"]
|
|
67
|
+
}).user("Where is that written down?").assistant("Each chip opens the page the answer came from, so nothing here needs taking on trust.", {
|
|
68
|
+
steps: [
|
|
69
|
+
{ status: "Collecting the sources", point: "Three product pages back the answer." },
|
|
70
|
+
{ status: "Drafting the answer", point: "Cite them so the visitor can check." }
|
|
71
|
+
],
|
|
72
|
+
card: card("sources", {
|
|
73
|
+
items: [
|
|
74
|
+
{
|
|
75
|
+
title: "Pricing guide",
|
|
76
|
+
index: 1,
|
|
77
|
+
letter: "A",
|
|
78
|
+
source: "acme.design/pricing",
|
|
79
|
+
meta: "Updated 3 days ago",
|
|
80
|
+
excerpt: "Team is billed per editor, monthly or yearly. Viewers are free on every plan, and a seat you remove mid-cycle comes back as credit on the next invoice."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
title: "Workspace docs",
|
|
84
|
+
index: 2,
|
|
85
|
+
letter: "D",
|
|
86
|
+
source: "docs.acme.design",
|
|
87
|
+
excerpt: "An editor can create and change files in any project they are invited to. Seats belong to the workspace, not to a project, so moving someone between teams costs nothing."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
title: "Fair use policy",
|
|
91
|
+
source: "acme.design/legal",
|
|
92
|
+
meta: "4 min read",
|
|
93
|
+
excerpt: "Unlimited means no hard cap on files or version history. Automated bulk uploads above the published rate are queued, never dropped."
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}),
|
|
97
|
+
followups: ["Which plan fits a team of five?", "How do I get started?", "How is the trial going?"]
|
|
98
|
+
}).user("How is the trial going?").assistant("Signups have climbed for five of the six weeks since the trial opened, and last week was the strongest yet.", {
|
|
99
|
+
steps: [
|
|
100
|
+
{ status: "Pulling the signup numbers", point: "Six weeks of signup data since the trial opened." },
|
|
101
|
+
{ status: "Charting the trend", point: "One series tells it; chart it in the card." }
|
|
102
|
+
],
|
|
103
|
+
card: card("chart", {
|
|
104
|
+
type: "area",
|
|
105
|
+
title: "Trial signups",
|
|
106
|
+
meta: "Last six weeks",
|
|
107
|
+
data: TRIAL_SIGNUPS,
|
|
108
|
+
xKey: "week",
|
|
109
|
+
yKey: "signups",
|
|
110
|
+
label: "Signups"
|
|
111
|
+
}),
|
|
112
|
+
followups: ["Which plan fits a team of five?", "Invite my team to a workspace", "Where is that written down?"]
|
|
113
|
+
}).user("Invite my team to a workspace").assistant("Happy to. Give me the word and I will send invites to the three teammates on your account, all as editors.", {
|
|
114
|
+
steps: [
|
|
115
|
+
{ status: "Checking the account", point: "Three teammates sit on the account, none invited yet." },
|
|
116
|
+
{ status: "Drafting the answer", point: "Confirm before sending anything." }
|
|
117
|
+
],
|
|
118
|
+
followups: ["Go ahead and invite the other three", "Take us up to twelve seats", "Find a time we can all meet"]
|
|
119
|
+
}).user("Go ahead and invite the other three").assistant("All three invites are out. Everyone lands in the workspace with editor access.", {
|
|
120
|
+
steps: [
|
|
121
|
+
{ status: "Sending the invites", point: "Three teammates to invite, all editors." },
|
|
122
|
+
{ status: "Confirming delivery", point: "One call covers the batch." }
|
|
123
|
+
],
|
|
124
|
+
card: card("tool-call", {
|
|
125
|
+
name: "invite_teammates",
|
|
126
|
+
summary: "3 editors to acme-design",
|
|
127
|
+
status: "success",
|
|
128
|
+
duration: "0.6s",
|
|
129
|
+
args: '{\n "workspace": "acme-design",\n "role": "editor",\n "emails": 3\n}'
|
|
130
|
+
}),
|
|
131
|
+
followups: ["Find a time we can all meet", "Take us up to twelve seats", "How is the trial going?"]
|
|
132
|
+
}).user("Take us up to twelve seats").assistant("Only the seat count moves, from five to twelve. Everything else in the config stays as it was.", {
|
|
133
|
+
steps: [
|
|
134
|
+
{ status: "Locating the seat limit", point: "The seat limit lives in workspace.config.ts." },
|
|
135
|
+
{ status: "Preparing the change", point: "One line changes; show the diff." }
|
|
136
|
+
],
|
|
137
|
+
card: card("diff", {
|
|
138
|
+
filename: "workspace.config.ts",
|
|
139
|
+
diff: '@@ -12,7 +12,7 @@\n export const workspace = {\n name: "acme-design",\n- seats: 5,\n+ seats: 12,\n region: "eu-west",\n };'
|
|
140
|
+
}),
|
|
141
|
+
followups: ["Go ahead and invite the other three", "What do the plans include?", "Find a time we can all meet"]
|
|
142
|
+
}).user("Here is the launch plan we are working from.").assistant(
|
|
143
|
+
"Three phases, and most of the budget sits behind launch week itself. The dates line up with the workspace you already set up, so nothing needs to move.",
|
|
144
|
+
{
|
|
145
|
+
steps: [
|
|
146
|
+
{ status: "Reading the document", point: "Fourteen pages; read it before answering." },
|
|
147
|
+
{ status: "Charting the budget", point: "Budget is the decision in the doc; chart it." }
|
|
148
|
+
],
|
|
149
|
+
card: card("stack", {
|
|
150
|
+
items: [
|
|
151
|
+
card("tool-call", {
|
|
152
|
+
name: "read_document",
|
|
153
|
+
summary: "Q3 launch plan.pdf",
|
|
154
|
+
status: "success",
|
|
155
|
+
duration: "1.8s"
|
|
156
|
+
}),
|
|
157
|
+
card("chart", {
|
|
158
|
+
type: "bar",
|
|
159
|
+
title: "Budget by phase",
|
|
160
|
+
meta: "From Q3 launch plan.pdf",
|
|
161
|
+
data: LAUNCH_BUDGET,
|
|
162
|
+
xKey: "phase",
|
|
163
|
+
yKey: "spend",
|
|
164
|
+
label: "Spend ($k)"
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
}),
|
|
168
|
+
followups: ["Set it all up for me", "Take us up to twelve seats", "Find a time we can all meet"]
|
|
169
|
+
}
|
|
170
|
+
).user("Sync the shared calendar").assistant("The sync timed out on the provider's side. Retry below and it should land.", {
|
|
171
|
+
steps: [{ status: "Starting the calendar sync", point: "The calendar sync runs after the invites." }],
|
|
172
|
+
card: card("tool-call", {
|
|
173
|
+
name: "sync_calendar",
|
|
174
|
+
summary: "the shared team calendar",
|
|
175
|
+
status: "error",
|
|
176
|
+
duration: "2.1s",
|
|
177
|
+
retryable: true
|
|
178
|
+
}),
|
|
179
|
+
followups: ["Find a time we can all meet", "Go ahead and invite the other three", "How is the trial going?"]
|
|
180
|
+
}).user("Find a time we can all meet").assistant("I can check everyone's calendar for a slot, once you allow it below.", {
|
|
181
|
+
steps: [
|
|
182
|
+
{ status: "Checking what this needs", point: "Suggesting times needs calendar access." },
|
|
183
|
+
{ status: "Preparing the request", point: "Ask before connecting anything." }
|
|
184
|
+
],
|
|
185
|
+
card: card("interrupt", {
|
|
186
|
+
title: "Connect the shared calendar?",
|
|
187
|
+
description: "The assistant wants read access to the team calendar to suggest meeting times.",
|
|
188
|
+
options: [
|
|
189
|
+
{ value: "allow", label: "Allow once", variant: "primary" },
|
|
190
|
+
{ value: "always", label: "Always allow", variant: "secondary" },
|
|
191
|
+
{ value: "deny", label: "Not now", variant: "secondary" }
|
|
192
|
+
]
|
|
193
|
+
}),
|
|
194
|
+
followups: ["Go ahead and invite the other three", "How is the trial going?", "How do I get started?"]
|
|
195
|
+
}).fallback(
|
|
196
|
+
"The launch is on track overall. Three of the four readiness tracks are green, and the one item I would watch is the **legal review**, since it gates the pricing rollout.",
|
|
197
|
+
{
|
|
198
|
+
steps: [
|
|
199
|
+
{ status: "Reading the readiness tracks", point: "Pull the four readiness tracks and their owners." },
|
|
200
|
+
{ status: "Reviewing the legal blocker", point: "Pricing copy is still sitting in legal review." },
|
|
201
|
+
{ status: "Drafting the answer", point: "Lead with the overall status, then flag the one real risk." }
|
|
202
|
+
],
|
|
203
|
+
followups: ["Which track is behind?", "Who owns the legal review?", "What happens if pricing slips?"]
|
|
204
|
+
}
|
|
205
|
+
).fallback(
|
|
206
|
+
"This chat is a scripted preview of the widget, so free text gets one of a few written replies. The suggestion chips lead through the parts of the story that show off the rich content: a plan, a chart, a diff, a tool call and an approval.",
|
|
207
|
+
{
|
|
208
|
+
steps: [{ status: "Reading the question", point: "Nothing scripted matches this one." }],
|
|
209
|
+
followups: ["How do I get started?", "What do the plans include?", "How is the trial going?"]
|
|
210
|
+
}
|
|
211
|
+
).build();
|
|
212
|
+
export {
|
|
213
|
+
DEMO_SCRIPT,
|
|
214
|
+
DEMO_STARTERS
|
|
215
|
+
};
|
package/chat/demo.css
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* The demo's card furniture: a drawn stand-in for product imagery, a row of
|
|
2
|
+
source chips, a stack of two cards landing as one turn, and the lettered
|
|
3
|
+
disc a source chip wears for a logo. Chart-series tokens keep it on-theme
|
|
4
|
+
in both modes and re-theme with the rest of a theme like every chart. */
|
|
5
|
+
|
|
6
|
+
.ds-chat-demo__card-media {
|
|
7
|
+
height: 120px;
|
|
8
|
+
background: linear-gradient(135deg, var(--color-chart-series-1), var(--color-chart-series-2));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ds-chat-demo__source-row {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
gap: var(--gap-200);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.ds-chat-demo__stack {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--gap-300);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ds-chat-demo__logo {
|
|
24
|
+
display: inline-grid;
|
|
25
|
+
place-items: center;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 100%;
|
|
28
|
+
border-radius: var(--radius-pill);
|
|
29
|
+
font-family: var(--font-caption-family);
|
|
30
|
+
font-size: var(--font-caption-size);
|
|
31
|
+
font-weight: var(--font-title-body-weight);
|
|
32
|
+
line-height: 1;
|
|
33
|
+
color: var(--color-text-inverse);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ds-chat-demo__logo--one {
|
|
37
|
+
background-color: var(--color-chart-series-1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ds-chat-demo__logo--two {
|
|
41
|
+
background-color: var(--color-chart-series-2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ds-chat-demo__logo--three {
|
|
45
|
+
background-color: var(--color-chart-series-3);
|
|
46
|
+
}
|
package/chat/demo.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SimTiming } from './sim';
|
|
2
|
+
import { ChatTransport } from './types';
|
|
3
|
+
export { DEMO_RENDERERS, type DemoProductCard, type DemoChartCard, type DemoSourcesCard, type DemoToolCallCard, type DemoDiffCard, type DemoPlanCard, type DemoInterruptCard, type DemoStackCard, } from './demo-cards';
|
|
4
|
+
export { DEMO_SCRIPT, DEMO_STARTERS } from './demo-script';
|
|
5
|
+
export { DEMO_MENTIONS, DEMO_MODELS, DEMO_THREAD_GROUPS, DEMO_PROJECTS, DEMO_THREAD_NAMES, DEMO_THREADS, DEMO_WELCOME_OPTIONS, type DemoWelcomeVariant, } from './demo-fixtures';
|
|
6
|
+
export { demoAgentTabs } from './demo-agent';
|
|
7
|
+
export { DemoBoard, DemoWelcome } from './DemoWelcome';
|
|
8
|
+
/** The demo's transport: the script, played at the default beats unless told otherwise. */
|
|
9
|
+
export declare const createDemoTransport: (timing?: Partial<SimTiming>) => ChatTransport;
|
package/chat/demo.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createSimTransport } from "./sim.js";
|
|
2
|
+
import { DEMO_SCRIPT } from "./demo-script.js";
|
|
3
|
+
import { DEMO_STARTERS } from "./demo-script.js";
|
|
4
|
+
import { DEMO_RENDERERS } from "./demo-cards.js";
|
|
5
|
+
import { DEMO_MENTIONS, DEMO_MODELS, DEMO_PROJECTS, DEMO_THREADS, DEMO_THREAD_GROUPS, DEMO_THREAD_NAMES, DEMO_WELCOME_OPTIONS } from "./demo-fixtures.js";
|
|
6
|
+
import { demoAgentTabs } from "./demo-agent.js";
|
|
7
|
+
import { DemoBoard, DemoWelcome } from "./DemoWelcome.js";
|
|
8
|
+
const createDemoTransport = (timing) => createSimTransport(DEMO_SCRIPT, timing);
|
|
9
|
+
export {
|
|
10
|
+
DEMO_MENTIONS,
|
|
11
|
+
DEMO_MODELS,
|
|
12
|
+
DEMO_PROJECTS,
|
|
13
|
+
DEMO_RENDERERS,
|
|
14
|
+
DEMO_SCRIPT,
|
|
15
|
+
DEMO_STARTERS,
|
|
16
|
+
DEMO_THREADS,
|
|
17
|
+
DEMO_THREAD_GROUPS,
|
|
18
|
+
DEMO_THREAD_NAMES,
|
|
19
|
+
DEMO_WELCOME_OPTIONS,
|
|
20
|
+
DemoBoard,
|
|
21
|
+
DemoWelcome,
|
|
22
|
+
createDemoTransport,
|
|
23
|
+
demoAgentTabs
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ChatTurn } from './types';
|
|
2
|
+
/** Chips offered under one answer. */
|
|
3
|
+
export declare const MAX_FOLLOWUPS = 3;
|
|
4
|
+
/**
|
|
5
|
+
* The written fallbacks a host that passes none gets: generic by design, so
|
|
6
|
+
* any of them makes sense under any answer. A host whose chat is about one
|
|
7
|
+
* product writes its own pool and passes it to the provider.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_FALLBACK_FOLLOWUPS: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* Three questions from a fallback pool, rotated by the turn id (stable
|
|
12
|
+
* across re-renders) and skipping one identical to what the visitor just
|
|
13
|
+
* asked: offering their own question back would read as not listening.
|
|
14
|
+
*/
|
|
15
|
+
export declare function pickFallbackFollowups(pool: readonly string[], turnId: string, question: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Suggestions have no value once the visitor has moved on, and the widget
|
|
18
|
+
* never blocks on them, so a slow one is simply dropped.
|
|
19
|
+
*/
|
|
20
|
+
export declare const FOLLOWUPS_TIMEOUT_MS = 10000;
|
|
21
|
+
/** A source of follow-up questions for a finished answer. */
|
|
22
|
+
export type FollowupsSource = (question: string, answer: string, signal: AbortSignal) => Promise<string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* A source that asks an HTTP endpoint speaking the follow-ups protocol
|
|
25
|
+
* (`createFollowupsHandler` on the server): POST `{ question, answer }`,
|
|
26
|
+
* read `{ suggestions: string[] }`. Resolves to an empty array on any
|
|
27
|
+
* failure, which the provider stores as-is: it records that the turn was
|
|
28
|
+
* asked about and had nothing to offer, then substitutes the fallbacks.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createFollowupsFetcher(endpoint?: string): FollowupsSource;
|
|
31
|
+
/**
|
|
32
|
+
* The turn to ask about, or null when there is nothing to ask about yet:
|
|
33
|
+
* the newest turn must be a committed assistant answer that came from a
|
|
34
|
+
* backend (an exchange id is the marker, as it is for the thumbs) and has
|
|
35
|
+
* not been asked about already.
|
|
36
|
+
*/
|
|
37
|
+
export declare function followupTarget(turns: ChatTurn[]): {
|
|
38
|
+
id: string;
|
|
39
|
+
question: string;
|
|
40
|
+
answer: string;
|
|
41
|
+
} | null;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { fitsChip } from "./suggestions.js";
|
|
2
|
+
const MAX_FOLLOWUPS = 3;
|
|
3
|
+
const DEFAULT_FALLBACK_FOLLOWUPS = [
|
|
4
|
+
"Can you say more about that?",
|
|
5
|
+
"What should I do next?",
|
|
6
|
+
"Where can I read more?",
|
|
7
|
+
"Can you give an example?",
|
|
8
|
+
"What are the alternatives?"
|
|
9
|
+
];
|
|
10
|
+
function pickFallbackFollowups(pool, turnId, question) {
|
|
11
|
+
const asked = question.trim().toLowerCase();
|
|
12
|
+
const usable = pool.filter((label) => label.toLowerCase() !== asked && fitsChip(label));
|
|
13
|
+
let hash = 0;
|
|
14
|
+
for (const char of turnId) hash = hash * 31 + char.charCodeAt(0) >>> 0;
|
|
15
|
+
return Array.from(
|
|
16
|
+
{ length: Math.min(MAX_FOLLOWUPS, usable.length) },
|
|
17
|
+
(_, index) => usable[(hash + index) % usable.length]
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const FOLLOWUPS_TIMEOUT_MS = 1e4;
|
|
21
|
+
function createFollowupsFetcher(endpoint = "/api/chat/followups") {
|
|
22
|
+
return async (question, answer, signal) => {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(endpoint, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({ question, answer }),
|
|
28
|
+
signal
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) return [];
|
|
31
|
+
const payload = await response.json();
|
|
32
|
+
const suggestions = payload?.suggestions;
|
|
33
|
+
if (!Array.isArray(suggestions)) return [];
|
|
34
|
+
return suggestions.filter((item) => typeof item === "string" && fitsChip(item)).slice(0, MAX_FOLLOWUPS);
|
|
35
|
+
} catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function followupTarget(turns) {
|
|
41
|
+
const last = turns[turns.length - 1];
|
|
42
|
+
if (!last || last.role !== "assistant") return null;
|
|
43
|
+
if (!last.exchangeId || last.followups || last.text === "") return null;
|
|
44
|
+
const question = turns[turns.length - 2];
|
|
45
|
+
if (!question || question.role !== "user") return null;
|
|
46
|
+
return { id: last.id, question: question.text, answer: last.text };
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
DEFAULT_FALLBACK_FOLLOWUPS,
|
|
50
|
+
FOLLOWUPS_TIMEOUT_MS,
|
|
51
|
+
MAX_FOLLOWUPS,
|
|
52
|
+
createFollowupsFetcher,
|
|
53
|
+
followupTarget,
|
|
54
|
+
pickFallbackFollowups
|
|
55
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The three greetings, overridable by a host that speaks differently. */
|
|
2
|
+
export interface GreetingCopy {
|
|
3
|
+
morning: string;
|
|
4
|
+
afternoon: string;
|
|
5
|
+
evening: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_GREETING_COPY: GreetingCopy;
|
|
8
|
+
export declare const greetingFor: (hour: number, copy?: GreetingCopy) => string;
|
|
9
|
+
export declare const subscribeClock: () => () => void;
|
|
10
|
+
export declare const readGreeting: () => string;
|
|
11
|
+
export declare const serverGreeting: () => string;
|
package/chat/greeting.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const DEFAULT_GREETING_COPY = {
|
|
2
|
+
morning: "Good morning",
|
|
3
|
+
afternoon: "Good afternoon",
|
|
4
|
+
evening: "Good evening"
|
|
5
|
+
};
|
|
6
|
+
const greetingFor = (hour, copy = DEFAULT_GREETING_COPY) => {
|
|
7
|
+
if (hour >= 5 && hour < 12) return copy.morning;
|
|
8
|
+
if (hour >= 12 && hour < 17) return copy.afternoon;
|
|
9
|
+
return copy.evening;
|
|
10
|
+
};
|
|
11
|
+
const subscribeClock = () => () => {
|
|
12
|
+
};
|
|
13
|
+
const readGreeting = () => greetingFor((/* @__PURE__ */ new Date()).getHours());
|
|
14
|
+
const serverGreeting = () => "";
|
|
15
|
+
export {
|
|
16
|
+
DEFAULT_GREETING_COPY,
|
|
17
|
+
greetingFor,
|
|
18
|
+
readGreeting,
|
|
19
|
+
serverGreeting,
|
|
20
|
+
subscribeClock
|
|
21
|
+
};
|