@athenaintel/react 0.39.0 → 0.40.1
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 +39 -3
- package/dist/agent-runtime/src/errors.cjs +8 -0
- package/dist/agent-runtime/src/errors.cjs.map +1 -1
- package/dist/agent-runtime/src/errors.js +8 -0
- package/dist/agent-runtime/src/errors.js.map +1 -1
- package/dist/agent-runtime/src/messages.cjs +76 -16
- package/dist/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/agent-runtime/src/messages.js +76 -16
- package/dist/agent-runtime/src/messages.js.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs +15 -0
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +15 -0
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/assets/use-asset-content.cjs +127 -0
- package/dist/assets/use-asset-content.cjs.map +1 -0
- package/dist/assets/use-asset-content.d.ts +60 -0
- package/dist/assets/use-asset-content.js +127 -0
- package/dist/assets/use-asset-content.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +24 -10
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +25 -11
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/run-activity.cjs +29 -10
- package/dist/chat/run-activity.cjs.map +1 -1
- package/dist/chat/run-activity.d.ts +30 -4
- package/dist/chat/run-activity.js +29 -10
- package/dist/chat/run-activity.js.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs +16 -8
- package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.d.ts +26 -1
- package/dist/chat/super-grouping/CollapsibleGroup.js +16 -8
- package/dist/chat/super-grouping/CollapsibleGroup.js.map +1 -1
- package/dist/chat/super-grouping/GroupedMessageParts.cjs +59 -0
- package/dist/chat/super-grouping/GroupedMessageParts.cjs.map +1 -0
- package/dist/chat/super-grouping/GroupedMessageParts.d.ts +25 -0
- package/dist/chat/super-grouping/GroupedMessageParts.js +59 -0
- package/dist/chat/super-grouping/GroupedMessageParts.js.map +1 -0
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +9 -5
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +2 -1
- package/dist/chat/super-grouping/SuperGroupingCard.js +13 -9
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +10 -6
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.js +14 -10
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
- package/dist/chat/super-grouping/grouped-part-keys.cjs +104 -0
- package/dist/chat/super-grouping/grouped-part-keys.cjs.map +1 -0
- package/dist/chat/super-grouping/grouped-part-keys.d.ts +37 -0
- package/dist/chat/super-grouping/grouped-part-keys.js +104 -0
- package/dist/chat/super-grouping/grouped-part-keys.js.map +1 -0
- package/dist/chat/super-grouping/index.cjs +2 -0
- package/dist/chat/super-grouping/index.cjs.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +3 -1
- package/dist/chat/super-grouping/index.js +2 -0
- package/dist/chat/super-grouping/index.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +25 -19
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.d.ts +5 -0
- package/dist/chat-ui/composer/tiptap-composer.js +26 -20
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/composer/clipboard.cjs +16 -0
- package/dist/composer/clipboard.cjs.map +1 -1
- package/dist/composer/clipboard.d.ts +9 -0
- package/dist/composer/clipboard.js +17 -1
- package/dist/composer/clipboard.js.map +1 -1
- package/dist/diagnostics/errors.cjs +6 -0
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +6 -0
- package/dist/diagnostics/errors.js +6 -0
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/index.cjs +23 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/use-document-active.cjs +40 -0
- package/dist/lib/use-document-active.cjs.map +1 -0
- package/dist/lib/use-document-active.d.ts +9 -0
- package/dist/lib/use-document-active.js +40 -0
- package/dist/lib/use-document-active.js.map +1 -0
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/statewire-lifecycle.cjs +8 -0
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.js +9 -1
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/threads/Inbox.cjs +6 -2
- package/dist/threads/Inbox.cjs.map +1 -1
- package/dist/threads/Inbox.js +6 -2
- package/dist/threads/Inbox.js.map +1 -1
- package/dist/threads/mark-viewed-thread-read.cjs +97 -0
- package/dist/threads/mark-viewed-thread-read.cjs.map +1 -0
- package/dist/threads/mark-viewed-thread-read.d.ts +7 -0
- package/dist/threads/mark-viewed-thread-read.js +97 -0
- package/dist/threads/mark-viewed-thread-read.js.map +1 -0
- package/dist/tools/index.cjs +7 -0
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inline-tools.cjs +10 -0
- package/dist/tools/inline-tools.cjs.map +1 -0
- package/dist/tools/inline-tools.d.ts +2 -0
- package/dist/tools/inline-tools.js +10 -0
- package/dist/tools/inline-tools.js.map +1 -0
- package/dist/tools/test-recording-report.cjs +175 -0
- package/dist/tools/test-recording-report.cjs.map +1 -0
- package/dist/tools/test-recording-report.d.ts +82 -0
- package/dist/tools/test-recording-report.js +175 -0
- package/dist/tools/test-recording-report.js.map +1 -0
- package/dist/tools/test-recording-tool-uis.cjs +498 -0
- package/dist/tools/test-recording-tool-uis.cjs.map +1 -0
- package/dist/tools/test-recording-tool-uis.d.ts +32 -0
- package/dist/tools/test-recording-tool-uis.js +498 -0
- package/dist/tools/test-recording-tool-uis.js.map +1 -0
- package/dist/tools/tool-summary.cjs +49 -1
- package/dist/tools/tool-summary.cjs.map +1 -1
- package/dist/tools/tool-summary.js +49 -1
- package/dist/tools/tool-summary.js.map +1 -1
- package/dist/tools/tool-uis.cjs +6 -1
- package/dist/tools/tool-uis.cjs.map +1 -1
- package/dist/tools/tool-uis.js +6 -1
- package/dist/tools/tool-uis.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,11 +13,32 @@ export declare function runActivityKey(parts: ReadonlyArray<unknown>): string |
|
|
|
13
13
|
/**
|
|
14
14
|
* The present-tense verb for what the run is doing right now — the registry's
|
|
15
15
|
* activity label for a tool call still awaiting its result ("Searching the
|
|
16
|
-
* web") — or `null` when the tail is not
|
|
16
|
+
* web") — or `null` when the tail is not a tool call in flight.
|
|
17
17
|
*/
|
|
18
18
|
export declare function runActivityVerb(parts: ReadonlyArray<unknown>): string | null;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Whether the run is waiting on a tool that is still running: the tail part
|
|
21
|
+
* is a tool call with no result yet, or only the streaming placeholder a
|
|
22
|
+
* long tool (Python execution, a task subagent) publishes while it works. A
|
|
23
|
+
* command, a build or a test suite streams nothing for minutes while it
|
|
24
|
+
* works, so a quiet stream then is work, not a stall.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isToolCallInFlight(parts: ReadonlyArray<unknown>): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Seconds without any streamed change before the row flips to a stall tone —
|
|
29
|
+
* while the run waits on the model, never while a tool call is in flight
|
|
30
|
+
* ({@link isRunActivityStalled}).
|
|
31
|
+
*/
|
|
20
32
|
export declare const RUN_ACTIVITY_STALL_SECONDS = 30;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the run looks stalled: nothing has streamed for
|
|
35
|
+
* {@link RUN_ACTIVITY_STALL_SECONDS} while it waits on the model. Never while a
|
|
36
|
+
* tool call is in flight: the call's own row says it is running.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isRunActivityStalled({ seconds, toolCallInFlight, }: {
|
|
39
|
+
seconds: number;
|
|
40
|
+
toolCallInFlight: boolean;
|
|
41
|
+
}): boolean;
|
|
21
42
|
/** Seconds before the row starts showing its elapsed counter. */
|
|
22
43
|
export declare const RUN_ACTIVITY_COUNTER_SECONDS = 3;
|
|
23
44
|
/** The default verb when nothing more specific is known. */
|
|
@@ -30,6 +51,11 @@ export declare const RUN_ACTIVITY_DEFAULT_VERB = "Thinking";
|
|
|
30
51
|
export declare function runElapsedLabel(seconds: number): string | null;
|
|
31
52
|
/**
|
|
32
53
|
* The status line for `seconds` since the stream last moved: the verb with an
|
|
33
|
-
* ellipsis
|
|
54
|
+
* ellipsis and the elapsed counter once it is due (`runElapsedLabel`), or the
|
|
55
|
+
* stall wording when the run looks `stalled` ({@link isRunActivityStalled}).
|
|
34
56
|
*/
|
|
35
|
-
export declare function runActivityLabel(seconds
|
|
57
|
+
export declare function runActivityLabel({ seconds, verb, stalled, }: {
|
|
58
|
+
seconds: number;
|
|
59
|
+
verb?: string;
|
|
60
|
+
stalled: boolean;
|
|
61
|
+
}): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStreamingToolPlaceholder } from "./super-grouping/activity-timeline.js";
|
|
1
2
|
import { getToolDisplayMeta } from "../agent-runtime/src/tool-metadata.js";
|
|
2
3
|
function runActivityKey(parts) {
|
|
3
4
|
const tail = tailPart(parts);
|
|
@@ -5,22 +6,38 @@ function runActivityKey(parts) {
|
|
|
5
6
|
const type = typeof tail["type"] === "string" ? tail["type"] : "unknown";
|
|
6
7
|
if (type === "text" || type === "reasoning") return null;
|
|
7
8
|
const argsText = typeof tail["argsText"] === "string" ? tail["argsText"].length : 0;
|
|
8
|
-
const settled = tail
|
|
9
|
+
const settled = hasNoResultYet(tail) ? 0 : 1;
|
|
9
10
|
return `${parts.length}:${type}:${argsText}:${settled}`;
|
|
10
11
|
}
|
|
11
12
|
function runActivityVerb(parts) {
|
|
12
13
|
const tail = tailPart(parts);
|
|
13
|
-
if (!tail || tail
|
|
14
|
+
if (!tail || !isTailToolCallRunning(tail)) return null;
|
|
14
15
|
const toolName = tail["toolName"];
|
|
15
16
|
if (typeof toolName !== "string" || toolName.length === 0) return null;
|
|
16
17
|
return getToolDisplayMeta(toolName).activeLabel;
|
|
17
18
|
}
|
|
19
|
+
function isToolCallInFlight(parts) {
|
|
20
|
+
const tail = tailPart(parts);
|
|
21
|
+
return tail !== null && isTailToolCallRunning(tail);
|
|
22
|
+
}
|
|
23
|
+
function isTailToolCallRunning(tail) {
|
|
24
|
+
return tail["type"] === "tool-call" && hasNoResultYet(tail);
|
|
25
|
+
}
|
|
26
|
+
function hasNoResultYet(tail) {
|
|
27
|
+
return tail["result"] === void 0 || isStreamingToolPlaceholder({ artifact: tail["artifact"] });
|
|
28
|
+
}
|
|
18
29
|
function tailPart(parts) {
|
|
19
30
|
const last = parts[parts.length - 1];
|
|
20
31
|
if (last === void 0 || last === null || typeof last !== "object") return null;
|
|
21
32
|
return last;
|
|
22
33
|
}
|
|
23
34
|
const RUN_ACTIVITY_STALL_SECONDS = 30;
|
|
35
|
+
function isRunActivityStalled({
|
|
36
|
+
seconds,
|
|
37
|
+
toolCallInFlight
|
|
38
|
+
}) {
|
|
39
|
+
return !toolCallInFlight && seconds >= RUN_ACTIVITY_STALL_SECONDS;
|
|
40
|
+
}
|
|
24
41
|
const RUN_ACTIVITY_COUNTER_SECONDS = 3;
|
|
25
42
|
const RUN_ACTIVITY_DEFAULT_VERB = "Thinking";
|
|
26
43
|
function runElapsedLabel(seconds) {
|
|
@@ -30,19 +47,21 @@ function runElapsedLabel(seconds) {
|
|
|
30
47
|
const remainder = seconds % 60;
|
|
31
48
|
return `${minutes}m ${String(remainder).padStart(2, "0")}s`;
|
|
32
49
|
}
|
|
33
|
-
function runActivityLabel(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return `${verb}
|
|
50
|
+
function runActivityLabel({
|
|
51
|
+
seconds,
|
|
52
|
+
verb = RUN_ACTIVITY_DEFAULT_VERB,
|
|
53
|
+
stalled
|
|
54
|
+
}) {
|
|
55
|
+
if (stalled) return `Still working — nothing new for ${seconds}s`;
|
|
56
|
+
const elapsed = runElapsedLabel(seconds);
|
|
57
|
+
return elapsed === null ? `${verb}…` : `${verb}… · ${elapsed}`;
|
|
41
58
|
}
|
|
42
59
|
export {
|
|
43
60
|
RUN_ACTIVITY_COUNTER_SECONDS,
|
|
44
61
|
RUN_ACTIVITY_DEFAULT_VERB,
|
|
45
62
|
RUN_ACTIVITY_STALL_SECONDS,
|
|
63
|
+
isRunActivityStalled,
|
|
64
|
+
isToolCallInFlight,
|
|
46
65
|
runActivityKey,
|
|
47
66
|
runActivityLabel,
|
|
48
67
|
runActivityVerb,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-activity.js","sources":["../../src/chat/run-activity.ts"],"sourcesContent":["import { getToolDisplayMeta } from '@athenaintel/agent-runtime';\n\n/**\n * Run-liveness key for a streaming assistant message.\n *\n * A tool card's spinner says \"this call is running\"; nothing says the RUN is\n * still alive once parts exist — a stalled stream and a busy one look\n * identical. The activity key summarizes the message tail: it is `null` when\n * no liveness row should show (no parts yet, or a text/reasoning tail that\n * already animates on its own), and otherwise changes whenever anything\n * streams into the tail part — so an elapsed timer keyed on it measures\n * \"time since the stream last moved\", not just \"time since the call began\".\n */\nexport function runActivityKey(parts: ReadonlyArray<unknown>): string | null {\n const tail = tailPart(parts);\n if (!tail) return null;\n const type = typeof tail['type'] === 'string' ? (tail['type'] as string) : 'unknown';\n if (type === 'text' || type === 'reasoning') return null;\n const argsText = typeof tail['argsText'] === 'string' ? (tail['argsText'] as string).length : 0;\n const settled = tail
|
|
1
|
+
{"version":3,"file":"run-activity.js","sources":["../../src/chat/run-activity.ts"],"sourcesContent":["import { getToolDisplayMeta } from '@athenaintel/agent-runtime';\nimport { isStreamingToolPlaceholder } from './super-grouping/activity-timeline';\n\n/**\n * Run-liveness key for a streaming assistant message.\n *\n * A tool card's spinner says \"this call is running\"; nothing says the RUN is\n * still alive once parts exist — a stalled stream and a busy one look\n * identical. The activity key summarizes the message tail: it is `null` when\n * no liveness row should show (no parts yet, or a text/reasoning tail that\n * already animates on its own), and otherwise changes whenever anything\n * streams into the tail part — so an elapsed timer keyed on it measures\n * \"time since the stream last moved\", not just \"time since the call began\".\n */\nexport function runActivityKey(parts: ReadonlyArray<unknown>): string | null {\n const tail = tailPart(parts);\n if (!tail) return null;\n const type = typeof tail['type'] === 'string' ? (tail['type'] as string) : 'unknown';\n if (type === 'text' || type === 'reasoning') return null;\n const argsText = typeof tail['argsText'] === 'string' ? (tail['argsText'] as string).length : 0;\n // A streaming placeholder is not a result: the key moves when the real one replaces it.\n const settled = hasNoResultYet(tail) ? 0 : 1;\n return `${parts.length}:${type}:${argsText}:${settled}`;\n}\n\n/**\n * The present-tense verb for what the run is doing right now — the registry's\n * activity label for a tool call still awaiting its result (\"Searching the\n * web\") — or `null` when the tail is not a tool call in flight.\n */\nexport function runActivityVerb(parts: ReadonlyArray<unknown>): string | null {\n const tail = tailPart(parts);\n if (!tail || !isTailToolCallRunning(tail)) return null;\n const toolName = tail['toolName'];\n if (typeof toolName !== 'string' || toolName.length === 0) return null;\n return getToolDisplayMeta(toolName).activeLabel;\n}\n\n/**\n * Whether the run is waiting on a tool that is still running: the tail part\n * is a tool call with no result yet, or only the streaming placeholder a\n * long tool (Python execution, a task subagent) publishes while it works. A\n * command, a build or a test suite streams nothing for minutes while it\n * works, so a quiet stream then is work, not a stall.\n */\nexport function isToolCallInFlight(parts: ReadonlyArray<unknown>): boolean {\n const tail = tailPart(parts);\n return tail !== null && isTailToolCallRunning(tail);\n}\n\nfunction isTailToolCallRunning(tail: Record<string, unknown>): boolean {\n return tail['type'] === 'tool-call' && hasNoResultYet(tail);\n}\n\n/** No result yet: none at all, or only the streaming placeholder (`isStreamingToolPlaceholder`). */\nfunction hasNoResultYet(tail: Record<string, unknown>): boolean {\n return tail['result'] === undefined || isStreamingToolPlaceholder({ artifact: tail['artifact'] });\n}\n\nfunction tailPart(parts: ReadonlyArray<unknown>): Record<string, unknown> | null {\n const last = parts[parts.length - 1];\n if (last === undefined || last === null || typeof last !== 'object') return null;\n return last as Record<string, unknown>;\n}\n\n/**\n * Seconds without any streamed change before the row flips to a stall tone —\n * while the run waits on the model, never while a tool call is in flight\n * ({@link isRunActivityStalled}).\n */\nexport const RUN_ACTIVITY_STALL_SECONDS = 30;\n\n/**\n * Whether the run looks stalled: nothing has streamed for\n * {@link RUN_ACTIVITY_STALL_SECONDS} while it waits on the model. Never while a\n * tool call is in flight: the call's own row says it is running.\n */\nexport function isRunActivityStalled({\n seconds,\n toolCallInFlight,\n}: {\n seconds: number;\n toolCallInFlight: boolean;\n}): boolean {\n return !toolCallInFlight && seconds >= RUN_ACTIVITY_STALL_SECONDS;\n}\n\n/** Seconds before the row starts showing its elapsed counter. */\nexport const RUN_ACTIVITY_COUNTER_SECONDS = 3;\n\n/** The default verb when nothing more specific is known. */\nexport const RUN_ACTIVITY_DEFAULT_VERB = 'Thinking';\n\n/**\n * The counter alone for a card whose header already names the verb: `null`\n * until it is due, then \"11s\" / \"1m 05s\". The stall tone is the caller's\n * (a color and an announcement), not more words on the header row.\n */\nexport function runElapsedLabel(seconds: number): string | null {\n if (seconds < RUN_ACTIVITY_COUNTER_SECONDS) return null;\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n const remainder = seconds % 60;\n return `${minutes}m ${String(remainder).padStart(2, '0')}s`;\n}\n\n/**\n * The status line for `seconds` since the stream last moved: the verb with an\n * ellipsis and the elapsed counter once it is due (`runElapsedLabel`), or the\n * stall wording when the run looks `stalled` ({@link isRunActivityStalled}).\n */\nexport function runActivityLabel({\n seconds,\n verb = RUN_ACTIVITY_DEFAULT_VERB,\n stalled,\n}: {\n seconds: number;\n verb?: string;\n stalled: boolean;\n}): string {\n if (stalled) return `Still working — nothing new for ${seconds}s`;\n const elapsed = runElapsedLabel(seconds);\n return elapsed === null ? `${verb}…` : `${verb}… · ${elapsed}`;\n}\n"],"names":[],"mappings":";;AAcO,SAAS,eAAe,OAA8C;AAC3E,QAAM,OAAO,SAAS,KAAK;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,OAAO,KAAK,MAAM,MAAM,WAAY,KAAK,MAAM,IAAe;AAC3E,MAAI,SAAS,UAAU,SAAS,YAAa,QAAO;AACpD,QAAM,WAAW,OAAO,KAAK,UAAU,MAAM,WAAY,KAAK,UAAU,EAAa,SAAS;AAE9F,QAAM,UAAU,eAAe,IAAI,IAAI,IAAI;AAC3C,SAAO,GAAG,MAAM,MAAM,IAAI,IAAI,IAAI,QAAQ,IAAI,OAAO;AACvD;AAOO,SAAS,gBAAgB,OAA8C;AAC5E,QAAM,OAAO,SAAS,KAAK;AAC3B,MAAI,CAAC,QAAQ,CAAC,sBAAsB,IAAI,EAAG,QAAO;AAClD,QAAM,WAAW,KAAK,UAAU;AAChC,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,EAAG,QAAO;AAClE,SAAO,mBAAmB,QAAQ,EAAE;AACtC;AASO,SAAS,mBAAmB,OAAwC;AACzE,QAAM,OAAO,SAAS,KAAK;AAC3B,SAAO,SAAS,QAAQ,sBAAsB,IAAI;AACpD;AAEA,SAAS,sBAAsB,MAAwC;AACrE,SAAO,KAAK,MAAM,MAAM,eAAe,eAAe,IAAI;AAC5D;AAGA,SAAS,eAAe,MAAwC;AAC9D,SAAO,KAAK,QAAQ,MAAM,UAAa,2BAA2B,EAAE,UAAU,KAAK,UAAU,GAAG;AAClG;AAEA,SAAS,SAAS,OAA+D;AAC/E,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,UAAa,SAAS,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC5E,SAAO;AACT;AAOO,MAAM,6BAA6B;AAOnC,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AACF,GAGY;AACV,SAAO,CAAC,oBAAoB,WAAW;AACzC;AAGO,MAAM,+BAA+B;AAGrC,MAAM,4BAA4B;AAOlC,SAAS,gBAAgB,SAAgC;AAC9D,MAAI,UAAU,6BAA8B,QAAO;AACnD,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,YAAY,UAAU;AAC5B,SAAO,GAAG,OAAO,KAAK,OAAO,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAC1D;AAOO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAIW;AACT,MAAI,QAAS,QAAO,mCAAmC,OAAO;AAC9D,QAAM,UAAU,gBAAgB,OAAO;AACvC,SAAO,YAAY,OAAO,GAAG,IAAI,MAAM,GAAG,IAAI,OAAO,OAAO;AAC9D;"}
|
|
@@ -28,6 +28,10 @@ const DEFAULT_TOOLKIT_THEME = { color: "#919EAB" };
|
|
|
28
28
|
const DEFAULT_GROUPING = groupingFunction.createToolBasedGroupingFunction(toolkitMap.defaultGetToolGroupKey);
|
|
29
29
|
const SuperGroupingContext = react.createContext(null);
|
|
30
30
|
const SuperGroupingProvider = SuperGroupingContext.Provider;
|
|
31
|
+
const GroupToolUIsMountedContext = react.createContext(true);
|
|
32
|
+
function GroupToolUI({ children }) {
|
|
33
|
+
return react.useContext(GroupToolUIsMountedContext) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : null;
|
|
34
|
+
}
|
|
31
35
|
const CollapsibleGroup = ({
|
|
32
36
|
groupKey,
|
|
33
37
|
indices,
|
|
@@ -35,21 +39,21 @@ const CollapsibleGroup = ({
|
|
|
35
39
|
themes,
|
|
36
40
|
forceOpen = false,
|
|
37
41
|
compactWhileRunning = false,
|
|
42
|
+
mountHidden = false,
|
|
38
43
|
headerActions,
|
|
39
44
|
groupingFunction: groupingFunction2,
|
|
40
45
|
className
|
|
41
46
|
}) => {
|
|
42
|
-
var _a;
|
|
43
47
|
const panelId = react.useId();
|
|
44
48
|
const isMessageRunning = react$1.useAuiState((s) => {
|
|
45
|
-
var
|
|
46
|
-
return ((
|
|
49
|
+
var _a;
|
|
50
|
+
return ((_a = s.message.status) == null ? void 0 : _a.type) === "running";
|
|
47
51
|
});
|
|
48
52
|
const messageContent = react$1.useAuiState(
|
|
49
53
|
(s) => s.message.role === "assistant" ? s.message.content : void 0
|
|
50
54
|
);
|
|
51
|
-
const
|
|
52
|
-
const grouping = groupingFunction2 ??
|
|
55
|
+
const superGrouping = react.useContext(SuperGroupingContext);
|
|
56
|
+
const grouping = groupingFunction2 ?? (superGrouping == null ? void 0 : superGrouping.groupingFunction) ?? DEFAULT_GROUPING;
|
|
53
57
|
const lastIndex = indices.at(-1);
|
|
54
58
|
const isLastGroup = react.useMemo(() => {
|
|
55
59
|
if (!messageContent) return false;
|
|
@@ -59,8 +63,8 @@ const CollapsibleGroup = ({
|
|
|
59
63
|
const toolCallCount = react.useMemo(
|
|
60
64
|
() => messageContent ? indices.reduce(
|
|
61
65
|
(count, index) => {
|
|
62
|
-
var
|
|
63
|
-
return ((
|
|
66
|
+
var _a;
|
|
67
|
+
return ((_a = messageContent[index]) == null ? void 0 : _a.type) === "tool-call" ? count + 1 : count;
|
|
64
68
|
},
|
|
65
69
|
0
|
|
66
70
|
) : 0,
|
|
@@ -73,6 +77,9 @@ const CollapsibleGroup = ({
|
|
|
73
77
|
const [userOpened, setOpen] = react.useState();
|
|
74
78
|
const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;
|
|
75
79
|
const open = userOpened ?? (autoOpen || forceOpen);
|
|
80
|
+
const mountToolUIs = open || isMessageRunning || mountHidden || (superGrouping == null ? void 0 : superGrouping.mountHidden) === true;
|
|
81
|
+
const [toolUIsMounted, setToolUIsMounted] = react.useState(mountToolUIs);
|
|
82
|
+
if (mountToolUIs && !toolUIsMounted) setToolUIsMounted(true);
|
|
76
83
|
if (!groupKey || indices.length <= 1) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
77
84
|
const toggle = () => setOpen(!open);
|
|
78
85
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -144,7 +151,7 @@ const CollapsibleGroup = ({
|
|
|
144
151
|
open ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
145
152
|
),
|
|
146
153
|
style: { borderColor: `${theme.color}33` },
|
|
147
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2 px-5 py-4", children }) })
|
|
154
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(GroupToolUIsMountedContext.Provider, { value: toolUIsMounted, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2 px-5 py-4", children }) }) })
|
|
148
155
|
}
|
|
149
156
|
)
|
|
150
157
|
]
|
|
@@ -153,6 +160,7 @@ const CollapsibleGroup = ({
|
|
|
153
160
|
};
|
|
154
161
|
exports.CollapsibleGroup = CollapsibleGroup;
|
|
155
162
|
exports.DEFAULT_TOOLKIT_THEME = DEFAULT_TOOLKIT_THEME;
|
|
163
|
+
exports.GroupToolUI = GroupToolUI;
|
|
156
164
|
exports.SuperGroupingProvider = SuperGroupingProvider;
|
|
157
165
|
exports.TOOLKIT_THEMES = TOOLKIT_THEMES;
|
|
158
166
|
//# sourceMappingURL=CollapsibleGroup.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollapsibleGroup.cjs","sources":["../../../src/chat/super-grouping/CollapsibleGroup.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useId, useMemo, useState } from 'react';\nimport { cn } from '../../lib/utils';\nimport { createToolBasedGroupingFunction, type GroupingFunction } from './grouping-function';\nimport { defaultGetToolGroupKey } from './toolkit-map';\n\n/**\n * Per-toolkit theme: the brand color that tints the group header, and an\n * optional icon URL a host serves (Olympus ships toolkit SVGs; the SDK does\n * not bundle images, so it tints only).\n */\nexport type ToolkitTheme = {\n /** Hex brand color for this toolkit. */\n color: string;\n /** Absolute or root-relative URL of the toolkit icon, when the host has one. */\n icon?: string;\n};\n\n/** Brand colors keyed by toolkit display name — the group keys the grouping function emits. */\nexport const TOOLKIT_THEMES: Record<string, ToolkitTheme> = {\n Web: { color: '#7BCCFA' },\n Document: { color: '#4586F9' },\n 'Document (Word)': { color: '#4586F9' },\n Notebook: { color: '#FFAB00' },\n Python: { color: '#00A76F' },\n Spreadsheet: { color: '#00A76F' },\n Email: { color: '#B584FF' },\n AOP: { color: '#336DFF' },\n App: { color: '#7336F5' },\n Presentation: { color: '#F56B36' },\n 'User Interface': { color: '#7336F5' },\n Canvas: { color: '#002542' },\n Computer: { color: '#3E0042' },\n Database: { color: '#7336F5' },\n 'Chrome Browser': { color: '#4285F4' },\n};\n\nexport const DEFAULT_TOOLKIT_THEME: ToolkitTheme = { color: '#919EAB' };\n\nconst DEFAULT_GROUPING = createToolBasedGroupingFunction(defaultGetToolGroupKey);\n\n/**\n * The grouping in force for the message being rendered. `SuperGroupingCard`\n * provides it so each `CollapsibleGroup` can tell whether it is the last\n * grouped block (the one that opens by default while the run is live) without\n * being handed the function as a prop through `Unstable_PartsGrouped`.\n */\nconst SuperGroupingContext = createContext<{ groupingFunction: GroupingFunction } | null>(null);\n\nexport const SuperGroupingProvider = SuperGroupingContext.Provider;\n\nexport interface CollapsibleGroupProps {\n /** Group key from the grouping function (toolkit display name); `undefined` for ungrouped parts. */\n groupKey: string | undefined;\n /** Indices of the parts belonging to this group. */\n indices: number[];\n children?: ReactNode;\n /** Host toolkit themes (colors, icons), merged over the built-in colors. */\n themes?: Record<string, ToolkitTheme>;\n /**\n * Keep the group open regardless of the run state — a host rule such as\n * \"an unsubmitted email draft stays visible\". The user's own toggle still\n * wins once used.\n */\n forceOpen?: boolean;\n /**\n * Do not open the live group by default (screenshot-heavy browser-control\n * runs hold hundreds of actions); the user can still expand it.\n */\n compactWhileRunning?: boolean;\n /** Extra header controls, rendered as siblings of the toggle (never nested buttons). */\n headerActions?: ReactNode;\n /** The grouping used to find the last grouped block; defaults to the card's, then the SDK's. */\n groupingFunction?: GroupingFunction;\n className?: string;\n}\n\n/**\n * Per-toolkit collapsible block inside the grouped details panel — \"Using\n * Spreadsheet Toolkit · 4 tool calls\" — used by the SDK, Olympus, and the\n * Chrome extension. The last grouped block opens by default while the message\n * is running and settles closed; the user's toggle is remembered for the\n * mounted block.\n *\n * Selector discipline: one raw `s.message.content` read; every per-group\n * derivation (grouping, counts) runs in `useMemo` off it, so the grouping\n * pass happens once per content change instead of once per selector pass.\n */\nexport const CollapsibleGroup = ({\n groupKey,\n indices,\n children,\n themes,\n forceOpen = false,\n compactWhileRunning = false,\n headerActions,\n groupingFunction,\n className,\n}: CollapsibleGroupProps) => {\n const panelId = useId();\n const isMessageRunning = useAuiState((s) => s.message.status?.type === 'running');\n const messageContent = useAuiState((s) =>\n s.message.role === 'assistant' ? s.message.content : undefined,\n );\n const contextGrouping = useContext(SuperGroupingContext)?.groupingFunction;\n const grouping = groupingFunction ?? contextGrouping ?? DEFAULT_GROUPING;\n\n const lastIndex = indices.at(-1);\n const isLastGroup = useMemo(() => {\n if (!messageContent) return false;\n const lastGroup = grouping(messageContent)\n .filter((group) => group.indices.length > 1)\n .at(-1);\n return lastGroup?.indices.at(-1) === lastIndex;\n }, [messageContent, grouping, lastIndex]);\n\n const toolCallCount = useMemo(\n () =>\n messageContent\n ? indices.reduce(\n (count, index) => (messageContent[index]?.type === 'tool-call' ? count + 1 : count),\n 0,\n )\n : 0,\n [messageContent, indices],\n );\n\n const theme = useMemo<ToolkitTheme>(\n () => (groupKey ? { ...TOOLKIT_THEMES, ...themes }[groupKey] ?? DEFAULT_TOOLKIT_THEME : DEFAULT_TOOLKIT_THEME),\n [groupKey, themes],\n );\n\n const [userOpened, setOpen] = useState<boolean>();\n const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;\n const open = userOpened ?? (autoOpen || forceOpen);\n\n if (!groupKey || indices.length <= 1) return <>{children}</>;\n\n const toggle = () => setOpen(!open);\n\n return (\n <div\n data-slot=\"collapsible-group\"\n data-group-key={groupKey}\n className={cn('my-4 rounded-2xl border shadow-sm', className)}\n style={{\n borderColor: `${theme.color}33`,\n background: `linear-gradient(to right, ${theme.color}0d, transparent, ${theme.color}0d)`,\n }}\n >\n <div className=\"flex w-full items-center rounded-2xl\">\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex min-w-0 flex-1 items-center justify-between gap-3 rounded-2xl px-5 py-3 text-left transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n <span className=\"flex min-w-0 items-center gap-3 text-foreground/90\">\n {theme.icon ? (\n <img src={theme.icon} alt=\"\" aria-hidden=\"true\" className=\"size-5 shrink-0\" />\n ) : (\n <span\n className=\"inline-block size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: theme.color }}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"truncate text-sm font-semibold\">\n {isMessageRunning ? 'Using ' : 'Used '}\n {groupKey} Toolkit\n </span>\n </span>\n <span className=\"shrink-0 text-xs font-medium text-muted-foreground\">\n {toolCallCount} tool {toolCallCount === 1 ? 'call' : 'calls'}\n </span>\n </button>\n <div className=\"flex shrink-0 items-center gap-1 pr-3 pl-1\">\n {headerActions}\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={open ? 'Collapse toolkit group' : 'Expand toolkit group'}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n {open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}\n </button>\n </div>\n </div>\n\n <div\n id={panelId}\n className={cn(\n 'grid overflow-hidden border-t transition-[grid-template-rows] duration-200 ease-in-out motion-reduce:transition-none',\n open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',\n )}\n style={{ borderColor: `${theme.color}33` }}\n >\n <div className=\"overflow-hidden\">\n <div className=\"flex flex-col gap-2 px-5 py-4\">{children}</div>\n </div>\n </div>\n </div>\n );\n};\n"],"names":["createToolBasedGroupingFunction","defaultGetToolGroupKey","createContext","groupingFunction","useId","useAuiState","_a","useContext","useMemo","useState","jsxs","cn","jsx","ChevronUp","ChevronDown"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CollapsibleGroup.cjs","sources":["../../../src/chat/super-grouping/CollapsibleGroup.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useId, useMemo, useState } from 'react';\nimport { cn } from '../../lib/utils';\nimport { createToolBasedGroupingFunction, type GroupingFunction } from './grouping-function';\nimport { defaultGetToolGroupKey } from './toolkit-map';\n\n/**\n * Per-toolkit theme: the brand color that tints the group header, and an\n * optional icon URL a host serves (Olympus ships toolkit SVGs; the SDK does\n * not bundle images, so it tints only).\n */\nexport type ToolkitTheme = {\n /** Hex brand color for this toolkit. */\n color: string;\n /** Absolute or root-relative URL of the toolkit icon, when the host has one. */\n icon?: string;\n};\n\n/** Brand colors keyed by toolkit display name — the group keys the grouping function emits. */\nexport const TOOLKIT_THEMES: Record<string, ToolkitTheme> = {\n Web: { color: '#7BCCFA' },\n Document: { color: '#4586F9' },\n 'Document (Word)': { color: '#4586F9' },\n Notebook: { color: '#FFAB00' },\n Python: { color: '#00A76F' },\n Spreadsheet: { color: '#00A76F' },\n Email: { color: '#B584FF' },\n AOP: { color: '#336DFF' },\n App: { color: '#7336F5' },\n Presentation: { color: '#F56B36' },\n 'User Interface': { color: '#7336F5' },\n Canvas: { color: '#002542' },\n Computer: { color: '#3E0042' },\n Database: { color: '#7336F5' },\n 'Chrome Browser': { color: '#4285F4' },\n};\n\nexport const DEFAULT_TOOLKIT_THEME: ToolkitTheme = { color: '#919EAB' };\n\nconst DEFAULT_GROUPING = createToolBasedGroupingFunction(defaultGetToolGroupKey);\n\n/**\n * The grouping in force for the message being rendered. `SuperGroupingCard`\n * provides it so each `CollapsibleGroup` can tell whether it is the last\n * grouped block (the one that opens by default while the run is live) without\n * being handed the function as a prop through `GroupedMessageParts`, and\n * whether the card keeps its tool UIs mounted while hidden (`mountHidden`:\n * its turn is active, or a deep link targets it).\n */\nconst SuperGroupingContext = createContext<{\n groupingFunction: GroupingFunction;\n mountHidden?: boolean;\n} | null>(null);\n\nexport const SuperGroupingProvider = SuperGroupingContext.Provider;\n\n/** Whether the group a tool UI renders in has mounted its tool UIs; outside a group, always. */\nconst GroupToolUIsMountedContext = createContext(true);\n\n/**\n * A tool call's UI inside a `CollapsibleGroup`: rendered once the group first\n * needs its tool UIs (see `CollapsibleGroup`). `GroupedMessageParts` renders\n * every tool call through it.\n */\nexport function GroupToolUI({ children }: { children: ReactNode }) {\n return useContext(GroupToolUIsMountedContext) ? <>{children}</> : null;\n}\n\nexport interface CollapsibleGroupProps {\n /** Group key from the grouping function (toolkit display name); `undefined` for ungrouped parts. */\n groupKey: string | undefined;\n /** Indices of the parts belonging to this group. */\n indices: number[];\n children?: ReactNode;\n /** Host toolkit themes (colors, icons), merged over the built-in colors. */\n themes?: Record<string, ToolkitTheme>;\n /**\n * Keep the group open regardless of the run state — a host rule such as\n * \"an unsubmitted email draft stays visible\". The user's own toggle still\n * wins once used.\n */\n forceOpen?: boolean;\n /**\n * Do not open the live group by default (screenshot-heavy browser-control\n * runs hold hundreds of actions); the user can still expand it.\n */\n compactWhileRunning?: boolean;\n /**\n * Mount the tool UIs while collapsed (hidden), so a host that needs to find\n * one — a deep link into one of its tool calls — can. The card a group\n * renders in asks the same while its turn is active or its `mountDetails` is\n * set.\n */\n mountHidden?: boolean;\n /** Extra header controls, rendered as siblings of the toggle (never nested buttons). */\n headerActions?: ReactNode;\n /** The grouping used to find the last grouped block; defaults to the card's, then the SDK's. */\n groupingFunction?: GroupingFunction;\n className?: string;\n}\n\n/**\n * Per-toolkit collapsible block inside the grouped details panel — \"Using\n * Spreadsheet Toolkit · 4 tool calls\" — used by the SDK, Olympus, and the\n * Chrome extension. The last grouped block opens by default while the message\n * is running and settles closed; the user's toggle is remembered for the\n * mounted block.\n *\n * The group's tool UIs (the tool calls `GroupedMessageParts` renders through\n * `GroupToolUI`) mount the first time they are needed — the group opens, its\n * turn is running (live tool UIs run their effects), or a host asks\n * (`mountHidden`) — and stay mounted after. A finished turn's collapsed groups\n * mount none of their tool UIs, which a long run holds hundreds of. The rest\n * of the body renders from the start: the agent's text between the calls is\n * part of the message that selections, in-page find and links into the\n * message count.\n *\n * Selector discipline: one raw `s.message.content` read; every per-group\n * derivation (grouping, counts) runs in `useMemo` off it, so the grouping\n * pass happens once per content change instead of once per selector pass.\n */\nexport const CollapsibleGroup = ({\n groupKey,\n indices,\n children,\n themes,\n forceOpen = false,\n compactWhileRunning = false,\n mountHidden = false,\n headerActions,\n groupingFunction,\n className,\n}: CollapsibleGroupProps) => {\n const panelId = useId();\n const isMessageRunning = useAuiState((s) => s.message.status?.type === 'running');\n const messageContent = useAuiState((s) =>\n s.message.role === 'assistant' ? s.message.content : undefined,\n );\n const superGrouping = useContext(SuperGroupingContext);\n const grouping = groupingFunction ?? superGrouping?.groupingFunction ?? DEFAULT_GROUPING;\n\n const lastIndex = indices.at(-1);\n const isLastGroup = useMemo(() => {\n if (!messageContent) return false;\n const lastGroup = grouping(messageContent)\n .filter((group) => group.indices.length > 1)\n .at(-1);\n return lastGroup?.indices.at(-1) === lastIndex;\n }, [messageContent, grouping, lastIndex]);\n\n const toolCallCount = useMemo(\n () =>\n messageContent\n ? indices.reduce(\n (count, index) => (messageContent[index]?.type === 'tool-call' ? count + 1 : count),\n 0,\n )\n : 0,\n [messageContent, indices],\n );\n\n const theme = useMemo<ToolkitTheme>(\n () => (groupKey ? { ...TOOLKIT_THEMES, ...themes }[groupKey] ?? DEFAULT_TOOLKIT_THEME : DEFAULT_TOOLKIT_THEME),\n [groupKey, themes],\n );\n\n const [userOpened, setOpen] = useState<boolean>();\n const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;\n const open = userOpened ?? (autoOpen || forceOpen);\n\n const mountToolUIs =\n open || isMessageRunning || mountHidden || superGrouping?.mountHidden === true;\n const [toolUIsMounted, setToolUIsMounted] = useState(mountToolUIs);\n // Adjusting state during render is React's sanctioned derived-state pattern.\n if (mountToolUIs && !toolUIsMounted) setToolUIsMounted(true);\n\n if (!groupKey || indices.length <= 1) return <>{children}</>;\n\n const toggle = () => setOpen(!open);\n\n return (\n <div\n data-slot=\"collapsible-group\"\n data-group-key={groupKey}\n className={cn('my-4 rounded-2xl border shadow-sm', className)}\n style={{\n borderColor: `${theme.color}33`,\n background: `linear-gradient(to right, ${theme.color}0d, transparent, ${theme.color}0d)`,\n }}\n >\n <div className=\"flex w-full items-center rounded-2xl\">\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex min-w-0 flex-1 items-center justify-between gap-3 rounded-2xl px-5 py-3 text-left transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n <span className=\"flex min-w-0 items-center gap-3 text-foreground/90\">\n {theme.icon ? (\n <img src={theme.icon} alt=\"\" aria-hidden=\"true\" className=\"size-5 shrink-0\" />\n ) : (\n <span\n className=\"inline-block size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: theme.color }}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"truncate text-sm font-semibold\">\n {isMessageRunning ? 'Using ' : 'Used '}\n {groupKey} Toolkit\n </span>\n </span>\n <span className=\"shrink-0 text-xs font-medium text-muted-foreground\">\n {toolCallCount} tool {toolCallCount === 1 ? 'call' : 'calls'}\n </span>\n </button>\n <div className=\"flex shrink-0 items-center gap-1 pr-3 pl-1\">\n {headerActions}\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={open ? 'Collapse toolkit group' : 'Expand toolkit group'}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n {open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}\n </button>\n </div>\n </div>\n\n <div\n id={panelId}\n className={cn(\n 'grid overflow-hidden border-t transition-[grid-template-rows] duration-200 ease-in-out motion-reduce:transition-none',\n open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',\n )}\n style={{ borderColor: `${theme.color}33` }}\n >\n <div className=\"overflow-hidden\">\n <GroupToolUIsMountedContext.Provider value={toolUIsMounted}>\n <div className=\"flex flex-col gap-2 px-5 py-4\">{children}</div>\n </GroupToolUIsMountedContext.Provider>\n </div>\n </div>\n </div>\n );\n};\n"],"names":["createToolBasedGroupingFunction","defaultGetToolGroupKey","createContext","useContext","jsx","Fragment","groupingFunction","useId","useAuiState","useMemo","useState","jsxs","cn","ChevronUp","ChevronDown"],"mappings":"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"}
|
|
@@ -16,7 +16,16 @@ export declare const TOOLKIT_THEMES: Record<string, ToolkitTheme>;
|
|
|
16
16
|
export declare const DEFAULT_TOOLKIT_THEME: ToolkitTheme;
|
|
17
17
|
export declare const SuperGroupingProvider: import("react").Provider<{
|
|
18
18
|
groupingFunction: GroupingFunction;
|
|
19
|
+
mountHidden?: boolean;
|
|
19
20
|
} | null>;
|
|
21
|
+
/**
|
|
22
|
+
* A tool call's UI inside a `CollapsibleGroup`: rendered once the group first
|
|
23
|
+
* needs its tool UIs (see `CollapsibleGroup`). `GroupedMessageParts` renders
|
|
24
|
+
* every tool call through it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function GroupToolUI({ children }: {
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}): import("react").JSX.Element | null;
|
|
20
29
|
export interface CollapsibleGroupProps {
|
|
21
30
|
/** Group key from the grouping function (toolkit display name); `undefined` for ungrouped parts. */
|
|
22
31
|
groupKey: string | undefined;
|
|
@@ -36,6 +45,13 @@ export interface CollapsibleGroupProps {
|
|
|
36
45
|
* runs hold hundreds of actions); the user can still expand it.
|
|
37
46
|
*/
|
|
38
47
|
compactWhileRunning?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Mount the tool UIs while collapsed (hidden), so a host that needs to find
|
|
50
|
+
* one — a deep link into one of its tool calls — can. The card a group
|
|
51
|
+
* renders in asks the same while its turn is active or its `mountDetails` is
|
|
52
|
+
* set.
|
|
53
|
+
*/
|
|
54
|
+
mountHidden?: boolean;
|
|
39
55
|
/** Extra header controls, rendered as siblings of the toggle (never nested buttons). */
|
|
40
56
|
headerActions?: ReactNode;
|
|
41
57
|
/** The grouping used to find the last grouped block; defaults to the card's, then the SDK's. */
|
|
@@ -49,8 +65,17 @@ export interface CollapsibleGroupProps {
|
|
|
49
65
|
* is running and settles closed; the user's toggle is remembered for the
|
|
50
66
|
* mounted block.
|
|
51
67
|
*
|
|
68
|
+
* The group's tool UIs (the tool calls `GroupedMessageParts` renders through
|
|
69
|
+
* `GroupToolUI`) mount the first time they are needed — the group opens, its
|
|
70
|
+
* turn is running (live tool UIs run their effects), or a host asks
|
|
71
|
+
* (`mountHidden`) — and stay mounted after. A finished turn's collapsed groups
|
|
72
|
+
* mount none of their tool UIs, which a long run holds hundreds of. The rest
|
|
73
|
+
* of the body renders from the start: the agent's text between the calls is
|
|
74
|
+
* part of the message that selections, in-page find and links into the
|
|
75
|
+
* message count.
|
|
76
|
+
*
|
|
52
77
|
* Selector discipline: one raw `s.message.content` read; every per-group
|
|
53
78
|
* derivation (grouping, counts) runs in `useMemo` off it, so the grouping
|
|
54
79
|
* pass happens once per content change instead of once per selector pass.
|
|
55
80
|
*/
|
|
56
|
-
export declare const CollapsibleGroup: ({ groupKey, indices, children, themes, forceOpen, compactWhileRunning, headerActions, groupingFunction, className, }: CollapsibleGroupProps) => import("react").JSX.Element;
|
|
81
|
+
export declare const CollapsibleGroup: ({ groupKey, indices, children, themes, forceOpen, compactWhileRunning, mountHidden, headerActions, groupingFunction, className, }: CollapsibleGroupProps) => import("react").JSX.Element;
|
|
@@ -26,6 +26,10 @@ const DEFAULT_TOOLKIT_THEME = { color: "#919EAB" };
|
|
|
26
26
|
const DEFAULT_GROUPING = createToolBasedGroupingFunction(defaultGetToolGroupKey);
|
|
27
27
|
const SuperGroupingContext = createContext(null);
|
|
28
28
|
const SuperGroupingProvider = SuperGroupingContext.Provider;
|
|
29
|
+
const GroupToolUIsMountedContext = createContext(true);
|
|
30
|
+
function GroupToolUI({ children }) {
|
|
31
|
+
return useContext(GroupToolUIsMountedContext) ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
|
|
32
|
+
}
|
|
29
33
|
const CollapsibleGroup = ({
|
|
30
34
|
groupKey,
|
|
31
35
|
indices,
|
|
@@ -33,21 +37,21 @@ const CollapsibleGroup = ({
|
|
|
33
37
|
themes,
|
|
34
38
|
forceOpen = false,
|
|
35
39
|
compactWhileRunning = false,
|
|
40
|
+
mountHidden = false,
|
|
36
41
|
headerActions,
|
|
37
42
|
groupingFunction,
|
|
38
43
|
className
|
|
39
44
|
}) => {
|
|
40
|
-
var _a;
|
|
41
45
|
const panelId = useId();
|
|
42
46
|
const isMessageRunning = useAuiState((s) => {
|
|
43
|
-
var
|
|
44
|
-
return ((
|
|
47
|
+
var _a;
|
|
48
|
+
return ((_a = s.message.status) == null ? void 0 : _a.type) === "running";
|
|
45
49
|
});
|
|
46
50
|
const messageContent = useAuiState(
|
|
47
51
|
(s) => s.message.role === "assistant" ? s.message.content : void 0
|
|
48
52
|
);
|
|
49
|
-
const
|
|
50
|
-
const grouping = groupingFunction ??
|
|
53
|
+
const superGrouping = useContext(SuperGroupingContext);
|
|
54
|
+
const grouping = groupingFunction ?? (superGrouping == null ? void 0 : superGrouping.groupingFunction) ?? DEFAULT_GROUPING;
|
|
51
55
|
const lastIndex = indices.at(-1);
|
|
52
56
|
const isLastGroup = useMemo(() => {
|
|
53
57
|
if (!messageContent) return false;
|
|
@@ -57,8 +61,8 @@ const CollapsibleGroup = ({
|
|
|
57
61
|
const toolCallCount = useMemo(
|
|
58
62
|
() => messageContent ? indices.reduce(
|
|
59
63
|
(count, index) => {
|
|
60
|
-
var
|
|
61
|
-
return ((
|
|
64
|
+
var _a;
|
|
65
|
+
return ((_a = messageContent[index]) == null ? void 0 : _a.type) === "tool-call" ? count + 1 : count;
|
|
62
66
|
},
|
|
63
67
|
0
|
|
64
68
|
) : 0,
|
|
@@ -71,6 +75,9 @@ const CollapsibleGroup = ({
|
|
|
71
75
|
const [userOpened, setOpen] = useState();
|
|
72
76
|
const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;
|
|
73
77
|
const open = userOpened ?? (autoOpen || forceOpen);
|
|
78
|
+
const mountToolUIs = open || isMessageRunning || mountHidden || (superGrouping == null ? void 0 : superGrouping.mountHidden) === true;
|
|
79
|
+
const [toolUIsMounted, setToolUIsMounted] = useState(mountToolUIs);
|
|
80
|
+
if (mountToolUIs && !toolUIsMounted) setToolUIsMounted(true);
|
|
74
81
|
if (!groupKey || indices.length <= 1) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
75
82
|
const toggle = () => setOpen(!open);
|
|
76
83
|
return /* @__PURE__ */ jsxs(
|
|
@@ -142,7 +149,7 @@ const CollapsibleGroup = ({
|
|
|
142
149
|
open ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
143
150
|
),
|
|
144
151
|
style: { borderColor: `${theme.color}33` },
|
|
145
|
-
children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 px-5 py-4", children }) })
|
|
152
|
+
children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx(GroupToolUIsMountedContext.Provider, { value: toolUIsMounted, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 px-5 py-4", children }) }) })
|
|
146
153
|
}
|
|
147
154
|
)
|
|
148
155
|
]
|
|
@@ -152,6 +159,7 @@ const CollapsibleGroup = ({
|
|
|
152
159
|
export {
|
|
153
160
|
CollapsibleGroup,
|
|
154
161
|
DEFAULT_TOOLKIT_THEME,
|
|
162
|
+
GroupToolUI,
|
|
155
163
|
SuperGroupingProvider,
|
|
156
164
|
TOOLKIT_THEMES
|
|
157
165
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollapsibleGroup.js","sources":["../../../src/chat/super-grouping/CollapsibleGroup.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useId, useMemo, useState } from 'react';\nimport { cn } from '../../lib/utils';\nimport { createToolBasedGroupingFunction, type GroupingFunction } from './grouping-function';\nimport { defaultGetToolGroupKey } from './toolkit-map';\n\n/**\n * Per-toolkit theme: the brand color that tints the group header, and an\n * optional icon URL a host serves (Olympus ships toolkit SVGs; the SDK does\n * not bundle images, so it tints only).\n */\nexport type ToolkitTheme = {\n /** Hex brand color for this toolkit. */\n color: string;\n /** Absolute or root-relative URL of the toolkit icon, when the host has one. */\n icon?: string;\n};\n\n/** Brand colors keyed by toolkit display name — the group keys the grouping function emits. */\nexport const TOOLKIT_THEMES: Record<string, ToolkitTheme> = {\n Web: { color: '#7BCCFA' },\n Document: { color: '#4586F9' },\n 'Document (Word)': { color: '#4586F9' },\n Notebook: { color: '#FFAB00' },\n Python: { color: '#00A76F' },\n Spreadsheet: { color: '#00A76F' },\n Email: { color: '#B584FF' },\n AOP: { color: '#336DFF' },\n App: { color: '#7336F5' },\n Presentation: { color: '#F56B36' },\n 'User Interface': { color: '#7336F5' },\n Canvas: { color: '#002542' },\n Computer: { color: '#3E0042' },\n Database: { color: '#7336F5' },\n 'Chrome Browser': { color: '#4285F4' },\n};\n\nexport const DEFAULT_TOOLKIT_THEME: ToolkitTheme = { color: '#919EAB' };\n\nconst DEFAULT_GROUPING = createToolBasedGroupingFunction(defaultGetToolGroupKey);\n\n/**\n * The grouping in force for the message being rendered. `SuperGroupingCard`\n * provides it so each `CollapsibleGroup` can tell whether it is the last\n * grouped block (the one that opens by default while the run is live) without\n * being handed the function as a prop through `Unstable_PartsGrouped`.\n */\nconst SuperGroupingContext = createContext<{ groupingFunction: GroupingFunction } | null>(null);\n\nexport const SuperGroupingProvider = SuperGroupingContext.Provider;\n\nexport interface CollapsibleGroupProps {\n /** Group key from the grouping function (toolkit display name); `undefined` for ungrouped parts. */\n groupKey: string | undefined;\n /** Indices of the parts belonging to this group. */\n indices: number[];\n children?: ReactNode;\n /** Host toolkit themes (colors, icons), merged over the built-in colors. */\n themes?: Record<string, ToolkitTheme>;\n /**\n * Keep the group open regardless of the run state — a host rule such as\n * \"an unsubmitted email draft stays visible\". The user's own toggle still\n * wins once used.\n */\n forceOpen?: boolean;\n /**\n * Do not open the live group by default (screenshot-heavy browser-control\n * runs hold hundreds of actions); the user can still expand it.\n */\n compactWhileRunning?: boolean;\n /** Extra header controls, rendered as siblings of the toggle (never nested buttons). */\n headerActions?: ReactNode;\n /** The grouping used to find the last grouped block; defaults to the card's, then the SDK's. */\n groupingFunction?: GroupingFunction;\n className?: string;\n}\n\n/**\n * Per-toolkit collapsible block inside the grouped details panel — \"Using\n * Spreadsheet Toolkit · 4 tool calls\" — used by the SDK, Olympus, and the\n * Chrome extension. The last grouped block opens by default while the message\n * is running and settles closed; the user's toggle is remembered for the\n * mounted block.\n *\n * Selector discipline: one raw `s.message.content` read; every per-group\n * derivation (grouping, counts) runs in `useMemo` off it, so the grouping\n * pass happens once per content change instead of once per selector pass.\n */\nexport const CollapsibleGroup = ({\n groupKey,\n indices,\n children,\n themes,\n forceOpen = false,\n compactWhileRunning = false,\n headerActions,\n groupingFunction,\n className,\n}: CollapsibleGroupProps) => {\n const panelId = useId();\n const isMessageRunning = useAuiState((s) => s.message.status?.type === 'running');\n const messageContent = useAuiState((s) =>\n s.message.role === 'assistant' ? s.message.content : undefined,\n );\n const contextGrouping = useContext(SuperGroupingContext)?.groupingFunction;\n const grouping = groupingFunction ?? contextGrouping ?? DEFAULT_GROUPING;\n\n const lastIndex = indices.at(-1);\n const isLastGroup = useMemo(() => {\n if (!messageContent) return false;\n const lastGroup = grouping(messageContent)\n .filter((group) => group.indices.length > 1)\n .at(-1);\n return lastGroup?.indices.at(-1) === lastIndex;\n }, [messageContent, grouping, lastIndex]);\n\n const toolCallCount = useMemo(\n () =>\n messageContent\n ? indices.reduce(\n (count, index) => (messageContent[index]?.type === 'tool-call' ? count + 1 : count),\n 0,\n )\n : 0,\n [messageContent, indices],\n );\n\n const theme = useMemo<ToolkitTheme>(\n () => (groupKey ? { ...TOOLKIT_THEMES, ...themes }[groupKey] ?? DEFAULT_TOOLKIT_THEME : DEFAULT_TOOLKIT_THEME),\n [groupKey, themes],\n );\n\n const [userOpened, setOpen] = useState<boolean>();\n const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;\n const open = userOpened ?? (autoOpen || forceOpen);\n\n if (!groupKey || indices.length <= 1) return <>{children}</>;\n\n const toggle = () => setOpen(!open);\n\n return (\n <div\n data-slot=\"collapsible-group\"\n data-group-key={groupKey}\n className={cn('my-4 rounded-2xl border shadow-sm', className)}\n style={{\n borderColor: `${theme.color}33`,\n background: `linear-gradient(to right, ${theme.color}0d, transparent, ${theme.color}0d)`,\n }}\n >\n <div className=\"flex w-full items-center rounded-2xl\">\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex min-w-0 flex-1 items-center justify-between gap-3 rounded-2xl px-5 py-3 text-left transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n <span className=\"flex min-w-0 items-center gap-3 text-foreground/90\">\n {theme.icon ? (\n <img src={theme.icon} alt=\"\" aria-hidden=\"true\" className=\"size-5 shrink-0\" />\n ) : (\n <span\n className=\"inline-block size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: theme.color }}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"truncate text-sm font-semibold\">\n {isMessageRunning ? 'Using ' : 'Used '}\n {groupKey} Toolkit\n </span>\n </span>\n <span className=\"shrink-0 text-xs font-medium text-muted-foreground\">\n {toolCallCount} tool {toolCallCount === 1 ? 'call' : 'calls'}\n </span>\n </button>\n <div className=\"flex shrink-0 items-center gap-1 pr-3 pl-1\">\n {headerActions}\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={open ? 'Collapse toolkit group' : 'Expand toolkit group'}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n {open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}\n </button>\n </div>\n </div>\n\n <div\n id={panelId}\n className={cn(\n 'grid overflow-hidden border-t transition-[grid-template-rows] duration-200 ease-in-out motion-reduce:transition-none',\n open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',\n )}\n style={{ borderColor: `${theme.color}33` }}\n >\n <div className=\"overflow-hidden\">\n <div className=\"flex flex-col gap-2 px-5 py-4\">{children}</div>\n </div>\n </div>\n </div>\n );\n};\n"],"names":["_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CollapsibleGroup.js","sources":["../../../src/chat/super-grouping/CollapsibleGroup.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useId, useMemo, useState } from 'react';\nimport { cn } from '../../lib/utils';\nimport { createToolBasedGroupingFunction, type GroupingFunction } from './grouping-function';\nimport { defaultGetToolGroupKey } from './toolkit-map';\n\n/**\n * Per-toolkit theme: the brand color that tints the group header, and an\n * optional icon URL a host serves (Olympus ships toolkit SVGs; the SDK does\n * not bundle images, so it tints only).\n */\nexport type ToolkitTheme = {\n /** Hex brand color for this toolkit. */\n color: string;\n /** Absolute or root-relative URL of the toolkit icon, when the host has one. */\n icon?: string;\n};\n\n/** Brand colors keyed by toolkit display name — the group keys the grouping function emits. */\nexport const TOOLKIT_THEMES: Record<string, ToolkitTheme> = {\n Web: { color: '#7BCCFA' },\n Document: { color: '#4586F9' },\n 'Document (Word)': { color: '#4586F9' },\n Notebook: { color: '#FFAB00' },\n Python: { color: '#00A76F' },\n Spreadsheet: { color: '#00A76F' },\n Email: { color: '#B584FF' },\n AOP: { color: '#336DFF' },\n App: { color: '#7336F5' },\n Presentation: { color: '#F56B36' },\n 'User Interface': { color: '#7336F5' },\n Canvas: { color: '#002542' },\n Computer: { color: '#3E0042' },\n Database: { color: '#7336F5' },\n 'Chrome Browser': { color: '#4285F4' },\n};\n\nexport const DEFAULT_TOOLKIT_THEME: ToolkitTheme = { color: '#919EAB' };\n\nconst DEFAULT_GROUPING = createToolBasedGroupingFunction(defaultGetToolGroupKey);\n\n/**\n * The grouping in force for the message being rendered. `SuperGroupingCard`\n * provides it so each `CollapsibleGroup` can tell whether it is the last\n * grouped block (the one that opens by default while the run is live) without\n * being handed the function as a prop through `GroupedMessageParts`, and\n * whether the card keeps its tool UIs mounted while hidden (`mountHidden`:\n * its turn is active, or a deep link targets it).\n */\nconst SuperGroupingContext = createContext<{\n groupingFunction: GroupingFunction;\n mountHidden?: boolean;\n} | null>(null);\n\nexport const SuperGroupingProvider = SuperGroupingContext.Provider;\n\n/** Whether the group a tool UI renders in has mounted its tool UIs; outside a group, always. */\nconst GroupToolUIsMountedContext = createContext(true);\n\n/**\n * A tool call's UI inside a `CollapsibleGroup`: rendered once the group first\n * needs its tool UIs (see `CollapsibleGroup`). `GroupedMessageParts` renders\n * every tool call through it.\n */\nexport function GroupToolUI({ children }: { children: ReactNode }) {\n return useContext(GroupToolUIsMountedContext) ? <>{children}</> : null;\n}\n\nexport interface CollapsibleGroupProps {\n /** Group key from the grouping function (toolkit display name); `undefined` for ungrouped parts. */\n groupKey: string | undefined;\n /** Indices of the parts belonging to this group. */\n indices: number[];\n children?: ReactNode;\n /** Host toolkit themes (colors, icons), merged over the built-in colors. */\n themes?: Record<string, ToolkitTheme>;\n /**\n * Keep the group open regardless of the run state — a host rule such as\n * \"an unsubmitted email draft stays visible\". The user's own toggle still\n * wins once used.\n */\n forceOpen?: boolean;\n /**\n * Do not open the live group by default (screenshot-heavy browser-control\n * runs hold hundreds of actions); the user can still expand it.\n */\n compactWhileRunning?: boolean;\n /**\n * Mount the tool UIs while collapsed (hidden), so a host that needs to find\n * one — a deep link into one of its tool calls — can. The card a group\n * renders in asks the same while its turn is active or its `mountDetails` is\n * set.\n */\n mountHidden?: boolean;\n /** Extra header controls, rendered as siblings of the toggle (never nested buttons). */\n headerActions?: ReactNode;\n /** The grouping used to find the last grouped block; defaults to the card's, then the SDK's. */\n groupingFunction?: GroupingFunction;\n className?: string;\n}\n\n/**\n * Per-toolkit collapsible block inside the grouped details panel — \"Using\n * Spreadsheet Toolkit · 4 tool calls\" — used by the SDK, Olympus, and the\n * Chrome extension. The last grouped block opens by default while the message\n * is running and settles closed; the user's toggle is remembered for the\n * mounted block.\n *\n * The group's tool UIs (the tool calls `GroupedMessageParts` renders through\n * `GroupToolUI`) mount the first time they are needed — the group opens, its\n * turn is running (live tool UIs run their effects), or a host asks\n * (`mountHidden`) — and stay mounted after. A finished turn's collapsed groups\n * mount none of their tool UIs, which a long run holds hundreds of. The rest\n * of the body renders from the start: the agent's text between the calls is\n * part of the message that selections, in-page find and links into the\n * message count.\n *\n * Selector discipline: one raw `s.message.content` read; every per-group\n * derivation (grouping, counts) runs in `useMemo` off it, so the grouping\n * pass happens once per content change instead of once per selector pass.\n */\nexport const CollapsibleGroup = ({\n groupKey,\n indices,\n children,\n themes,\n forceOpen = false,\n compactWhileRunning = false,\n mountHidden = false,\n headerActions,\n groupingFunction,\n className,\n}: CollapsibleGroupProps) => {\n const panelId = useId();\n const isMessageRunning = useAuiState((s) => s.message.status?.type === 'running');\n const messageContent = useAuiState((s) =>\n s.message.role === 'assistant' ? s.message.content : undefined,\n );\n const superGrouping = useContext(SuperGroupingContext);\n const grouping = groupingFunction ?? superGrouping?.groupingFunction ?? DEFAULT_GROUPING;\n\n const lastIndex = indices.at(-1);\n const isLastGroup = useMemo(() => {\n if (!messageContent) return false;\n const lastGroup = grouping(messageContent)\n .filter((group) => group.indices.length > 1)\n .at(-1);\n return lastGroup?.indices.at(-1) === lastIndex;\n }, [messageContent, grouping, lastIndex]);\n\n const toolCallCount = useMemo(\n () =>\n messageContent\n ? indices.reduce(\n (count, index) => (messageContent[index]?.type === 'tool-call' ? count + 1 : count),\n 0,\n )\n : 0,\n [messageContent, indices],\n );\n\n const theme = useMemo<ToolkitTheme>(\n () => (groupKey ? { ...TOOLKIT_THEMES, ...themes }[groupKey] ?? DEFAULT_TOOLKIT_THEME : DEFAULT_TOOLKIT_THEME),\n [groupKey, themes],\n );\n\n const [userOpened, setOpen] = useState<boolean>();\n const autoOpen = isLastGroup && isMessageRunning && !compactWhileRunning;\n const open = userOpened ?? (autoOpen || forceOpen);\n\n const mountToolUIs =\n open || isMessageRunning || mountHidden || superGrouping?.mountHidden === true;\n const [toolUIsMounted, setToolUIsMounted] = useState(mountToolUIs);\n // Adjusting state during render is React's sanctioned derived-state pattern.\n if (mountToolUIs && !toolUIsMounted) setToolUIsMounted(true);\n\n if (!groupKey || indices.length <= 1) return <>{children}</>;\n\n const toggle = () => setOpen(!open);\n\n return (\n <div\n data-slot=\"collapsible-group\"\n data-group-key={groupKey}\n className={cn('my-4 rounded-2xl border shadow-sm', className)}\n style={{\n borderColor: `${theme.color}33`,\n background: `linear-gradient(to right, ${theme.color}0d, transparent, ${theme.color}0d)`,\n }}\n >\n <div className=\"flex w-full items-center rounded-2xl\">\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex min-w-0 flex-1 items-center justify-between gap-3 rounded-2xl px-5 py-3 text-left transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n <span className=\"flex min-w-0 items-center gap-3 text-foreground/90\">\n {theme.icon ? (\n <img src={theme.icon} alt=\"\" aria-hidden=\"true\" className=\"size-5 shrink-0\" />\n ) : (\n <span\n className=\"inline-block size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: theme.color }}\n aria-hidden=\"true\"\n />\n )}\n <span className=\"truncate text-sm font-semibold\">\n {isMessageRunning ? 'Using ' : 'Used '}\n {groupKey} Toolkit\n </span>\n </span>\n <span className=\"shrink-0 text-xs font-medium text-muted-foreground\">\n {toolCallCount} tool {toolCallCount === 1 ? 'call' : 'calls'}\n </span>\n </button>\n <div className=\"flex shrink-0 items-center gap-1 pr-3 pl-1\">\n {headerActions}\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={open ? 'Collapse toolkit group' : 'Expand toolkit group'}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:bg-black/5 dark:hover:bg-white/5\"\n >\n {open ? <ChevronUp size={16} /> : <ChevronDown size={16} />}\n </button>\n </div>\n </div>\n\n <div\n id={panelId}\n className={cn(\n 'grid overflow-hidden border-t transition-[grid-template-rows] duration-200 ease-in-out motion-reduce:transition-none',\n open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',\n )}\n style={{ borderColor: `${theme.color}33` }}\n >\n <div className=\"overflow-hidden\">\n <GroupToolUIsMountedContext.Provider value={toolUIsMounted}>\n <div className=\"flex flex-col gap-2 px-5 py-4\">{children}</div>\n </GroupToolUIsMountedContext.Provider>\n </div>\n </div>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("@assistant-ui/react");
|
|
5
|
+
const react$1 = require("react");
|
|
6
|
+
const CollapsibleGroup = require("./CollapsibleGroup.cjs");
|
|
7
|
+
const groupedPartKeys = require("./grouped-part-keys.cjs");
|
|
8
|
+
const NO_GROUPS = [];
|
|
9
|
+
const DefaultText = () => /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { whiteSpace: "pre-line" }, children: [
|
|
10
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.MessagePartPrimitive.Text, {}),
|
|
11
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.MessagePartPrimitive.InProgress, { children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: "revert" }, children: " ●" }) })
|
|
12
|
+
] });
|
|
13
|
+
const DefaultImage = () => /* @__PURE__ */ jsxRuntime.jsx(react.MessagePartPrimitive.Image, {});
|
|
14
|
+
const PassThroughGroup = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
15
|
+
function useGroupedPartKeys({
|
|
16
|
+
parts,
|
|
17
|
+
groups
|
|
18
|
+
}) {
|
|
19
|
+
const [keys, setKeys] = react$1.useState(null);
|
|
20
|
+
if (keys !== null && keys.parts === parts && keys.groups === groups) return keys;
|
|
21
|
+
const next = groupedPartKeys.carryGroupedPartKeys({ previous: keys, parts, groups });
|
|
22
|
+
setKeys(next);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
const GroupedMessageParts = ({ groupingFunction, components }) => {
|
|
26
|
+
const parts = react.useAuiState((s) => s.message.parts);
|
|
27
|
+
const groups = react$1.useMemo(
|
|
28
|
+
() => parts.length === 0 ? NO_GROUPS : groupingFunction(parts),
|
|
29
|
+
[parts, groupingFunction]
|
|
30
|
+
);
|
|
31
|
+
const keys = useGroupedPartKeys({ parts, groups });
|
|
32
|
+
const elements = react$1.useMemo(() => {
|
|
33
|
+
const leafComponents = {
|
|
34
|
+
...components,
|
|
35
|
+
Text: (components == null ? void 0 : components.Text) ?? DefaultText,
|
|
36
|
+
Image: (components == null ? void 0 : components.Image) ?? DefaultImage
|
|
37
|
+
};
|
|
38
|
+
const Group = (components == null ? void 0 : components.Group) ?? PassThroughGroup;
|
|
39
|
+
return groups.map((group, groupIndex) => /* @__PURE__ */ jsxRuntime.jsx(Group, { groupKey: group.groupKey, indices: group.indices, children: group.indices.map(
|
|
40
|
+
(partIndex) => {
|
|
41
|
+
var _a;
|
|
42
|
+
return ((_a = parts[partIndex]) == null ? void 0 : _a.type) === "tool-call" ? /* @__PURE__ */ jsxRuntime.jsx(CollapsibleGroup.GroupToolUI, { children: /* @__PURE__ */ jsxRuntime.jsx(react.MessagePrimitive.PartByIndex, { index: partIndex, components: leafComponents }) }, keys.partKeys[partIndex]) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
43
|
+
react.MessagePrimitive.PartByIndex,
|
|
44
|
+
{
|
|
45
|
+
index: partIndex,
|
|
46
|
+
components: leafComponents
|
|
47
|
+
},
|
|
48
|
+
keys.partKeys[partIndex]
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
) }, keys.groupKeys[groupIndex]));
|
|
52
|
+
}, [components, groups, keys, parts]);
|
|
53
|
+
if (parts.length === 0) {
|
|
54
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.MessagePrimitive.Unstable_PartsGrouped, { groupingFunction, components });
|
|
55
|
+
}
|
|
56
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: elements });
|
|
57
|
+
};
|
|
58
|
+
exports.GroupedMessageParts = GroupedMessageParts;
|
|
59
|
+
//# sourceMappingURL=GroupedMessageParts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupedMessageParts.cjs","sources":["../../../src/chat/super-grouping/GroupedMessageParts.tsx"],"sourcesContent":["import {\n type ImageMessagePartComponent,\n MessagePartPrimitive,\n MessagePrimitive,\n type TextMessagePartComponent,\n useAuiState,\n} from '@assistant-ui/react';\nimport { type ComponentProps, type FC, type ReactNode, useMemo, useState } from 'react';\nimport { GroupToolUI } from './CollapsibleGroup';\nimport { carryGroupedPartKeys, type GroupedPartKeys, type KeyablePart } from './grouped-part-keys';\nimport type { GroupingFunction, MessagePartGroup } from './grouping-function';\n\nexport interface GroupedMessagePartsProps {\n /** Groups the message's parts; each group renders through `components.Group`. */\n groupingFunction: GroupingFunction;\n /** The part and group renderers, as `MessagePrimitive.Unstable_PartsGrouped` takes them. */\n components: ComponentProps<typeof MessagePrimitive.Unstable_PartsGrouped>['components'];\n}\n\nconst NO_GROUPS: readonly MessagePartGroup[] = [];\n\n/** assistant-ui's grouped-parts defaults for the leaves a host does not render. */\nconst DefaultText: TextMessagePartComponent = () => (\n <p style={{ whiteSpace: 'pre-line' }}>\n <MessagePartPrimitive.Text />\n <MessagePartPrimitive.InProgress>\n <span style={{ fontFamily: 'revert' }}>{' ●'}</span>\n </MessagePartPrimitive.InProgress>\n </p>\n);\nconst DefaultImage: ImageMessagePartComponent = () => <MessagePartPrimitive.Image />;\nconst PassThroughGroup = ({ children }: { children?: ReactNode }) => <>{children}</>;\n\n/**\n * The keys of the last render, carried into this one (see\n * {@link carryGroupedPartKeys}). Kept as state, React's way of storing\n * information from previous renders: a render whose parts or groups changed\n * stores the new keys, and React renders again with them before committing.\n */\nfunction useGroupedPartKeys({\n parts,\n groups,\n}: {\n parts: readonly KeyablePart[];\n groups: readonly MessagePartGroup[];\n}): GroupedPartKeys {\n const [keys, setKeys] = useState<GroupedPartKeys | null>(null);\n if (keys !== null && keys.parts === parts && keys.groups === groups) return keys;\n const next = carryGroupedPartKeys({ previous: keys, parts, groups });\n setKeys(next);\n return next;\n}\n\n/**\n * A message's parts grouped by `groupingFunction`, each group through\n * `components.Group` — `MessagePrimitive.Unstable_PartsGrouped` with two\n * differences:\n *\n * - What is mounted stays mounted when the message grows at its head (a page\n * of older steps folding into the first loaded turn) as well as at its tail:\n * groups and parts keep their React keys across renders (see\n * {@link carryGroupedPartKeys}), where assistant-ui keys a group by its\n * first part and a text part by its index.\n * - A tool call's UI renders through {@link GroupToolUI}, so a collapsed\n * `CollapsibleGroup` of a finished turn mounts it only when first needed.\n *\n * An empty message renders as `Unstable_PartsGrouped` renders it.\n */\nexport const GroupedMessageParts: FC<GroupedMessagePartsProps> = ({ groupingFunction, components }) => {\n const parts = useAuiState((s) => s.message.parts);\n const groups = useMemo(\n () => (parts.length === 0 ? NO_GROUPS : groupingFunction(parts)),\n [parts, groupingFunction],\n );\n const keys = useGroupedPartKeys({ parts, groups });\n\n const elements = useMemo(() => {\n const leafComponents = {\n ...components,\n Text: components?.Text ?? DefaultText,\n Image: components?.Image ?? DefaultImage,\n };\n const Group = components?.Group ?? PassThroughGroup;\n return groups.map((group, groupIndex) => (\n <Group key={keys.groupKeys[groupIndex]} groupKey={group.groupKey} indices={group.indices}>\n {group.indices.map((partIndex) =>\n parts[partIndex]?.type === 'tool-call' ? (\n <GroupToolUI key={keys.partKeys[partIndex]}>\n <MessagePrimitive.PartByIndex index={partIndex} components={leafComponents} />\n </GroupToolUI>\n ) : (\n <MessagePrimitive.PartByIndex\n key={keys.partKeys[partIndex]}\n index={partIndex}\n components={leafComponents}\n />\n ),\n )}\n </Group>\n ));\n }, [components, groups, keys, parts]);\n\n if (parts.length === 0) {\n return <MessagePrimitive.Unstable_PartsGrouped groupingFunction={groupingFunction} components={components} />;\n }\n return <>{elements}</>;\n};\n"],"names":["jsxs","jsx","MessagePartPrimitive","useState","carryGroupedPartKeys","useAuiState","useMemo","GroupToolUI","MessagePrimitive"],"mappings":";;;;;;;AAmBA,MAAM,YAAyC,CAAA;AAG/C,MAAM,cAAwC,MAC5CA,2BAAAA,KAAC,KAAA,EAAE,OAAO,EAAE,YAAY,cACtB,UAAA;AAAA,EAAAC,+BAACC,MAAAA,qBAAqB,MAArB,EAA0B;AAAA,EAC3BD,2BAAAA,IAACC,MAAAA,qBAAqB,YAArB,EACC,UAAAD,2BAAAA,IAAC,QAAA,EAAK,OAAO,EAAE,YAAY,SAAA,GAAa,UAAA,KAAA,CAAK,EAAA,CAC/C;AAAA,GACF;AAEF,MAAM,eAA0C,MAAMA,2BAAAA,IAACC,2BAAqB,OAArB,CAAA,CAA2B;AAClF,MAAM,mBAAmB,CAAC,EAAE,SAAA,4DAA4C,UAAS;AAQjF,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AACF,GAGoB;AAClB,QAAM,CAAC,MAAM,OAAO,IAAIC,QAAAA,SAAiC,IAAI;AAC7D,MAAI,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,WAAW,OAAQ,QAAO;AAC5E,QAAM,OAAOC,gBAAAA,qBAAqB,EAAE,UAAU,MAAM,OAAO,QAAQ;AACnE,UAAQ,IAAI;AACZ,SAAO;AACT;AAiBO,MAAM,sBAAoD,CAAC,EAAE,kBAAkB,iBAAiB;AACrG,QAAM,QAAQC,MAAAA,YAAY,CAAC,MAAM,EAAE,QAAQ,KAAK;AAChD,QAAM,SAASC,QAAAA;AAAAA,IACb,MAAO,MAAM,WAAW,IAAI,YAAY,iBAAiB,KAAK;AAAA,IAC9D,CAAC,OAAO,gBAAgB;AAAA,EAAA;AAE1B,QAAM,OAAO,mBAAmB,EAAE,OAAO,QAAQ;AAEjD,QAAM,WAAWA,QAAAA,QAAQ,MAAM;AAC7B,UAAM,iBAAiB;AAAA,MACrB,GAAG;AAAA,MACH,OAAM,yCAAY,SAAQ;AAAA,MAC1B,QAAO,yCAAY,UAAS;AAAA,IAAA;AAE9B,UAAM,SAAQ,yCAAY,UAAS;AACnC,WAAO,OAAO,IAAI,CAAC,OAAO,eACxBL,2BAAAA,IAAC,OAAA,EAAuC,UAAU,MAAM,UAAU,SAAS,MAAM,SAC9E,gBAAM,QAAQ;AAAA,MAAI,CAAC;;AAClB,4BAAM,SAAS,MAAf,mBAAkB,UAAS,cACzBA,2BAAAA,IAACM,iBAAAA,aAAA,EACC,UAAAN,+BAACO,MAAAA,iBAAiB,aAAjB,EAA6B,OAAO,WAAW,YAAY,eAAA,CAAgB,KAD5D,KAAK,SAAS,SAAS,CAEzC,IAEAP,2BAAAA;AAAAA,UAACO,MAAAA,iBAAiB;AAAA,UAAjB;AAAA,YAEC,OAAO;AAAA,YACP,YAAY;AAAA,UAAA;AAAA,UAFP,KAAK,SAAS,SAAS;AAAA,QAAA;AAAA;AAAA,IAG9B,EAEJ,GAbU,KAAK,UAAU,UAAU,CAcrC,CACD;AAAA,EACH,GAAG,CAAC,YAAY,QAAQ,MAAM,KAAK,CAAC;AAEpC,MAAI,MAAM,WAAW,GAAG;AACtB,WAAOP,2BAAAA,IAACO,MAAAA,iBAAiB,uBAAjB,EAAuC,kBAAoC,WAAA,CAAwB;AAAA,EAC7G;AACA,+DAAU,UAAA,SAAA,CAAS;AACrB;;"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MessagePrimitive } from '@assistant-ui/react';
|
|
2
|
+
import { type ComponentProps, type FC } from 'react';
|
|
3
|
+
import type { GroupingFunction } from './grouping-function';
|
|
4
|
+
export interface GroupedMessagePartsProps {
|
|
5
|
+
/** Groups the message's parts; each group renders through `components.Group`. */
|
|
6
|
+
groupingFunction: GroupingFunction;
|
|
7
|
+
/** The part and group renderers, as `MessagePrimitive.Unstable_PartsGrouped` takes them. */
|
|
8
|
+
components: ComponentProps<typeof MessagePrimitive.Unstable_PartsGrouped>['components'];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A message's parts grouped by `groupingFunction`, each group through
|
|
12
|
+
* `components.Group` — `MessagePrimitive.Unstable_PartsGrouped` with two
|
|
13
|
+
* differences:
|
|
14
|
+
*
|
|
15
|
+
* - What is mounted stays mounted when the message grows at its head (a page
|
|
16
|
+
* of older steps folding into the first loaded turn) as well as at its tail:
|
|
17
|
+
* groups and parts keep their React keys across renders (see
|
|
18
|
+
* {@link carryGroupedPartKeys}), where assistant-ui keys a group by its
|
|
19
|
+
* first part and a text part by its index.
|
|
20
|
+
* - A tool call's UI renders through {@link GroupToolUI}, so a collapsed
|
|
21
|
+
* `CollapsibleGroup` of a finished turn mounts it only when first needed.
|
|
22
|
+
*
|
|
23
|
+
* An empty message renders as `Unstable_PartsGrouped` renders it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const GroupedMessageParts: FC<GroupedMessagePartsProps>;
|