rift-ds 1.7.0 → 1.9.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 +31 -8
- 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 +24 -0
- package/chat/ChatAgentPane.js +28 -0
- package/chat/ChatArtifactCard.d.ts +13 -0
- package/chat/ChatArtifactCard.js +21 -0
- package/chat/ChatArtifactPane.d.ts +18 -0
- package/chat/ChatArtifactPane.js +43 -0
- package/chat/ChatLauncher.d.ts +64 -0
- package/chat/ChatLauncher.js +347 -0
- package/chat/ChatProvider.d.ts +55 -0
- package/chat/ChatProvider.js +196 -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 +1259 -0
- package/chat/ChatWidget.d.ts +115 -0
- package/chat/ChatWidget.js +709 -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 +11 -0
- package/chat/TurnContent.js +19 -0
- package/chat/artifacts.d.ts +5 -0
- package/chat/artifacts.js +14 -0
- package/chat/context.d.ts +82 -0
- package/chat/context.js +29 -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-artifacts.d.ts +6 -0
- package/chat/demo-artifacts.js +80 -0
- package/chat/demo-card-components.d.ts +48 -0
- package/chat/demo-card-components.js +66 -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 +237 -0
- package/chat/demo.css +52 -0
- package/chat/demo.d.ts +10 -0
- package/chat/demo.js +28 -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 +37 -0
- package/chat/index.js +90 -0
- package/chat/placement.d.ts +48 -0
- package/chat/placement.js +39 -0
- package/chat/queries.d.ts +18 -0
- package/chat/queries.js +26 -0
- package/chat/registry.d.ts +25 -0
- package/chat/registry.js +9 -0
- package/chat/registry.json.d.ts +74 -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 +246 -0
- package/chat/types.js +18 -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/AgentRail/AgentRail.css +14 -5
- package/components/AgentRail/AgentRail.d.ts +2 -0
- package/components/AgentRail/AgentRail.js +3 -2
- package/components/ArtifactCard/ArtifactCard.css +106 -0
- package/components/ArtifactCard/ArtifactCard.d.ts +27 -0
- package/components/ArtifactCard/ArtifactCard.js +29 -0
- package/components/ArtifactPanel/ArtifactPanel.css +159 -0
- package/components/ArtifactPanel/ArtifactPanel.d.ts +70 -0
- package/components/ArtifactPanel/ArtifactPanel.js +131 -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/components/ThreadPanel/ThreadPanel.css +87 -33
- package/components/ThreadPanel/ThreadPanel.js +21 -10
- package/components/registry.json +16 -0
- package/components/registry.json.d.ts +16 -0
- package/components/registry.json.js +1 -1
- package/fonts/material-symbols.css +2 -1
- package/index.d.ts +2 -0
- package/index.js +4 -0
- 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
package/chat/demo.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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_ARTIFACT_RENDERERS, DEMO_DASHBOARD_ARTIFACT, DEMO_PAGE_ARTIFACT } from "./demo-artifacts.js";
|
|
6
|
+
import { DEMO_MENTIONS, DEMO_MODELS, DEMO_PROJECTS, DEMO_THREADS, DEMO_THREAD_GROUPS, DEMO_THREAD_NAMES, DEMO_WELCOME_OPTIONS } from "./demo-fixtures.js";
|
|
7
|
+
import { demoAgentTabs } from "./demo-agent.js";
|
|
8
|
+
import { DemoBoard, DemoWelcome } from "./DemoWelcome.js";
|
|
9
|
+
const createDemoTransport = (timing) => createSimTransport(DEMO_SCRIPT, timing);
|
|
10
|
+
export {
|
|
11
|
+
DEMO_ARTIFACT_RENDERERS,
|
|
12
|
+
DEMO_DASHBOARD_ARTIFACT,
|
|
13
|
+
DEMO_MENTIONS,
|
|
14
|
+
DEMO_MODELS,
|
|
15
|
+
DEMO_PAGE_ARTIFACT,
|
|
16
|
+
DEMO_PROJECTS,
|
|
17
|
+
DEMO_RENDERERS,
|
|
18
|
+
DEMO_SCRIPT,
|
|
19
|
+
DEMO_STARTERS,
|
|
20
|
+
DEMO_THREADS,
|
|
21
|
+
DEMO_THREAD_GROUPS,
|
|
22
|
+
DEMO_THREAD_NAMES,
|
|
23
|
+
DEMO_WELCOME_OPTIONS,
|
|
24
|
+
DemoBoard,
|
|
25
|
+
DemoWelcome,
|
|
26
|
+
createDemoTransport,
|
|
27
|
+
demoAgentTabs
|
|
28
|
+
};
|
|
@@ -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
|
+
};
|
package/chat/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rift-ds/chat: the composed chat widget and the engine under it.
|
|
3
|
+
*
|
|
4
|
+
* The widget: ChatProvider (the state), ChatWidget (the surface), ChatLauncher
|
|
5
|
+
* (the floating button and the docked / overlay / floating / takeover panel).
|
|
6
|
+
* The engine, importable on its own under your own markup: useChat, the
|
|
7
|
+
* transports, useThreads, and the event contract. The server half lives in
|
|
8
|
+
* rift-ds/chat/server; a scripted conversation for previews in
|
|
9
|
+
* rift-ds/chat/demo.
|
|
10
|
+
*
|
|
11
|
+
* Hand-written, unlike the component barrels: the registry generator never
|
|
12
|
+
* reaches src/chat, and this file is the one place the widget's public
|
|
13
|
+
* surface is declared (src/chat/registry.json names the same exports for the
|
|
14
|
+
* documentation, and validate-chat-widget.mjs holds the two together).
|
|
15
|
+
*/
|
|
16
|
+
export { ChatProvider, type ChatProviderProps } from './ChatProvider';
|
|
17
|
+
export { ChatWidget, type ChatWidgetProps, type ChatAgentOptions, type RailSlot } from './ChatWidget';
|
|
18
|
+
export { ChatLauncher, CHAT_PANEL_ID, type ChatLauncherProps } from './ChatLauncher';
|
|
19
|
+
export { ChatThreads, type ChatThreadsProps } from './ChatThreads';
|
|
20
|
+
export { ChatTabs, type ChatTabsProps } from './ChatTabs';
|
|
21
|
+
export { ChatAgentPane, type ChatAgentPaneProps } from './ChatAgentPane';
|
|
22
|
+
export { ChatArtifactPane, type ChatArtifactPaneProps } from './ChatArtifactPane';
|
|
23
|
+
export { ChatArtifactCard, type ChatArtifactCardProps } from './ChatArtifactCard';
|
|
24
|
+
export { useChatWidget, isInternalPath, createFeedbackPoster, type ChatView, type ChatArtifactRenderers, type ChatCardRenderers, type ChatLinkRenderer, type ChatWidgetContextValue, type FeedbackSink, } from './context';
|
|
25
|
+
export { DOCK_QUERY, TAKEOVER_QUERY, THREADS_RAIL_MIN_WIDTH, ASIDE_RAIL_MIN_WIDTH, ARTIFACT_MIN_COLUMN_WIDTH, ASIDE_WITH_THREADS_MIN_WIDTH, useTakeoverViewport, } from './queries';
|
|
26
|
+
export { useChat, DEFAULT_PLACEHOLDER_COPY, type ChatState, type ChatPlaceholderCopy, } from './useChat';
|
|
27
|
+
export { useThreads, THREAD_NAMING_DELAY_MS, type ThreadsOptions, type ThreadsState } from './threads';
|
|
28
|
+
export { createFetchTransport, DEFAULT_FETCH_COPY, FIRST_BYTE_TIMEOUT_MS, MAX_TRANSPORT_TURNS, type FetchTransportOptions, type FetchTransportCopy, } from './transport';
|
|
29
|
+
export { scriptChat, createSimTransport, defaultFilesReply, DEFAULT_FALLBACK_REPLY, DEFAULT_SIM_TIMING, type ChatScript, type ChatScriptBuilder, type ScriptNode, type ScriptStep, type ScriptedReply, type SimTiming, } from './sim';
|
|
30
|
+
export { createFollowupsFetcher, followupTarget, pickFallbackFollowups, DEFAULT_FALLBACK_FOLLOWUPS, MAX_FOLLOWUPS, FOLLOWUPS_TIMEOUT_MS, type FollowupsSource, } from './followups';
|
|
31
|
+
export { SUGGESTION_MAX_CHARS, fitsChip } from './suggestions';
|
|
32
|
+
export { STARTER_COUNT, pickStarters, namedStarter, hashString, mulberry32, } from './starters';
|
|
33
|
+
export { greetingFor, readGreeting, serverGreeting, subscribeClock, DEFAULT_GREETING_COPY, type GreetingCopy, } from './greeting';
|
|
34
|
+
export { CHAT_PLACEMENTS, chatPlacement, DOCK_MIN_WIDTH, DOCK_MAX_WIDTH, FLOAT_MAX_SCALE, FLOAT_EDGE_GAP, dockWidthFromDrag, isFloatDragStart, clampFloatPosition, type ChatPlacement, type ChatSide, } from './placement';
|
|
35
|
+
export { artifactCard, cardArtifact } from './artifacts';
|
|
36
|
+
export { ARTIFACT_CARD_KIND, CHAT_EVENT_KINDS, type ChatArtifact, type ChatCard, type ChatEvent, type ChatTransport, type ChatTransportMessage, type ChatTurn, type ChatModelOption, type ChatFeedbackVerdict, type LivePhase, type LiveResponse, type Starter, } from './types';
|
|
37
|
+
export { chatWidget, chatWidgetRegistry, WIDGET_COUNT, type ChatWidgetMeta } from './registry';
|
package/chat/index.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ChatProvider } from "./ChatProvider.js";
|
|
2
|
+
import { ChatWidget } from "./ChatWidget.js";
|
|
3
|
+
import { CHAT_PANEL_ID, ChatLauncher } from "./ChatLauncher.js";
|
|
4
|
+
import { ChatThreads } from "./ChatThreads.js";
|
|
5
|
+
import { ChatTabs } from "./ChatTabs.js";
|
|
6
|
+
import { ChatAgentPane } from "./ChatAgentPane.js";
|
|
7
|
+
import { ChatArtifactPane } from "./ChatArtifactPane.js";
|
|
8
|
+
import { ChatArtifactCard } from "./ChatArtifactCard.js";
|
|
9
|
+
import { createFeedbackPoster, isInternalPath, useChatWidget } from "./context.js";
|
|
10
|
+
import { ARTIFACT_MIN_COLUMN_WIDTH, ASIDE_RAIL_MIN_WIDTH, ASIDE_WITH_THREADS_MIN_WIDTH, DOCK_QUERY, TAKEOVER_QUERY, THREADS_RAIL_MIN_WIDTH, useTakeoverViewport } from "./queries.js";
|
|
11
|
+
import { DEFAULT_PLACEHOLDER_COPY, useChat } from "./useChat.js";
|
|
12
|
+
import { THREAD_NAMING_DELAY_MS, useThreads } from "./threads.js";
|
|
13
|
+
import { DEFAULT_FETCH_COPY, FIRST_BYTE_TIMEOUT_MS, MAX_TRANSPORT_TURNS, createFetchTransport } from "./transport.js";
|
|
14
|
+
import { DEFAULT_FALLBACK_REPLY, DEFAULT_SIM_TIMING, createSimTransport, defaultFilesReply, scriptChat } from "./sim.js";
|
|
15
|
+
import { DEFAULT_FALLBACK_FOLLOWUPS, FOLLOWUPS_TIMEOUT_MS, MAX_FOLLOWUPS, createFollowupsFetcher, followupTarget, pickFallbackFollowups } from "./followups.js";
|
|
16
|
+
import { SUGGESTION_MAX_CHARS, fitsChip } from "./suggestions.js";
|
|
17
|
+
import { STARTER_COUNT, hashString, mulberry32, namedStarter, pickStarters } from "./starters.js";
|
|
18
|
+
import { DEFAULT_GREETING_COPY, greetingFor, readGreeting, serverGreeting, subscribeClock } from "./greeting.js";
|
|
19
|
+
import { CHAT_PLACEMENTS, DOCK_MAX_WIDTH, DOCK_MIN_WIDTH, FLOAT_EDGE_GAP, FLOAT_MAX_SCALE, chatPlacement, clampFloatPosition, dockWidthFromDrag, isFloatDragStart } from "./placement.js";
|
|
20
|
+
import { artifactCard, cardArtifact } from "./artifacts.js";
|
|
21
|
+
import { ARTIFACT_CARD_KIND, CHAT_EVENT_KINDS } from "./types.js";
|
|
22
|
+
import { WIDGET_COUNT, chatWidget, chatWidgetRegistry } from "./registry.js";
|
|
23
|
+
export {
|
|
24
|
+
ARTIFACT_CARD_KIND,
|
|
25
|
+
ARTIFACT_MIN_COLUMN_WIDTH,
|
|
26
|
+
ASIDE_RAIL_MIN_WIDTH,
|
|
27
|
+
ASIDE_WITH_THREADS_MIN_WIDTH,
|
|
28
|
+
CHAT_EVENT_KINDS,
|
|
29
|
+
CHAT_PANEL_ID,
|
|
30
|
+
CHAT_PLACEMENTS,
|
|
31
|
+
ChatAgentPane,
|
|
32
|
+
ChatArtifactCard,
|
|
33
|
+
ChatArtifactPane,
|
|
34
|
+
ChatLauncher,
|
|
35
|
+
ChatProvider,
|
|
36
|
+
ChatTabs,
|
|
37
|
+
ChatThreads,
|
|
38
|
+
ChatWidget,
|
|
39
|
+
DEFAULT_FALLBACK_FOLLOWUPS,
|
|
40
|
+
DEFAULT_FALLBACK_REPLY,
|
|
41
|
+
DEFAULT_FETCH_COPY,
|
|
42
|
+
DEFAULT_GREETING_COPY,
|
|
43
|
+
DEFAULT_PLACEHOLDER_COPY,
|
|
44
|
+
DEFAULT_SIM_TIMING,
|
|
45
|
+
DOCK_MAX_WIDTH,
|
|
46
|
+
DOCK_MIN_WIDTH,
|
|
47
|
+
DOCK_QUERY,
|
|
48
|
+
FIRST_BYTE_TIMEOUT_MS,
|
|
49
|
+
FLOAT_EDGE_GAP,
|
|
50
|
+
FLOAT_MAX_SCALE,
|
|
51
|
+
FOLLOWUPS_TIMEOUT_MS,
|
|
52
|
+
MAX_FOLLOWUPS,
|
|
53
|
+
MAX_TRANSPORT_TURNS,
|
|
54
|
+
STARTER_COUNT,
|
|
55
|
+
SUGGESTION_MAX_CHARS,
|
|
56
|
+
TAKEOVER_QUERY,
|
|
57
|
+
THREADS_RAIL_MIN_WIDTH,
|
|
58
|
+
THREAD_NAMING_DELAY_MS,
|
|
59
|
+
WIDGET_COUNT,
|
|
60
|
+
artifactCard,
|
|
61
|
+
cardArtifact,
|
|
62
|
+
chatPlacement,
|
|
63
|
+
chatWidget,
|
|
64
|
+
chatWidgetRegistry,
|
|
65
|
+
clampFloatPosition,
|
|
66
|
+
createFeedbackPoster,
|
|
67
|
+
createFetchTransport,
|
|
68
|
+
createFollowupsFetcher,
|
|
69
|
+
createSimTransport,
|
|
70
|
+
defaultFilesReply,
|
|
71
|
+
dockWidthFromDrag,
|
|
72
|
+
fitsChip,
|
|
73
|
+
followupTarget,
|
|
74
|
+
greetingFor,
|
|
75
|
+
hashString,
|
|
76
|
+
isFloatDragStart,
|
|
77
|
+
isInternalPath,
|
|
78
|
+
mulberry32,
|
|
79
|
+
namedStarter,
|
|
80
|
+
pickFallbackFollowups,
|
|
81
|
+
pickStarters,
|
|
82
|
+
readGreeting,
|
|
83
|
+
scriptChat,
|
|
84
|
+
serverGreeting,
|
|
85
|
+
subscribeClock,
|
|
86
|
+
useChat,
|
|
87
|
+
useChatWidget,
|
|
88
|
+
useTakeoverViewport,
|
|
89
|
+
useThreads
|
|
90
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a chat panel sits: docked to either edge, or popped out as a
|
|
3
|
+
* floating card the visitor moves and sizes. One roster, so the header menu
|
|
4
|
+
* (ChatWidget), the launcher and any host stage all name the same three
|
|
5
|
+
* seats the same way. design.md's ChatWidget spec owns the behaviour each
|
|
6
|
+
* seat promises.
|
|
7
|
+
*
|
|
8
|
+
* No React and no component imports, like the dock query's home in
|
|
9
|
+
* ChatProvider: hosts deep in the tree read it without pulling the widget.
|
|
10
|
+
*/
|
|
11
|
+
/** The edge a docked panel takes, and the edge it overlays from below the dock threshold. */
|
|
12
|
+
export type ChatSide = 'left' | 'right';
|
|
13
|
+
/** A panel's seat: a dock edge, or floating free of both. */
|
|
14
|
+
export type ChatPlacement = ChatSide | 'floating';
|
|
15
|
+
export declare const CHAT_PLACEMENTS: readonly {
|
|
16
|
+
id: ChatPlacement;
|
|
17
|
+
label: string;
|
|
18
|
+
icon: string;
|
|
19
|
+
}[];
|
|
20
|
+
/** The roster entry for a placement: the header trigger wears its icon. */
|
|
21
|
+
export declare const chatPlacement: (id: ChatPlacement) => {
|
|
22
|
+
id: ChatPlacement;
|
|
23
|
+
label: string;
|
|
24
|
+
icon: string;
|
|
25
|
+
};
|
|
26
|
+
export declare const DOCK_MIN_WIDTH = 420;
|
|
27
|
+
export declare const DOCK_MAX_WIDTH = 655;
|
|
28
|
+
/** A docked panel's width after a grip drag: the inner edge is the one
|
|
29
|
+
that moves, and it faces the other way on the left dock. */
|
|
30
|
+
export declare function dockWidthFromDrag(startWidth: number, pointerDelta: number, side: ChatSide): number;
|
|
31
|
+
export declare const FLOAT_MAX_SCALE = 1.2;
|
|
32
|
+
export declare const FLOAT_EDGE_GAP = 8;
|
|
33
|
+
/** Whether a pointerdown on `target` should start moving a floating card. */
|
|
34
|
+
export declare function isFloatDragStart(target: EventTarget | null): boolean;
|
|
35
|
+
/** Holds a moved card inside `bounds`, FLOAT_EDGE_GAP off each edge. A card
|
|
36
|
+
larger than the bounds pins to the leading edge rather than oscillating. */
|
|
37
|
+
export declare function clampFloatPosition(x: number, y: number, size: {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
}, bounds: {
|
|
41
|
+
left: number;
|
|
42
|
+
top: number;
|
|
43
|
+
right: number;
|
|
44
|
+
bottom: number;
|
|
45
|
+
}): {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const CHAT_PLACEMENTS = [
|
|
2
|
+
{ id: "right", label: "Dock right", icon: "dock_to_left" },
|
|
3
|
+
{ id: "left", label: "Dock left", icon: "dock_to_right" },
|
|
4
|
+
{ id: "floating", label: "Floating", icon: "picture_in_picture" }
|
|
5
|
+
];
|
|
6
|
+
const chatPlacement = (id) => CHAT_PLACEMENTS.find((placement) => placement.id === id) ?? CHAT_PLACEMENTS[0];
|
|
7
|
+
const DOCK_MIN_WIDTH = 420;
|
|
8
|
+
const DOCK_MAX_WIDTH = 655;
|
|
9
|
+
function dockWidthFromDrag(startWidth, pointerDelta, side) {
|
|
10
|
+
const grown = startWidth + (side === "left" ? pointerDelta : -pointerDelta);
|
|
11
|
+
return Math.round(Math.min(DOCK_MAX_WIDTH, Math.max(DOCK_MIN_WIDTH, grown)));
|
|
12
|
+
}
|
|
13
|
+
const FLOAT_MAX_SCALE = 1.2;
|
|
14
|
+
const FLOAT_EDGE_GAP = 8;
|
|
15
|
+
const FLOAT_DRAG_ZONE = ".ds-chat-header";
|
|
16
|
+
const FLOAT_DRAG_EXEMPT = "button, a, input, textarea, select, [role='button'], [role='menu']";
|
|
17
|
+
function isFloatDragStart(target) {
|
|
18
|
+
if (!(target instanceof Element)) return false;
|
|
19
|
+
return Boolean(target.closest(FLOAT_DRAG_ZONE)) && !target.closest(FLOAT_DRAG_EXEMPT);
|
|
20
|
+
}
|
|
21
|
+
function clampFloatPosition(x, y, size, bounds) {
|
|
22
|
+
const minX = bounds.left + FLOAT_EDGE_GAP;
|
|
23
|
+
const minY = bounds.top + FLOAT_EDGE_GAP;
|
|
24
|
+
return {
|
|
25
|
+
x: Math.round(Math.min(Math.max(minX, bounds.right - size.width - FLOAT_EDGE_GAP), Math.max(minX, x))),
|
|
26
|
+
y: Math.round(Math.min(Math.max(minY, bounds.bottom - size.height - FLOAT_EDGE_GAP), Math.max(minY, y)))
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
CHAT_PLACEMENTS,
|
|
31
|
+
DOCK_MAX_WIDTH,
|
|
32
|
+
DOCK_MIN_WIDTH,
|
|
33
|
+
FLOAT_EDGE_GAP,
|
|
34
|
+
FLOAT_MAX_SCALE,
|
|
35
|
+
chatPlacement,
|
|
36
|
+
clampFloatPosition,
|
|
37
|
+
dockWidthFromDrag,
|
|
38
|
+
isFloatDragStart
|
|
39
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const DOCK_QUERY = "(min-width: 1440px)";
|
|
2
|
+
export declare const TAKEOVER_QUERY = "(max-width: 719px)";
|
|
3
|
+
/** The widget's own width thresholds for seating its rails inline. */
|
|
4
|
+
export declare const THREADS_RAIL_MIN_WIDTH = 720;
|
|
5
|
+
/** The trailing seat's threshold, one number for both of its tenants: at
|
|
6
|
+
this width and up the agent pane and the artifact panel sit beside the
|
|
7
|
+
conversation, below it each rises as a bottom sheet. */
|
|
8
|
+
export declare const ASIDE_RAIL_MIN_WIDTH = 760;
|
|
9
|
+
/** Below this the inline threads rail yields its room to a trailing panel
|
|
10
|
+
seated inline, and goes back behind the header's hamburger. */
|
|
11
|
+
export declare const ASIDE_WITH_THREADS_MIN_WIDTH = 1040;
|
|
12
|
+
/** The floor either column keeps while an artifact is dragged wider or
|
|
13
|
+
narrower: the conversation never loses more than this, nor the panel. */
|
|
14
|
+
export declare const ARTIFACT_MIN_COLUMN_WIDTH = 320;
|
|
15
|
+
/** Whether the viewport is a phone width (TAKEOVER_QUERY), false on the
|
|
16
|
+
server and for the first client render. Shared by the launcher and any
|
|
17
|
+
host stage, which both hand the widget its phone layout. */
|
|
18
|
+
export declare function useTakeoverViewport(): boolean;
|
package/chat/queries.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
const DOCK_QUERY = "(min-width: 1440px)";
|
|
4
|
+
const TAKEOVER_QUERY = "(max-width: 719px)";
|
|
5
|
+
const THREADS_RAIL_MIN_WIDTH = 720;
|
|
6
|
+
const ASIDE_RAIL_MIN_WIDTH = 760;
|
|
7
|
+
const ASIDE_WITH_THREADS_MIN_WIDTH = 1040;
|
|
8
|
+
const ARTIFACT_MIN_COLUMN_WIDTH = 320;
|
|
9
|
+
const subscribeTakeover = (onChange) => {
|
|
10
|
+
const media = window.matchMedia(TAKEOVER_QUERY);
|
|
11
|
+
media.addEventListener("change", onChange);
|
|
12
|
+
return () => media.removeEventListener("change", onChange);
|
|
13
|
+
};
|
|
14
|
+
const readTakeover = () => window.matchMedia(TAKEOVER_QUERY).matches;
|
|
15
|
+
function useTakeoverViewport() {
|
|
16
|
+
return useSyncExternalStore(subscribeTakeover, readTakeover, () => false);
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
ARTIFACT_MIN_COLUMN_WIDTH,
|
|
20
|
+
ASIDE_RAIL_MIN_WIDTH,
|
|
21
|
+
ASIDE_WITH_THREADS_MIN_WIDTH,
|
|
22
|
+
DOCK_QUERY,
|
|
23
|
+
TAKEOVER_QUERY,
|
|
24
|
+
THREADS_RAIL_MIN_WIDTH,
|
|
25
|
+
useTakeoverViewport
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** One widget's metadata, as the registry records it. */
|
|
2
|
+
export interface ChatWidgetMeta {
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description: string;
|
|
7
|
+
/** The component category whose /components section and sidebar group the widget's page joins. */
|
|
8
|
+
category: string;
|
|
9
|
+
/** The init bin's subcommand that scaffolds it. */
|
|
10
|
+
command: string;
|
|
11
|
+
/** Each package subpath the widget occupies, with what it holds. */
|
|
12
|
+
subpaths: Record<string, string>;
|
|
13
|
+
/** The optional peer dependencies each subpath needs. */
|
|
14
|
+
peers: Record<string, string[]>;
|
|
15
|
+
/** The library components the widget composes. */
|
|
16
|
+
parts: string[];
|
|
17
|
+
/** The exported components whose props are documented through docgen. */
|
|
18
|
+
documented: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare const chatWidgetRegistry: {
|
|
21
|
+
widgets: ChatWidgetMeta[];
|
|
22
|
+
};
|
|
23
|
+
/** The one widget, by design. */
|
|
24
|
+
export declare const chatWidget: ChatWidgetMeta;
|
|
25
|
+
export declare const WIDGET_COUNT: number;
|
package/chat/registry.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
"$comment": "The widget registry: the fourth kind of thing the package ships, between the components and the templates. One member by design. `parts` names the library components the widget composes (held to src/components/registry.json by scripts/validate-chat-widget.mjs), `subpaths` the package exports it occupies (held to scripts/package-manifest.mjs), `category` the component category whose section and sidebar group its page joins (held to src/components/registry.json), `peers` the optional peer dependencies each subpath needs (held to package.json). The widget's page under /components, the /docs/chat page, the chat corpus and the MCP tools read this file; never type its facts elsewhere.",
|
|
3
|
+
"widgets": [
|
|
4
|
+
{
|
|
5
|
+
"name": "ChatWidget",
|
|
6
|
+
"slug": "ai-chat",
|
|
7
|
+
"label": "AI chat",
|
|
8
|
+
"category": "ai",
|
|
9
|
+
"description": "The whole AI chat, assembled: the panel, the streaming replies, the history, the cards a reply can carry and the server handlers, set up with props.",
|
|
10
|
+
"command": "init chat",
|
|
11
|
+
"subpaths": {
|
|
12
|
+
"./chat": "The widget, the provider, the launcher and the engine under them.",
|
|
13
|
+
"./chat/server": "The streaming chat handler and its follow-ups and feedback siblings, for any web-standard route.",
|
|
14
|
+
"./chat/upstash": "Rate limits, a daily budget and the exchange log over Upstash Redis, as the handlers' guardrails.",
|
|
15
|
+
"./chat/demo": "A scripted conversation with rich cards and fixtures, for a preview that needs no key.",
|
|
16
|
+
"./chat/chat.css": "The widget's stylesheet."
|
|
17
|
+
},
|
|
18
|
+
"peers": {
|
|
19
|
+
"./chat": [
|
|
20
|
+
"react-markdown",
|
|
21
|
+
"remark-gfm"
|
|
22
|
+
],
|
|
23
|
+
"./chat/server": [
|
|
24
|
+
"@anthropic-ai/sdk"
|
|
25
|
+
],
|
|
26
|
+
"./chat/upstash": [
|
|
27
|
+
"@upstash/ratelimit",
|
|
28
|
+
"@upstash/redis"
|
|
29
|
+
],
|
|
30
|
+
"./chat/demo": [
|
|
31
|
+
"recharts"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"parts": [
|
|
35
|
+
"AgentRail",
|
|
36
|
+
"AgentStatus",
|
|
37
|
+
"AiButton",
|
|
38
|
+
"ArtifactCard",
|
|
39
|
+
"ArtifactPanel",
|
|
40
|
+
"AttachmentDropZone",
|
|
41
|
+
"AttachmentGroup",
|
|
42
|
+
"ChatHeader",
|
|
43
|
+
"ChatMessage",
|
|
44
|
+
"ChatThread",
|
|
45
|
+
"CircularButton",
|
|
46
|
+
"CodeBlock",
|
|
47
|
+
"Composer",
|
|
48
|
+
"DropdownMenu",
|
|
49
|
+
"EmptyState",
|
|
50
|
+
"Mention",
|
|
51
|
+
"ModelPicker",
|
|
52
|
+
"PixelAvatar",
|
|
53
|
+
"PromptSuggestions",
|
|
54
|
+
"Prose",
|
|
55
|
+
"Reasoning",
|
|
56
|
+
"ThreadPanel",
|
|
57
|
+
"ThreadTabs"
|
|
58
|
+
],
|
|
59
|
+
"documented": [
|
|
60
|
+
"ChatWidget",
|
|
61
|
+
"ChatProvider",
|
|
62
|
+
"ChatLauncher",
|
|
63
|
+
"ChatThreads",
|
|
64
|
+
"ChatTabs",
|
|
65
|
+
"ChatAgentPane",
|
|
66
|
+
"ChatArtifactPane",
|
|
67
|
+
"ChatArtifactCard"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
;
|
|
73
|
+
|
|
74
|
+
export default _default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const $comment = "The widget registry: the fourth kind of thing the package ships, between the components and the templates. One member by design. `parts` names the library components the widget composes (held to src/components/registry.json by scripts/validate-chat-widget.mjs), `subpaths` the package exports it occupies (held to scripts/package-manifest.mjs), `category` the component category whose section and sidebar group its page joins (held to src/components/registry.json), `peers` the optional peer dependencies each subpath needs (held to package.json). The widget's page under /components, the /docs/chat page, the chat corpus and the MCP tools read this file; never type its facts elsewhere.";
|
|
2
|
+
const widgets = [{ "name": "ChatWidget", "slug": "ai-chat", "label": "AI chat", "category": "ai", "description": "The whole AI chat, assembled: the panel, the streaming replies, the history, the cards a reply can carry and the server handlers, set up with props.", "command": "init chat", "subpaths": { "./chat": "The widget, the provider, the launcher and the engine under them.", "./chat/server": "The streaming chat handler and its follow-ups and feedback siblings, for any web-standard route.", "./chat/upstash": "Rate limits, a daily budget and the exchange log over Upstash Redis, as the handlers' guardrails.", "./chat/demo": "A scripted conversation with rich cards and fixtures, for a preview that needs no key.", "./chat/chat.css": "The widget's stylesheet." }, "peers": { "./chat": ["react-markdown", "remark-gfm"], "./chat/server": ["@anthropic-ai/sdk"], "./chat/upstash": ["@upstash/ratelimit", "@upstash/redis"], "./chat/demo": ["recharts"] }, "parts": ["AgentRail", "AgentStatus", "AiButton", "ArtifactCard", "ArtifactPanel", "AttachmentDropZone", "AttachmentGroup", "ChatHeader", "ChatMessage", "ChatThread", "CircularButton", "CodeBlock", "Composer", "DropdownMenu", "EmptyState", "Mention", "ModelPicker", "PixelAvatar", "PromptSuggestions", "Prose", "Reasoning", "ThreadPanel", "ThreadTabs"], "documented": ["ChatWidget", "ChatProvider", "ChatLauncher", "ChatThreads", "ChatTabs", "ChatAgentPane", "ChatArtifactPane", "ChatArtifactCard"] }];
|
|
3
|
+
const registry = {
|
|
4
|
+
$comment,
|
|
5
|
+
widgets
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
$comment,
|
|
9
|
+
registry as default,
|
|
10
|
+
widgets
|
|
11
|
+
};
|