@athenaintel/react 0.42.2 → 0.43.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.
Files changed (60) hide show
  1. package/README.md +10 -0
  2. package/dist/agent-runtime/src/conversation-list-groups.d.ts +74 -0
  3. package/dist/agent-runtime/src/index.d.ts +1 -0
  4. package/dist/agent-runtime/src/tool-metadata.cjs +0 -8
  5. package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
  6. package/dist/agent-runtime/src/tool-metadata.js +0 -8
  7. package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
  8. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +30 -15
  9. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  10. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +8 -0
  11. package/dist/agent-runtime/src/use-deep-agent-voice.js +30 -15
  12. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  13. package/dist/chat/super-grouping/SuperGroupingCard.cjs +8 -2
  14. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  15. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +6 -1
  16. package/dist/chat/super-grouping/SuperGroupingCard.js +8 -2
  17. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  18. package/dist/chat/super-grouping/ToolTimeline.cjs +27 -0
  19. package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -1
  20. package/dist/chat/super-grouping/ToolTimeline.d.ts +5 -1
  21. package/dist/chat/super-grouping/ToolTimeline.js +27 -0
  22. package/dist/chat/super-grouping/ToolTimeline.js.map +1 -1
  23. package/dist/chat/super-grouping/index.d.ts +1 -0
  24. package/dist/chat/super-grouping/timeline-step.cjs +61 -2
  25. package/dist/chat/super-grouping/timeline-step.cjs.map +1 -1
  26. package/dist/chat/super-grouping/timeline-step.d.ts +13 -1
  27. package/dist/chat/super-grouping/timeline-step.js +61 -2
  28. package/dist/chat/super-grouping/timeline-step.js.map +1 -1
  29. package/dist/chat-ui/mentions/mention-chip.cjs +144 -35
  30. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  31. package/dist/chat-ui/mentions/mention-chip.d.ts +17 -1
  32. package/dist/chat-ui/mentions/mention-chip.js +146 -37
  33. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  34. package/dist/chat-ui/mentions/use-mention-asset.cjs +140 -0
  35. package/dist/chat-ui/mentions/use-mention-asset.cjs.map +1 -0
  36. package/dist/chat-ui/mentions/use-mention-asset.d.ts +16 -0
  37. package/dist/chat-ui/mentions/use-mention-asset.js +140 -0
  38. package/dist/chat-ui/mentions/use-mention-asset.js.map +1 -0
  39. package/dist/chat-ui/rich-text/chat-link.cjs +5 -4
  40. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
  41. package/dist/chat-ui/rich-text/chat-link.d.ts +2 -3
  42. package/dist/chat-ui/rich-text/chat-link.js +6 -5
  43. package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
  44. package/dist/chat-ui/rich-text/chat-node-views.cjs +1 -0
  45. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
  46. package/dist/chat-ui/rich-text/chat-node-views.js +1 -0
  47. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
  48. package/dist/react/package.json.cjs +1 -1
  49. package/dist/react/package.json.js +1 -1
  50. package/dist/references/src/tool-option-schemas.d.ts +0 -34
  51. package/dist/styles.css +1 -1
  52. package/dist/threads/statewire-thread-list.cjs +6 -2
  53. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  54. package/dist/threads/statewire-thread-list.js +6 -2
  55. package/dist/threads/statewire-thread-list.js.map +1 -1
  56. package/dist/tools/tool-summary.cjs +0 -2
  57. package/dist/tools/tool-summary.cjs.map +1 -1
  58. package/dist/tools/tool-summary.js +0 -2
  59. package/dist/tools/tool-summary.js.map +1 -1
  60. package/package.json +1 -1
@@ -15,6 +15,7 @@ export { GroupedMessageParts, type GroupedMessagePartsProps } from './GroupedMes
15
15
  export { createToolBasedGroupingFunction, type GroupablePart, type GroupingFunction, type MessagePartGroup, } from './grouping-function';
16
16
  export { resetRememberedExpansionsForTests, SuperGroupingCard, type SuperGroupingCardProps, SuperGroupingFinalText, useActivityExpansion, } from './SuperGroupingCard';
17
17
  export { SuperGroupingTranscript, type SuperGroupingTranscriptProps } from './SuperGroupingTranscript';
18
+ export type { TimelineStep } from './timeline-step';
18
19
  export { scopeGroupingToSegment, type SegmentablePart, type SegmentSummary, splitTurnSegments, summarizeSegment, type TurnSegment, } from './turn-segments';
19
20
  export { formatWorkDuration, readTurnTiming, type TurnTiming, type TurnTimingSources, workedDurationMs, } from './turn-timing';
20
21
  export { defaultGetToolGroupKey, type GetToolGroupKey, type ToolGroupKeyOptions } from './toolkit-map';
@@ -30,7 +30,8 @@ function toTimelineStep({
30
30
  item,
31
31
  index,
32
32
  resolveToolDisplayName,
33
- assetTitles
33
+ assetTitles,
34
+ withSourceUrls = false
34
35
  }) {
35
36
  const id = item.id ?? `${item.kind}-${index}`;
36
37
  if (item.kind !== "tool") {
@@ -44,8 +45,13 @@ function toTimelineStep({
44
45
  ...summary.detail ? { detail: summary.detail } : {},
45
46
  ...summary.meta ? { chip: summary.meta } : {},
46
47
  state: item.state,
47
- kind: item.kind
48
+ kind: item.kind,
49
+ toolName: item.toolName
48
50
  };
51
+ if (withSourceUrls && item.state === "complete") {
52
+ const sourceUrls = collectSourceUrls(item.result);
53
+ if (sourceUrls.length > 0) step.sourceUrls = sourceUrls;
54
+ }
49
55
  const args = item.args;
50
56
  if (!args || !item.toolName.startsWith("chrome_")) return step;
51
57
  const tabId = args.tabId ?? args.tab_id;
@@ -69,6 +75,59 @@ function visibleTimelineSteps({ steps, limit }) {
69
75
  (step, index) => index >= steps.length - safeLimit || step.state === "running" || step.state === "waiting"
70
76
  );
71
77
  }
78
+ const SOURCE_URL_KEYS = ["url", "link", "href"];
79
+ const MAX_SOURCE_URLS = 3;
80
+ const MAX_SOURCE_DEPTH = 4;
81
+ const MAX_SOURCE_RESULT_CHARS = 256 * 1024;
82
+ function parseJson(text) {
83
+ if (text.length > MAX_SOURCE_RESULT_CHARS) return void 0;
84
+ try {
85
+ return JSON.parse(text);
86
+ } catch {
87
+ return void 0;
88
+ }
89
+ }
90
+ function parseResult(result) {
91
+ if (typeof result === "string") return parseJson(result);
92
+ if (Array.isArray(result) && result.length > 0 && result.every(isTextBlock)) {
93
+ return parseJson(result.map((block) => block.text).join(""));
94
+ }
95
+ return result;
96
+ }
97
+ function isTextBlock(value) {
98
+ if (typeof value !== "object" || value === null) return false;
99
+ const block = value;
100
+ return block.type === "text" && typeof block.text === "string";
101
+ }
102
+ function collectSourceUrls(result) {
103
+ const urls = [];
104
+ const hosts = /* @__PURE__ */ new Set();
105
+ const visit = ({ value, depth }) => {
106
+ if (urls.length >= MAX_SOURCE_URLS || depth > MAX_SOURCE_DEPTH || value === null) return;
107
+ if (Array.isArray(value)) {
108
+ for (const entry of value) visit({ value: entry, depth: depth + 1 });
109
+ return;
110
+ }
111
+ if (typeof value !== "object") return;
112
+ const record = value;
113
+ for (const key of SOURCE_URL_KEYS) {
114
+ const candidate = record[key];
115
+ if (typeof candidate !== "string") continue;
116
+ const host = pageTarget(candidate);
117
+ if (host && !hosts.has(host)) {
118
+ hosts.add(host);
119
+ urls.push(candidate);
120
+ }
121
+ break;
122
+ }
123
+ for (const entry of Object.values(record)) {
124
+ if (typeof entry === "object") visit({ value: entry, depth: depth + 1 });
125
+ }
126
+ };
127
+ visit({ value: parseResult(result), depth: 0 });
128
+ return urls;
129
+ }
130
+ exports.collectSourceUrls = collectSourceUrls;
72
131
  exports.toTimelineStep = toTimelineStep;
73
132
  exports.visibleTimelineSteps = visibleTimelineSteps;
74
133
  //# sourceMappingURL=timeline-step.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-step.cjs","sources":["../../../src/chat/super-grouping/timeline-step.ts"],"sourcesContent":["import { type ActivityItem, getReasoningLabel, summarizeActivityTool } from './activity-timeline';\n\nexport interface TimelineStep {\n id: string;\n /** The row's line (\"Ran git status\"); `code` marks the literal part of it. */\n verb: string;\n /** The span of `verb` that is literal code (a command, a file name, a pattern). */\n code?: string;\n /** Longer form for the row's tooltip (the full path, the whole command line). */\n detail?: string;\n /** A short chip after the line (\"+4 −1\", \"on jevin-box\", \"Tab 7\"). */\n chip?: string;\n state: ActivityItem['state'];\n kind: ActivityItem['kind'];\n}\n\nconst COMPUTER_ACTIONS: Record<string, readonly [string, string]> = {\n screenshot: ['Taking screenshot', 'Took screenshot'],\n left_click: ['Clicking', 'Clicked'],\n right_click: ['Right-clicking', 'Right-clicked'],\n double_click: ['Double-clicking', 'Double-clicked'],\n triple_click: ['Triple-clicking', 'Triple-clicked'],\n type: ['Typing', 'Typed'],\n key: ['Pressing keys', 'Pressed keys'],\n scroll: ['Scrolling', 'Scrolled'],\n scroll_to: ['Scrolling to element', 'Scrolled to element'],\n zoom: ['Zooming', 'Zoomed'],\n hover: ['Hovering', 'Hovered'],\n left_click_drag: ['Dragging', 'Dragged'],\n wait: ['Waiting', 'Waited'],\n};\n\n/** Keep credentials, query strings, fragments, and typed input out of summary chips. */\nfunction pageTarget(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return undefined;\n return url.hostname;\n } catch {\n return undefined;\n }\n}\n\nexport function toTimelineStep({\n item,\n index,\n resolveToolDisplayName,\n assetTitles,\n}: {\n item: ActivityItem;\n index: number;\n resolveToolDisplayName?: (toolName: string) => string | undefined;\n /** Titles for asset ids earlier results in the turn named (see `collectAssetTitles`). */\n assetTitles?: ReadonlyMap<string, string>;\n}): TimelineStep {\n const id = item.id ?? `${item.kind}-${index}`;\n if (item.kind !== 'tool') {\n return { id, verb: getReasoningLabel(item.text, item.state), state: item.state, kind: item.kind };\n }\n const summary = summarizeActivityTool(item, resolveToolDisplayName, assetTitles);\n const step: TimelineStep = {\n id,\n verb: summary.label,\n ...(summary.code ? { code: summary.code } : {}),\n ...(summary.detail ? { detail: summary.detail } : {}),\n ...(summary.meta ? { chip: summary.meta } : {}),\n state: item.state,\n kind: item.kind,\n };\n const args = item.args;\n if (!args || !item.toolName.startsWith('chrome_')) return step;\n\n const tabId = args.tabId ?? args.tab_id;\n step.chip = pageTarget(args.url) ?? (typeof tabId === 'number' ? `Tab ${tabId}` : undefined);\n // Only successful/live actions get action-specific tense. Failure, rejection,\n // cancellation and approval copy continues to come from the shared classifier.\n if (item.state !== 'running' && item.state !== 'complete') return step;\n if (item.toolName === 'chrome_computer' && typeof args.action === 'string') {\n const action = COMPUTER_ACTIONS[args.action];\n if (action) step.verb = action[item.state === 'running' ? 0 : 1];\n } else if (item.toolName === 'chrome_navigate') {\n step.verb = item.state === 'running' ? 'Navigating to' : 'Navigated to';\n } else if (item.toolName === 'chrome_batch' && Array.isArray(args.steps)) {\n const count = args.steps.length;\n step.verb = item.state === 'running' ? 'Running Chrome actions' : 'Completed Chrome actions';\n step.chip = `${count} ${count === 1 ? 'action' : 'actions'}`;\n }\n return step;\n}\n\n/** Every unresolved parallel call stays visible even when older settled steps fold away. */\nexport function visibleTimelineSteps({ steps, limit }: { steps: readonly TimelineStep[]; limit: number }) {\n const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 3;\n return steps.filter(\n (step, index) =>\n index >= steps.length - safeLimit || step.state === 'running' || step.state === 'waiting',\n );\n}\n"],"names":["getReasoningLabel","summarizeActivityTool"],"mappings":"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"}
1
+ {"version":3,"file":"timeline-step.cjs","sources":["../../../src/chat/super-grouping/timeline-step.ts"],"sourcesContent":["import { type ActivityItem, getReasoningLabel, summarizeActivityTool } from './activity-timeline';\n\nexport interface TimelineStep {\n id: string;\n /** The row's line (\"Ran git status\"); `code` marks the literal part of it. */\n verb: string;\n /** The span of `verb` that is literal code (a command, a file name, a pattern). */\n code?: string;\n /** Longer form for the row's tooltip (the full path, the whole command line). */\n detail?: string;\n /** A short chip after the line (\"+4 −1\", \"on jevin-box\", \"Tab 7\"). */\n chip?: string;\n state: ActivityItem['state'];\n kind: ActivityItem['kind'];\n /** The tool a `tool` step called, so a host can show that tool's icon. */\n toolName?: string;\n /** Web pages a finished step's result names (up to three, one per site), for their favicons. */\n sourceUrls?: readonly string[];\n}\n\nconst COMPUTER_ACTIONS: Record<string, readonly [string, string]> = {\n screenshot: ['Taking screenshot', 'Took screenshot'],\n left_click: ['Clicking', 'Clicked'],\n right_click: ['Right-clicking', 'Right-clicked'],\n double_click: ['Double-clicking', 'Double-clicked'],\n triple_click: ['Triple-clicking', 'Triple-clicked'],\n type: ['Typing', 'Typed'],\n key: ['Pressing keys', 'Pressed keys'],\n scroll: ['Scrolling', 'Scrolled'],\n scroll_to: ['Scrolling to element', 'Scrolled to element'],\n zoom: ['Zooming', 'Zoomed'],\n hover: ['Hovering', 'Hovered'],\n left_click_drag: ['Dragging', 'Dragged'],\n wait: ['Waiting', 'Waited'],\n};\n\n/** Keep credentials, query strings, fragments, and typed input out of summary chips. */\nfunction pageTarget(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return undefined;\n return url.hostname;\n } catch {\n return undefined;\n }\n}\n\nexport function toTimelineStep({\n item,\n index,\n resolveToolDisplayName,\n assetTitles,\n withSourceUrls = false,\n}: {\n item: ActivityItem;\n index: number;\n resolveToolDisplayName?: (toolName: string) => string | undefined;\n /** Titles for asset ids earlier results in the turn named (see `collectAssetTitles`). */\n assetTitles?: ReadonlyMap<string, string>;\n /** Read the web pages a finished result names into `sourceUrls` (only when the card shows favicons). */\n withSourceUrls?: boolean;\n}): TimelineStep {\n const id = item.id ?? `${item.kind}-${index}`;\n if (item.kind !== 'tool') {\n return { id, verb: getReasoningLabel(item.text, item.state), state: item.state, kind: item.kind };\n }\n const summary = summarizeActivityTool(item, resolveToolDisplayName, assetTitles);\n const step: TimelineStep = {\n id,\n verb: summary.label,\n ...(summary.code ? { code: summary.code } : {}),\n ...(summary.detail ? { detail: summary.detail } : {}),\n ...(summary.meta ? { chip: summary.meta } : {}),\n state: item.state,\n kind: item.kind,\n toolName: item.toolName,\n };\n if (withSourceUrls && item.state === 'complete') {\n const sourceUrls = collectSourceUrls(item.result);\n if (sourceUrls.length > 0) step.sourceUrls = sourceUrls;\n }\n const args = item.args;\n if (!args || !item.toolName.startsWith('chrome_')) return step;\n\n const tabId = args.tabId ?? args.tab_id;\n step.chip = pageTarget(args.url) ?? (typeof tabId === 'number' ? `Tab ${tabId}` : undefined);\n // Only successful/live actions get action-specific tense. Failure, rejection,\n // cancellation and approval copy continues to come from the shared classifier.\n if (item.state !== 'running' && item.state !== 'complete') return step;\n if (item.toolName === 'chrome_computer' && typeof args.action === 'string') {\n const action = COMPUTER_ACTIONS[args.action];\n if (action) step.verb = action[item.state === 'running' ? 0 : 1];\n } else if (item.toolName === 'chrome_navigate') {\n step.verb = item.state === 'running' ? 'Navigating to' : 'Navigated to';\n } else if (item.toolName === 'chrome_batch' && Array.isArray(args.steps)) {\n const count = args.steps.length;\n step.verb = item.state === 'running' ? 'Running Chrome actions' : 'Completed Chrome actions';\n step.chip = `${count} ${count === 1 ? 'action' : 'actions'}`;\n }\n return step;\n}\n\n/** Every unresolved parallel call stays visible even when older settled steps fold away. */\nexport function visibleTimelineSteps({ steps, limit }: { steps: readonly TimelineStep[]; limit: number }) {\n const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 3;\n return steps.filter(\n (step, index) =>\n index >= steps.length - safeLimit || step.state === 'running' || step.state === 'waiting',\n );\n}\n\nconst SOURCE_URL_KEYS = ['url', 'link', 'href'] as const;\nconst MAX_SOURCE_URLS = 3;\nconst MAX_SOURCE_DEPTH = 4;\n\nconst MAX_SOURCE_RESULT_CHARS = 256 * 1024;\n\nfunction parseJson(text: string): unknown {\n if (text.length > MAX_SOURCE_RESULT_CHARS) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** A result as JSON: an object, a JSON string, or LangChain text blocks holding JSON. */\nfunction parseResult(result: unknown): unknown {\n if (typeof result === 'string') return parseJson(result);\n if (Array.isArray(result) && result.length > 0 && result.every(isTextBlock)) {\n return parseJson(result.map((block) => block.text).join(''));\n }\n return result;\n}\n\nfunction isTextBlock(value: unknown): value is { type: 'text'; text: string } {\n if (typeof value !== 'object' || value === null) return false;\n const block = value as Record<string, unknown>;\n return block.type === 'text' && typeof block.text === 'string';\n}\n\n/**\n * The web pages a tool result lists (a search's `results[].url`), at most one\n * per site and three in all, in the order the result gives them. Only http(s)\n * links count; anything else in the result is ignored.\n */\nexport function collectSourceUrls(result: unknown): string[] {\n const urls: string[] = [];\n const hosts = new Set<string>();\n const visit = ({ value, depth }: { value: unknown; depth: number }): void => {\n if (urls.length >= MAX_SOURCE_URLS || depth > MAX_SOURCE_DEPTH || value === null) return;\n if (Array.isArray(value)) {\n for (const entry of value) visit({ value: entry, depth: depth + 1 });\n return;\n }\n if (typeof value !== 'object') return;\n const record = value as Record<string, unknown>;\n for (const key of SOURCE_URL_KEYS) {\n const candidate = record[key];\n if (typeof candidate !== 'string') continue;\n const host = pageTarget(candidate);\n if (host && !hosts.has(host)) {\n hosts.add(host);\n urls.push(candidate);\n }\n break;\n }\n for (const entry of Object.values(record)) {\n if (typeof entry === 'object') visit({ value: entry, depth: depth + 1 });\n }\n };\n visit({ value: parseResult(result), depth: 0 });\n return urls;\n}\n"],"names":["getReasoningLabel","summarizeActivityTool"],"mappings":"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"}
@@ -11,16 +11,28 @@ export interface TimelineStep {
11
11
  chip?: string;
12
12
  state: ActivityItem['state'];
13
13
  kind: ActivityItem['kind'];
14
+ /** The tool a `tool` step called, so a host can show that tool's icon. */
15
+ toolName?: string;
16
+ /** Web pages a finished step's result names (up to three, one per site), for their favicons. */
17
+ sourceUrls?: readonly string[];
14
18
  }
15
- export declare function toTimelineStep({ item, index, resolveToolDisplayName, assetTitles, }: {
19
+ export declare function toTimelineStep({ item, index, resolveToolDisplayName, assetTitles, withSourceUrls, }: {
16
20
  item: ActivityItem;
17
21
  index: number;
18
22
  resolveToolDisplayName?: (toolName: string) => string | undefined;
19
23
  /** Titles for asset ids earlier results in the turn named (see `collectAssetTitles`). */
20
24
  assetTitles?: ReadonlyMap<string, string>;
25
+ /** Read the web pages a finished result names into `sourceUrls` (only when the card shows favicons). */
26
+ withSourceUrls?: boolean;
21
27
  }): TimelineStep;
22
28
  /** Every unresolved parallel call stays visible even when older settled steps fold away. */
23
29
  export declare function visibleTimelineSteps({ steps, limit }: {
24
30
  steps: readonly TimelineStep[];
25
31
  limit: number;
26
32
  }): TimelineStep[];
33
+ /**
34
+ * The web pages a tool result lists (a search's `results[].url`), at most one
35
+ * per site and three in all, in the order the result gives them. Only http(s)
36
+ * links count; anything else in the result is ignored.
37
+ */
38
+ export declare function collectSourceUrls(result: unknown): string[];
@@ -28,7 +28,8 @@ function toTimelineStep({
28
28
  item,
29
29
  index,
30
30
  resolveToolDisplayName,
31
- assetTitles
31
+ assetTitles,
32
+ withSourceUrls = false
32
33
  }) {
33
34
  const id = item.id ?? `${item.kind}-${index}`;
34
35
  if (item.kind !== "tool") {
@@ -42,8 +43,13 @@ function toTimelineStep({
42
43
  ...summary.detail ? { detail: summary.detail } : {},
43
44
  ...summary.meta ? { chip: summary.meta } : {},
44
45
  state: item.state,
45
- kind: item.kind
46
+ kind: item.kind,
47
+ toolName: item.toolName
46
48
  };
49
+ if (withSourceUrls && item.state === "complete") {
50
+ const sourceUrls = collectSourceUrls(item.result);
51
+ if (sourceUrls.length > 0) step.sourceUrls = sourceUrls;
52
+ }
47
53
  const args = item.args;
48
54
  if (!args || !item.toolName.startsWith("chrome_")) return step;
49
55
  const tabId = args.tabId ?? args.tab_id;
@@ -67,7 +73,60 @@ function visibleTimelineSteps({ steps, limit }) {
67
73
  (step, index) => index >= steps.length - safeLimit || step.state === "running" || step.state === "waiting"
68
74
  );
69
75
  }
76
+ const SOURCE_URL_KEYS = ["url", "link", "href"];
77
+ const MAX_SOURCE_URLS = 3;
78
+ const MAX_SOURCE_DEPTH = 4;
79
+ const MAX_SOURCE_RESULT_CHARS = 256 * 1024;
80
+ function parseJson(text) {
81
+ if (text.length > MAX_SOURCE_RESULT_CHARS) return void 0;
82
+ try {
83
+ return JSON.parse(text);
84
+ } catch {
85
+ return void 0;
86
+ }
87
+ }
88
+ function parseResult(result) {
89
+ if (typeof result === "string") return parseJson(result);
90
+ if (Array.isArray(result) && result.length > 0 && result.every(isTextBlock)) {
91
+ return parseJson(result.map((block) => block.text).join(""));
92
+ }
93
+ return result;
94
+ }
95
+ function isTextBlock(value) {
96
+ if (typeof value !== "object" || value === null) return false;
97
+ const block = value;
98
+ return block.type === "text" && typeof block.text === "string";
99
+ }
100
+ function collectSourceUrls(result) {
101
+ const urls = [];
102
+ const hosts = /* @__PURE__ */ new Set();
103
+ const visit = ({ value, depth }) => {
104
+ if (urls.length >= MAX_SOURCE_URLS || depth > MAX_SOURCE_DEPTH || value === null) return;
105
+ if (Array.isArray(value)) {
106
+ for (const entry of value) visit({ value: entry, depth: depth + 1 });
107
+ return;
108
+ }
109
+ if (typeof value !== "object") return;
110
+ const record = value;
111
+ for (const key of SOURCE_URL_KEYS) {
112
+ const candidate = record[key];
113
+ if (typeof candidate !== "string") continue;
114
+ const host = pageTarget(candidate);
115
+ if (host && !hosts.has(host)) {
116
+ hosts.add(host);
117
+ urls.push(candidate);
118
+ }
119
+ break;
120
+ }
121
+ for (const entry of Object.values(record)) {
122
+ if (typeof entry === "object") visit({ value: entry, depth: depth + 1 });
123
+ }
124
+ };
125
+ visit({ value: parseResult(result), depth: 0 });
126
+ return urls;
127
+ }
70
128
  export {
129
+ collectSourceUrls,
71
130
  toTimelineStep,
72
131
  visibleTimelineSteps
73
132
  };
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-step.js","sources":["../../../src/chat/super-grouping/timeline-step.ts"],"sourcesContent":["import { type ActivityItem, getReasoningLabel, summarizeActivityTool } from './activity-timeline';\n\nexport interface TimelineStep {\n id: string;\n /** The row's line (\"Ran git status\"); `code` marks the literal part of it. */\n verb: string;\n /** The span of `verb` that is literal code (a command, a file name, a pattern). */\n code?: string;\n /** Longer form for the row's tooltip (the full path, the whole command line). */\n detail?: string;\n /** A short chip after the line (\"+4 −1\", \"on jevin-box\", \"Tab 7\"). */\n chip?: string;\n state: ActivityItem['state'];\n kind: ActivityItem['kind'];\n}\n\nconst COMPUTER_ACTIONS: Record<string, readonly [string, string]> = {\n screenshot: ['Taking screenshot', 'Took screenshot'],\n left_click: ['Clicking', 'Clicked'],\n right_click: ['Right-clicking', 'Right-clicked'],\n double_click: ['Double-clicking', 'Double-clicked'],\n triple_click: ['Triple-clicking', 'Triple-clicked'],\n type: ['Typing', 'Typed'],\n key: ['Pressing keys', 'Pressed keys'],\n scroll: ['Scrolling', 'Scrolled'],\n scroll_to: ['Scrolling to element', 'Scrolled to element'],\n zoom: ['Zooming', 'Zoomed'],\n hover: ['Hovering', 'Hovered'],\n left_click_drag: ['Dragging', 'Dragged'],\n wait: ['Waiting', 'Waited'],\n};\n\n/** Keep credentials, query strings, fragments, and typed input out of summary chips. */\nfunction pageTarget(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return undefined;\n return url.hostname;\n } catch {\n return undefined;\n }\n}\n\nexport function toTimelineStep({\n item,\n index,\n resolveToolDisplayName,\n assetTitles,\n}: {\n item: ActivityItem;\n index: number;\n resolveToolDisplayName?: (toolName: string) => string | undefined;\n /** Titles for asset ids earlier results in the turn named (see `collectAssetTitles`). */\n assetTitles?: ReadonlyMap<string, string>;\n}): TimelineStep {\n const id = item.id ?? `${item.kind}-${index}`;\n if (item.kind !== 'tool') {\n return { id, verb: getReasoningLabel(item.text, item.state), state: item.state, kind: item.kind };\n }\n const summary = summarizeActivityTool(item, resolveToolDisplayName, assetTitles);\n const step: TimelineStep = {\n id,\n verb: summary.label,\n ...(summary.code ? { code: summary.code } : {}),\n ...(summary.detail ? { detail: summary.detail } : {}),\n ...(summary.meta ? { chip: summary.meta } : {}),\n state: item.state,\n kind: item.kind,\n };\n const args = item.args;\n if (!args || !item.toolName.startsWith('chrome_')) return step;\n\n const tabId = args.tabId ?? args.tab_id;\n step.chip = pageTarget(args.url) ?? (typeof tabId === 'number' ? `Tab ${tabId}` : undefined);\n // Only successful/live actions get action-specific tense. Failure, rejection,\n // cancellation and approval copy continues to come from the shared classifier.\n if (item.state !== 'running' && item.state !== 'complete') return step;\n if (item.toolName === 'chrome_computer' && typeof args.action === 'string') {\n const action = COMPUTER_ACTIONS[args.action];\n if (action) step.verb = action[item.state === 'running' ? 0 : 1];\n } else if (item.toolName === 'chrome_navigate') {\n step.verb = item.state === 'running' ? 'Navigating to' : 'Navigated to';\n } else if (item.toolName === 'chrome_batch' && Array.isArray(args.steps)) {\n const count = args.steps.length;\n step.verb = item.state === 'running' ? 'Running Chrome actions' : 'Completed Chrome actions';\n step.chip = `${count} ${count === 1 ? 'action' : 'actions'}`;\n }\n return step;\n}\n\n/** Every unresolved parallel call stays visible even when older settled steps fold away. */\nexport function visibleTimelineSteps({ steps, limit }: { steps: readonly TimelineStep[]; limit: number }) {\n const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 3;\n return steps.filter(\n (step, index) =>\n index >= steps.length - safeLimit || step.state === 'running' || step.state === 'waiting',\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"timeline-step.js","sources":["../../../src/chat/super-grouping/timeline-step.ts"],"sourcesContent":["import { type ActivityItem, getReasoningLabel, summarizeActivityTool } from './activity-timeline';\n\nexport interface TimelineStep {\n id: string;\n /** The row's line (\"Ran git status\"); `code` marks the literal part of it. */\n verb: string;\n /** The span of `verb` that is literal code (a command, a file name, a pattern). */\n code?: string;\n /** Longer form for the row's tooltip (the full path, the whole command line). */\n detail?: string;\n /** A short chip after the line (\"+4 −1\", \"on jevin-box\", \"Tab 7\"). */\n chip?: string;\n state: ActivityItem['state'];\n kind: ActivityItem['kind'];\n /** The tool a `tool` step called, so a host can show that tool's icon. */\n toolName?: string;\n /** Web pages a finished step's result names (up to three, one per site), for their favicons. */\n sourceUrls?: readonly string[];\n}\n\nconst COMPUTER_ACTIONS: Record<string, readonly [string, string]> = {\n screenshot: ['Taking screenshot', 'Took screenshot'],\n left_click: ['Clicking', 'Clicked'],\n right_click: ['Right-clicking', 'Right-clicked'],\n double_click: ['Double-clicking', 'Double-clicked'],\n triple_click: ['Triple-clicking', 'Triple-clicked'],\n type: ['Typing', 'Typed'],\n key: ['Pressing keys', 'Pressed keys'],\n scroll: ['Scrolling', 'Scrolled'],\n scroll_to: ['Scrolling to element', 'Scrolled to element'],\n zoom: ['Zooming', 'Zoomed'],\n hover: ['Hovering', 'Hovered'],\n left_click_drag: ['Dragging', 'Dragged'],\n wait: ['Waiting', 'Waited'],\n};\n\n/** Keep credentials, query strings, fragments, and typed input out of summary chips. */\nfunction pageTarget(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return undefined;\n return url.hostname;\n } catch {\n return undefined;\n }\n}\n\nexport function toTimelineStep({\n item,\n index,\n resolveToolDisplayName,\n assetTitles,\n withSourceUrls = false,\n}: {\n item: ActivityItem;\n index: number;\n resolveToolDisplayName?: (toolName: string) => string | undefined;\n /** Titles for asset ids earlier results in the turn named (see `collectAssetTitles`). */\n assetTitles?: ReadonlyMap<string, string>;\n /** Read the web pages a finished result names into `sourceUrls` (only when the card shows favicons). */\n withSourceUrls?: boolean;\n}): TimelineStep {\n const id = item.id ?? `${item.kind}-${index}`;\n if (item.kind !== 'tool') {\n return { id, verb: getReasoningLabel(item.text, item.state), state: item.state, kind: item.kind };\n }\n const summary = summarizeActivityTool(item, resolveToolDisplayName, assetTitles);\n const step: TimelineStep = {\n id,\n verb: summary.label,\n ...(summary.code ? { code: summary.code } : {}),\n ...(summary.detail ? { detail: summary.detail } : {}),\n ...(summary.meta ? { chip: summary.meta } : {}),\n state: item.state,\n kind: item.kind,\n toolName: item.toolName,\n };\n if (withSourceUrls && item.state === 'complete') {\n const sourceUrls = collectSourceUrls(item.result);\n if (sourceUrls.length > 0) step.sourceUrls = sourceUrls;\n }\n const args = item.args;\n if (!args || !item.toolName.startsWith('chrome_')) return step;\n\n const tabId = args.tabId ?? args.tab_id;\n step.chip = pageTarget(args.url) ?? (typeof tabId === 'number' ? `Tab ${tabId}` : undefined);\n // Only successful/live actions get action-specific tense. Failure, rejection,\n // cancellation and approval copy continues to come from the shared classifier.\n if (item.state !== 'running' && item.state !== 'complete') return step;\n if (item.toolName === 'chrome_computer' && typeof args.action === 'string') {\n const action = COMPUTER_ACTIONS[args.action];\n if (action) step.verb = action[item.state === 'running' ? 0 : 1];\n } else if (item.toolName === 'chrome_navigate') {\n step.verb = item.state === 'running' ? 'Navigating to' : 'Navigated to';\n } else if (item.toolName === 'chrome_batch' && Array.isArray(args.steps)) {\n const count = args.steps.length;\n step.verb = item.state === 'running' ? 'Running Chrome actions' : 'Completed Chrome actions';\n step.chip = `${count} ${count === 1 ? 'action' : 'actions'}`;\n }\n return step;\n}\n\n/** Every unresolved parallel call stays visible even when older settled steps fold away. */\nexport function visibleTimelineSteps({ steps, limit }: { steps: readonly TimelineStep[]; limit: number }) {\n const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 3;\n return steps.filter(\n (step, index) =>\n index >= steps.length - safeLimit || step.state === 'running' || step.state === 'waiting',\n );\n}\n\nconst SOURCE_URL_KEYS = ['url', 'link', 'href'] as const;\nconst MAX_SOURCE_URLS = 3;\nconst MAX_SOURCE_DEPTH = 4;\n\nconst MAX_SOURCE_RESULT_CHARS = 256 * 1024;\n\nfunction parseJson(text: string): unknown {\n if (text.length > MAX_SOURCE_RESULT_CHARS) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** A result as JSON: an object, a JSON string, or LangChain text blocks holding JSON. */\nfunction parseResult(result: unknown): unknown {\n if (typeof result === 'string') return parseJson(result);\n if (Array.isArray(result) && result.length > 0 && result.every(isTextBlock)) {\n return parseJson(result.map((block) => block.text).join(''));\n }\n return result;\n}\n\nfunction isTextBlock(value: unknown): value is { type: 'text'; text: string } {\n if (typeof value !== 'object' || value === null) return false;\n const block = value as Record<string, unknown>;\n return block.type === 'text' && typeof block.text === 'string';\n}\n\n/**\n * The web pages a tool result lists (a search's `results[].url`), at most one\n * per site and three in all, in the order the result gives them. Only http(s)\n * links count; anything else in the result is ignored.\n */\nexport function collectSourceUrls(result: unknown): string[] {\n const urls: string[] = [];\n const hosts = new Set<string>();\n const visit = ({ value, depth }: { value: unknown; depth: number }): void => {\n if (urls.length >= MAX_SOURCE_URLS || depth > MAX_SOURCE_DEPTH || value === null) return;\n if (Array.isArray(value)) {\n for (const entry of value) visit({ value: entry, depth: depth + 1 });\n return;\n }\n if (typeof value !== 'object') return;\n const record = value as Record<string, unknown>;\n for (const key of SOURCE_URL_KEYS) {\n const candidate = record[key];\n if (typeof candidate !== 'string') continue;\n const host = pageTarget(candidate);\n if (host && !hosts.has(host)) {\n hosts.add(host);\n urls.push(candidate);\n }\n break;\n }\n for (const entry of Object.values(record)) {\n if (typeof entry === 'object') visit({ value: entry, depth: depth + 1 });\n }\n };\n visit({ value: parseResult(result), depth: 0 });\n return urls;\n}\n"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ const assetUrls = require("../lib/asset-urls.cjs");
10
10
  const apiClient = require("./suggestions/data/api-client.cjs");
11
11
  const assetTypeIcons = require("./suggestions/data/sources/asset-type-icons.cjs");
12
12
  const useMemberAvatar = require("./use-member-avatar.cjs");
13
+ const useMentionAsset = require("./use-mention-asset.cjs");
13
14
  const screenPoint = require("../../agent-runtime/src/screen-point.cjs");
14
15
  const cnfast = require("cnfast");
15
16
  const SPACES_MENTION_PATH = /\/dashboard\/spaces\/?\?/;
@@ -81,11 +82,62 @@ const ASSET_CHIP_ICONS = {
81
82
  web_page: lucideReact.Globe2Icon,
82
83
  browse_session: lucideReact.Globe2Icon
83
84
  };
84
- function assetChipIcon(assetType, citation) {
85
- if ((citation == null ? void 0 : citation.anchorType) === "file_line_range") return lucideReact.FileCodeIcon;
85
+ function assetChipIcon(assetType) {
86
86
  return assetType && ASSET_CHIP_ICONS[assetType.toLowerCase()] || lucideReact.FileIcon;
87
87
  }
88
+ function GitHubMarkIcon({ className }) {
89
+ return /* @__PURE__ */ jsxRuntime.jsx(
90
+ "svg",
91
+ {
92
+ viewBox: "0 0 16 16",
93
+ fill: "currentColor",
94
+ "aria-hidden": "true",
95
+ className,
96
+ "data-chip-icon": "github",
97
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" })
98
+ }
99
+ );
100
+ }
101
+ const SESSION_ANCHORS = /* @__PURE__ */ new Set([
102
+ "msg",
103
+ "msg_range",
104
+ "msg_block",
105
+ "msg_section",
106
+ "msg_text",
107
+ "tool_call"
108
+ ]);
109
+ function isSessionLink(assetType, citation) {
110
+ return (assetType == null ? void 0 : assetType.toLowerCase()) === "session" || citation !== void 0 && SESSION_ANCHORS.has(citation.anchorType);
111
+ }
112
+ const PLACE_FITS_CHARS = 20;
113
+ function splitFileLines(label) {
114
+ const colon = label.lastIndexOf(":");
115
+ return colon > 0 ? { file: label.slice(0, colon), lines: label.slice(colon) } : { file: label, lines: "" };
116
+ }
117
+ function restatesPlace(name, citation) {
118
+ const said = name.trim().toLowerCase();
119
+ const label = citation.label.toLowerCase();
120
+ return said === label || said === citation.description.toLowerCase() || said.endsWith(label);
121
+ }
122
+ function informativeLabel({
123
+ label,
124
+ shown,
125
+ citation
126
+ }) {
127
+ const trimmed = label.trim();
128
+ if (!trimmed || !SPEAKABLE.test(trimmed)) return null;
129
+ if (PLACEHOLDER_LABELS.has(trimmed.toLowerCase())) return null;
130
+ if (citation && restatesPlace(trimmed, citation)) return null;
131
+ const shownWords = new Set(
132
+ [...shown, citation == null ? void 0 : citation.description].flatMap((text) => text ? wordsOf(text) : [])
133
+ );
134
+ return wordsOf(trimmed).every((word) => shownWords.has(word)) ? null : trimmed;
135
+ }
136
+ function wordsOf(text) {
137
+ return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
138
+ }
88
139
  const SPEAKABLE = /[\p{L}\p{N}]/u;
140
+ const PLACEHOLDER_LABELS = /* @__PURE__ */ new Set(["citation", "source", "link", "here"]);
89
141
  function decodeAssetLink(url, decode) {
90
142
  try {
91
143
  return decode(url);
@@ -97,9 +149,15 @@ function personEmailForMention(parsed) {
97
149
  if (parsed.type !== "person") return void 0;
98
150
  return parsed.email || (parsed.id.includes("@") ? parsed.id : void 0);
99
151
  }
152
+ function textOf(node) {
153
+ if (typeof node === "string" || typeof node === "number") return String(node);
154
+ if (Array.isArray(node)) return node.map(textOf).join("");
155
+ if (react.isValidElement(node)) return textOf(node.props.children);
156
+ return "";
157
+ }
100
158
  const MentionLink = ({ href, children, interactive = true }) => {
101
159
  if (href && (href.startsWith("mention://") || isSpacesMentionUrl(href))) {
102
- const name = typeof children === "string" ? children.replace(/^@/, "") : Array.isArray(children) ? String(children[0] ?? "").replace(/^@/, "") : "";
160
+ const name = textOf(children).trim().replace(/^@/, "");
103
161
  const parsed = parseMentionUrl(href);
104
162
  return /* @__PURE__ */ jsxRuntime.jsx(
105
163
  MentionChip,
@@ -124,6 +182,9 @@ const MentionLink = ({ href, children, interactive = true }) => {
124
182
  }
125
183
  return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
126
184
  };
185
+ const CHIP_PILL_CLASS = "not-prose box-border inline-flex h-5 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-muted px-1 py-0.5 align-middle font-normal leading-none text-foreground no-underline";
186
+ const CHIP_LINK_CLASS = "cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1";
187
+ const CHIP_ICON_CLASS = "size-4 shrink-0";
127
188
  const ScreenPointChip = ({ href, children }) => {
128
189
  const parsed = screenPoint.parseScreenPointHref(href);
129
190
  if (!parsed.ok) {
@@ -133,14 +194,14 @@ const ScreenPointChip = ({ href, children }) => {
133
194
  return /* @__PURE__ */ jsxRuntime.jsxs(
134
195
  "span",
135
196
  {
136
- className: "not-prose inline-flex max-w-[260px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600",
197
+ className: cnfast.cn(CHIP_PILL_CLASS, "max-w-[260px]"),
137
198
  title: description,
138
199
  "aria-label": description,
139
200
  "data-screen-point": "valid",
140
201
  children: [
141
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CrosshairIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
142
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children }),
143
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal text-slate-400", children: [
202
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CrosshairIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }),
203
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 max-w-[180px] overflow-hidden text-ellipsis", children }),
204
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
144
205
  parsed.point.x,
145
206
  ", ",
146
207
  parsed.point.y
@@ -164,8 +225,21 @@ const MentionChip = ({
164
225
  const memberAvatarUrl = useMemberAvatar.useMemberAvatarUrl(personEmail);
165
226
  const [erroredAvatarUrl, setErroredAvatarUrl] = react.useState();
166
227
  const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
167
- const tooltip = excerpt ?? (citation == null ? void 0 : citation.description) ?? null;
168
- const anchorLabel = citation && name !== citation.description && name !== citation.label ? citation.label : null;
228
+ const repositoryFile = (citation == null ? void 0 : citation.anchorType) === "file_line_range" ? { ...splitFileLines(citation.label), label: citation.label } : null;
229
+ const asset = useMentionAsset.useMentionAsset(
230
+ type === "asset" && (assetIds == null ? void 0 : assetIds.length) === 1 && !repositoryFile && !isSessionLink(assetType, citation) ? assetIds[0] : void 0
231
+ );
232
+ const assetTitle = (asset == null ? void 0 : asset.title) || null;
233
+ const chipName = (repositoryFile == null ? void 0 : repositoryFile.label) ?? assetTitle ?? name;
234
+ const placeLabel = citation && !repositoryFile && !restatesPlace(chipName, citation) ? citation.label : null;
235
+ const tooltip = citation || excerpt ? tooltipText({
236
+ lines: [
237
+ informativeLabel({ label: name, shown: [chipName, assetTitle], citation }),
238
+ repositoryFile ? assetTitle : null,
239
+ repositoryFile || !excerpt || ((placeLabel == null ? void 0 : placeLabel.length) ?? 0) > PLACE_FITS_CHARS ? citation == null ? void 0 : citation.description : null
240
+ ],
241
+ excerpt
242
+ }) : null;
169
243
  const assetRootHref = type === "asset" && (assetIds == null ? void 0 : assetIds.length) ? assetUrls.createOlympusAssetUrl({
170
244
  appUrl: auth == null ? void 0 : auth.appUrl,
171
245
  backendUrl: auth == null ? void 0 : auth.backendUrl,
@@ -185,51 +259,71 @@ const MentionChip = ({
185
259
  return assetRootHref;
186
260
  }
187
261
  })();
188
- const className = cnfast.cn(
189
- "not-prose inline-flex max-w-[220px] items-center gap-1 whitespace-nowrap rounded-md border border-slate-300 bg-white px-1.5 py-px align-middle text-[0.85em] font-medium leading-[1.4] text-slate-600 no-underline",
190
- // The cited place gets its own room after a name already at its cap.
191
- anchorLabel && "max-w-[340px]",
192
- href && "cursor-pointer transition-colors hover:border-slate-400 hover:bg-slate-50 hover:text-slate-800 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"
193
- );
194
- const AssetIcon = assetChipIcon(assetType, citation);
262
+ const className = cnfast.cn(CHIP_PILL_CLASS, "max-w-[275px]", href && CHIP_LINK_CLASS);
263
+ const chipThumbnailUrl = repositoryFile ? void 0 : (asset == null ? void 0 : asset.assetType) && assetTypeIcons.assetTypeToThumb(asset.assetType) || thumbnailUrl;
264
+ const AssetIcon = assetChipIcon((asset == null ? void 0 : asset.assetType) || assetType);
195
265
  const contents = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
196
- thumbnailUrl && /* @__PURE__ */ jsxRuntime.jsx(
266
+ repositoryFile ? /* @__PURE__ */ jsxRuntime.jsx(GitHubMarkIcon, { className: CHIP_ICON_CLASS }) : chipThumbnailUrl ? /* @__PURE__ */ jsxRuntime.jsx(
197
267
  "img",
198
268
  {
199
- src: thumbnailUrl,
269
+ src: chipThumbnailUrl,
200
270
  alt: "",
201
- className: "size-3.5 shrink-0",
271
+ className: CHIP_ICON_CLASS,
202
272
  draggable: false
203
273
  }
204
- ),
205
- !thumbnailUrl && (type === "asset" || type === "folder") && // A host that bundles no asset-type SVGs (computer apps, most SDK
206
- // apps) still gets the type's icon.
207
- /* @__PURE__ */ jsxRuntime.jsx(AssetIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
208
- !thumbnailUrl && type === "webpage" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe2Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
209
- !thumbnailUrl && type === "person" && (memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
274
+ ) : type === "asset" || type === "folder" ? (
275
+ // A host that bundles no asset-type SVGs (computer apps, most SDK
276
+ // apps) still gets the type's icon.
277
+ /* @__PURE__ */ jsxRuntime.jsx(AssetIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" })
278
+ ) : type === "webpage" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe2Icon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }) : type === "person" ? memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
210
279
  "img",
211
280
  {
212
281
  src: memberAvatarUrl,
213
282
  alt: "",
214
- className: "size-3.5 shrink-0 rounded-full object-cover",
283
+ className: cnfast.cn(CHIP_ICON_CLASS, "rounded-full object-cover"),
215
284
  draggable: false,
216
285
  onError: () => setErroredAvatarUrl(memberAvatarUrl)
217
286
  }
218
- ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserIcon, { className: "size-3.5 shrink-0", "aria-hidden": "true" })),
219
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[180px] overflow-hidden text-ellipsis", children: name }),
220
- citation && anchorLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
287
+ ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }) : null,
288
+ repositoryFile ? /* @__PURE__ */ jsxRuntime.jsxs(
289
+ "span",
290
+ {
291
+ className: "flex min-w-0 items-center",
292
+ "data-citation-anchor": "file_line_range",
293
+ children: [
294
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: repositoryFile.file }),
295
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: repositoryFile.lines })
296
+ ]
297
+ }
298
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
299
+ "span",
300
+ {
301
+ className: cnfast.cn(
302
+ "max-w-[180px] overflow-hidden text-ellipsis",
303
+ // A long place may shorten the name, never make it vanish.
304
+ placeLabel && "min-w-8"
305
+ ),
306
+ children: chipName
307
+ }
308
+ ),
309
+ citation && placeLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
221
310
  " ",
222
- /* @__PURE__ */ jsxRuntime.jsx(
311
+ /* @__PURE__ */ jsxRuntime.jsxs(
223
312
  "span",
224
313
  {
225
- className: "max-w-[140px] shrink-0 overflow-hidden text-ellipsis font-normal text-slate-400",
314
+ className: "flex min-w-0 items-center text-xs text-muted-foreground",
226
315
  "data-citation-anchor": citation.anchorType,
227
- children: anchorLabel
316
+ children: [
317
+ "[",
318
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: placeLabel }),
319
+ "]"
320
+ ]
228
321
  }
229
322
  )
230
323
  ] })
231
324
  ] });
232
325
  if (href && interactive) {
326
+ const speakablePlace = citation && SPEAKABLE.test(citation.description);
233
327
  return /* @__PURE__ */ jsxRuntime.jsx(
234
328
  "a",
235
329
  {
@@ -237,10 +331,10 @@ const MentionChip = ({
237
331
  target: "_blank",
238
332
  rel: "noopener noreferrer",
239
333
  className,
240
- "aria-label": anchorLabel && citation && SPEAKABLE.test(citation.description) ? `Open ${name} at ${citation.description} in Spaces` : `Open ${name} in Spaces`,
241
- title: tooltip ?? `Open ${name} in Spaces`,
334
+ "aria-label": repositoryFile && citation ? `Open ${citation.description} in Spaces` : placeLabel && citation && speakablePlace ? `Open ${chipName} at ${citation.description} in Spaces` : `Open ${chipName} in Spaces`,
335
+ title: tooltip ?? `Open ${chipName} in Spaces`,
242
336
  "data-excerpt": excerpt ?? void 0,
243
- "data-mention-name": name,
337
+ "data-mention-name": repositoryFile ? assetTitle ?? name : chipName,
244
338
  children: contents
245
339
  }
246
340
  );
@@ -255,6 +349,18 @@ const MentionChip = ({
255
349
  }
256
350
  );
257
351
  };
352
+ function tooltipText({
353
+ lines,
354
+ excerpt
355
+ }) {
356
+ const kept = [];
357
+ for (const line of lines) {
358
+ const trimmed = line == null ? void 0 : line.trim();
359
+ if (trimmed && !kept.includes(trimmed)) kept.push(trimmed);
360
+ }
361
+ if (excerpt) kept.push(kept.length ? `“${excerpt}”` : excerpt);
362
+ return kept.length ? kept.join("\n") : null;
363
+ }
258
364
  function readSourceExcerpt(href) {
259
365
  var _a;
260
366
  try {
@@ -400,6 +506,9 @@ function mentionsToPlainText(text) {
400
506
  return `@${name || parseMentionUrl(url).name}`;
401
507
  });
402
508
  }
509
+ exports.CHIP_ICON_CLASS = CHIP_ICON_CLASS;
510
+ exports.CHIP_LINK_CLASS = CHIP_LINK_CLASS;
511
+ exports.CHIP_PILL_CLASS = CHIP_PILL_CLASS;
403
512
  exports.MentionChip = MentionChip;
404
513
  exports.MentionLink = MentionLink;
405
514
  exports.ScreenPointChip = ScreenPointChip;