@agent-native/toolkit 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/chat-history/ChatHistoryList.d.ts +103 -0
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.js +109 -0
- package/dist/chat-history/ChatHistoryList.js.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts +2 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.js +283 -0
- package/dist/chat-history/ChatHistoryList.spec.js.map +1 -0
- package/dist/chat-history/index.d.ts +2 -0
- package/dist/chat-history/index.d.ts.map +1 -0
- package/dist/chat-history/index.js +2 -0
- package/dist/chat-history/index.js.map +1 -0
- package/dist/chat-history.css +314 -0
- package/package.json +17 -1
- package/src/app-shell/header-actions.tsx +84 -0
- package/src/app-shell/index.ts +1 -0
- package/src/chat-history/ChatHistoryList.spec.tsx +453 -0
- package/src/chat-history/ChatHistoryList.tsx +469 -0
- package/src/chat-history/index.ts +7 -0
- package/src/chat-history.css +314 -0
- package/src/collab-ui/AgentPresenceChip.tsx +69 -0
- package/src/collab-ui/LiveCursorOverlay.tsx +241 -0
- package/src/collab-ui/PresenceBar.tsx +346 -0
- package/src/collab-ui/RecentEditHighlights.tsx +225 -0
- package/src/collab-ui/RemoteSelectionRings.tsx +227 -0
- package/src/collab-ui/agent-identity.ts +16 -0
- package/src/collab-ui/index.ts +39 -0
- package/src/collab-ui/lead-client.ts +32 -0
- package/src/collab-ui/types.spec.ts +18 -0
- package/src/collab-ui/types.ts +96 -0
- package/src/composer/AgentComposerFrame.tsx +59 -0
- package/src/composer/ComposerPlusMenu.tsx +983 -0
- package/src/composer/MentionPopover.tsx +412 -0
- package/src/composer/PastedTextChip.tsx +150 -0
- package/src/composer/PromptComposer.spec.ts +73 -0
- package/src/composer/PromptComposer.tsx +718 -0
- package/src/composer/RealtimeVoiceMode.spec.tsx +771 -0
- package/src/composer/RealtimeVoiceMode.tsx +856 -0
- package/src/composer/TiptapComposer.spec.ts +390 -0
- package/src/composer/TiptapComposer.tsx +2936 -0
- package/src/composer/VoiceButton.spec.ts +33 -0
- package/src/composer/VoiceButton.tsx +255 -0
- package/src/composer/asset-picker-url.spec.ts +56 -0
- package/src/composer/asset-picker-url.ts +80 -0
- package/src/composer/attachment-accept.spec.ts +22 -0
- package/src/composer/attachment-accept.ts +71 -0
- package/src/composer/draft-key.spec.ts +19 -0
- package/src/composer/draft-key.ts +7 -0
- package/src/composer/extensions/FileReference.tsx +55 -0
- package/src/composer/extensions/MentionReference.tsx +93 -0
- package/src/composer/extensions/SkillReference.tsx +56 -0
- package/src/composer/index.ts +79 -0
- package/src/composer/pasted-text.spec.ts +162 -0
- package/src/composer/pasted-text.ts +160 -0
- package/src/composer/prompt-attachments.spec.ts +80 -0
- package/src/composer/prompt-attachments.ts +93 -0
- package/src/composer/realtime-voice-audio-level.spec.ts +32 -0
- package/src/composer/realtime-voice-audio-level.ts +64 -0
- package/src/composer/realtime-voice-transcript.spec.ts +95 -0
- package/src/composer/realtime-voice-transcript.ts +134 -0
- package/src/composer/runtime-adapters.tsx +343 -0
- package/src/composer/types.ts +66 -0
- package/src/composer/use-file-search.ts +64 -0
- package/src/composer/use-mention-search.ts +90 -0
- package/src/composer/use-skills.ts +45 -0
- package/src/composer/useRealtimeVoiceMode.spec.ts +818 -0
- package/src/composer/useRealtimeVoiceMode.tsx +2117 -0
- package/src/composer/useVoiceDictation.spec.ts +45 -0
- package/src/composer/useVoiceDictation.ts +1191 -0
- package/src/context-ui/ContextMeter.tsx +101 -0
- package/src/context-ui/ContextSegmentRow.tsx +184 -0
- package/src/context-ui/ContextTreemap.tsx +125 -0
- package/src/context-ui/ContextXRayPanel.tsx +377 -0
- package/src/context-ui/format.ts +42 -0
- package/src/context-ui/index.ts +15 -0
- package/src/context-ui/types.ts +43 -0
- package/src/design-tweaks/index.ts +1 -0
- package/src/design-tweaks/visual-style-controls.tsx +1084 -0
- package/src/editor/BubbleToolbar.tsx +253 -0
- package/src/editor/CodeBlockNode.tsx +254 -0
- package/src/editor/DragHandle.spec.ts +588 -0
- package/src/editor/DragHandle.ts +1437 -0
- package/src/editor/ImageExtension.roundtrip.spec.ts +117 -0
- package/src/editor/ImageExtension.ts +306 -0
- package/src/editor/RegistryBlockContext.tsx +107 -0
- package/src/editor/RegistryBlockNode.spec.ts +279 -0
- package/src/editor/RegistryBlockNode.tsx +652 -0
- package/src/editor/RichMarkdownEditor.collab.spec.ts +79 -0
- package/src/editor/RichMarkdownEditor.roundtrip.spec.ts +226 -0
- package/src/editor/RichMarkdownEditor.tsx +81 -0
- package/src/editor/RunId.ts +61 -0
- package/src/editor/SharedRichEditor.tsx +321 -0
- package/src/editor/SlashCommandMenu.tsx +310 -0
- package/src/editor/disableHistory.spec.ts +85 -0
- package/src/editor/extensions.ts +402 -0
- package/src/editor/gfmDoc.spec.ts +230 -0
- package/src/editor/gfmDoc.ts +92 -0
- package/src/editor/index.ts +93 -0
- package/src/editor/registrySlashCommands.ts +131 -0
- package/src/editor/structuralReorderUndo.spec.ts +144 -0
- package/src/editor/surgical-apply.spec.ts +279 -0
- package/src/editor/surgical-apply.ts +192 -0
- package/src/editor/useCollabReconcile.concurrent.spec.ts +422 -0
- package/src/editor/useCollabReconcile.idempotent.spec.ts +324 -0
- package/src/editor/useCollabReconcile.ts +589 -0
- package/src/editor.css +510 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-mobile.tsx +21 -0
- package/src/hooks/use-toast.ts +188 -0
- package/src/index.ts +8 -0
- package/src/onboarding/index.ts +1 -0
- package/src/provider.tsx +47 -0
- package/src/sharing/VisibilityBadge.spec.ts +96 -0
- package/src/sharing/VisibilityBadge.tsx +37 -0
- package/src/sharing/index.ts +4 -0
- package/src/styles.css +21 -0
- package/src/ui/accordion.tsx +56 -0
- package/src/ui/alert-dialog.tsx +141 -0
- package/src/ui/alert.tsx +59 -0
- package/src/ui/aspect-ratio.tsx +5 -0
- package/src/ui/avatar.tsx +48 -0
- package/src/ui/badge.tsx +37 -0
- package/src/ui/breadcrumb.tsx +115 -0
- package/src/ui/button.tsx +78 -0
- package/src/ui/calendar.tsx +214 -0
- package/src/ui/card.tsx +86 -0
- package/src/ui/carousel.tsx +260 -0
- package/src/ui/chart.tsx +375 -0
- package/src/ui/checkbox.tsx +28 -0
- package/src/ui/collapsible.tsx +9 -0
- package/src/ui/command.spec.tsx +51 -0
- package/src/ui/command.tsx +174 -0
- package/src/ui/context-menu.tsx +202 -0
- package/src/ui/date-picker.tsx +64 -0
- package/src/ui/dialog.spec.tsx +43 -0
- package/src/ui/dialog.tsx +146 -0
- package/src/ui/drawer.tsx +126 -0
- package/src/ui/dropdown-menu.tsx +218 -0
- package/src/ui/form.tsx +177 -0
- package/src/ui/hover-card.tsx +27 -0
- package/src/ui/index.ts +52 -0
- package/src/ui/input-otp.tsx +69 -0
- package/src/ui/input.tsx +22 -0
- package/src/ui/label.tsx +24 -0
- package/src/ui/logo.tsx +36 -0
- package/src/ui/menubar.tsx +235 -0
- package/src/ui/navigation-menu.tsx +128 -0
- package/src/ui/pagination.tsx +121 -0
- package/src/ui/popover.tsx +44 -0
- package/src/ui/progress.tsx +26 -0
- package/src/ui/radio-group.tsx +42 -0
- package/src/ui/resizable.tsx +43 -0
- package/src/ui/scroll-area.tsx +46 -0
- package/src/ui/select.tsx +158 -0
- package/src/ui/separator.tsx +29 -0
- package/src/ui/sheet.tsx +160 -0
- package/src/ui/sidebar.tsx +777 -0
- package/src/ui/skeleton.tsx +18 -0
- package/src/ui/slider.tsx +26 -0
- package/src/ui/sonner.tsx +58 -0
- package/src/ui/spinner.tsx +17 -0
- package/src/ui/switch.tsx +27 -0
- package/src/ui/table.tsx +117 -0
- package/src/ui/tabs.tsx +53 -0
- package/src/ui/textarea.tsx +23 -0
- package/src/ui/toast.tsx +127 -0
- package/src/ui/toaster.tsx +33 -0
- package/src/ui/toggle-group.tsx +59 -0
- package/src/ui/toggle.tsx +43 -0
- package/src/ui/tooltip.tsx +53 -0
- package/src/ui/use-toast.ts +3 -0
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IconChartTreemap,
|
|
3
|
+
IconChevronDown,
|
|
4
|
+
IconChevronRight,
|
|
5
|
+
IconLock,
|
|
6
|
+
IconListDetails,
|
|
7
|
+
} from "@tabler/icons-react";
|
|
8
|
+
import { useMemo, useState } from "react";
|
|
9
|
+
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
|
|
11
|
+
import { cn } from "../utils.js";
|
|
12
|
+
import { ContextSegmentRowView } from "./ContextSegmentRow.js";
|
|
13
|
+
import { ContextTreemapView } from "./ContextTreemap.js";
|
|
14
|
+
import { contextGroupColor, formatContextTokens } from "./format.js";
|
|
15
|
+
import type {
|
|
16
|
+
ContextManifestViewData,
|
|
17
|
+
ContextSegmentStatus,
|
|
18
|
+
ContextSegmentViewData,
|
|
19
|
+
ContextSystemSectionViewData,
|
|
20
|
+
} from "./types.js";
|
|
21
|
+
|
|
22
|
+
interface Group {
|
|
23
|
+
name: string;
|
|
24
|
+
tokens: number;
|
|
25
|
+
segments: ContextSegmentViewData[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function applyOptimisticStatus(
|
|
29
|
+
segments: ContextSegmentViewData[],
|
|
30
|
+
optimistic: Map<string, ContextSegmentStatus>,
|
|
31
|
+
) {
|
|
32
|
+
if (optimistic.size === 0) return segments;
|
|
33
|
+
return segments.map((segment) =>
|
|
34
|
+
optimistic.has(segment.segmentId)
|
|
35
|
+
? { ...segment, status: optimistic.get(segment.segmentId)! }
|
|
36
|
+
: segment,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function groupedSegments(segments: ContextSegmentViewData[]): Group[] {
|
|
41
|
+
const map = new Map<string, Group>();
|
|
42
|
+
for (const segment of segments) {
|
|
43
|
+
const name =
|
|
44
|
+
segment.status === "pinned"
|
|
45
|
+
? "Pinned"
|
|
46
|
+
: segment.status === "evicted"
|
|
47
|
+
? "Evicted"
|
|
48
|
+
: segment.group;
|
|
49
|
+
const group = map.get(name) ?? { name, tokens: 0, segments: [] };
|
|
50
|
+
group.tokens += segment.tokenCount;
|
|
51
|
+
group.segments.push(segment);
|
|
52
|
+
map.set(name, group);
|
|
53
|
+
}
|
|
54
|
+
const order = [
|
|
55
|
+
"Pinned",
|
|
56
|
+
"Tool results",
|
|
57
|
+
"Files read",
|
|
58
|
+
"Conversation",
|
|
59
|
+
"Thinking",
|
|
60
|
+
"Evicted",
|
|
61
|
+
];
|
|
62
|
+
return [...map.values()].sort((a, b) => {
|
|
63
|
+
const ai = order.indexOf(a.name);
|
|
64
|
+
const bi = order.indexOf(b.name);
|
|
65
|
+
return ai >= 0 || bi >= 0
|
|
66
|
+
? (ai < 0 ? 99 : ai) - (bi < 0 ? 99 : bi)
|
|
67
|
+
: b.tokens - a.tokens;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sourceLabel(section: ContextSystemSectionViewData) {
|
|
72
|
+
return (
|
|
73
|
+
section.sourceRef?.path ??
|
|
74
|
+
section.sourceRef?.resourceId ??
|
|
75
|
+
section.sourceRef?.scope ??
|
|
76
|
+
"framework"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function SystemSectionRow({
|
|
81
|
+
section,
|
|
82
|
+
totalTokens,
|
|
83
|
+
}: {
|
|
84
|
+
section: ContextSystemSectionViewData;
|
|
85
|
+
totalTokens: number;
|
|
86
|
+
}) {
|
|
87
|
+
const share =
|
|
88
|
+
totalTokens > 0 ? Math.round((section.tokenCount / totalTokens) * 100) : 0;
|
|
89
|
+
return (
|
|
90
|
+
<div className="flex min-h-12 items-start gap-2 rounded-sm px-2 py-1.5">
|
|
91
|
+
<span className="mt-1 flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground">
|
|
92
|
+
<IconLock className="h-3.5 w-3.5" />
|
|
93
|
+
</span>
|
|
94
|
+
<div className="min-w-0 flex-1">
|
|
95
|
+
<div className="truncate text-[13px] font-medium leading-5 text-foreground">
|
|
96
|
+
{section.label}
|
|
97
|
+
</div>
|
|
98
|
+
<div className="truncate text-[11px] text-muted-foreground">
|
|
99
|
+
{sourceLabel(section)} · {formatContextTokens(section.tokenCount)}{" "}
|
|
100
|
+
tokens · {share}%
|
|
101
|
+
{section.tokenMethod === "estimate" ? " · estimated" : ""}
|
|
102
|
+
</div>
|
|
103
|
+
{section.preview ? (
|
|
104
|
+
<div className="mt-0.5 line-clamp-2 text-[11px] text-muted-foreground/80">
|
|
105
|
+
{section.preview}
|
|
106
|
+
</div>
|
|
107
|
+
) : null}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function ContextXRayPanelView({
|
|
114
|
+
manifest,
|
|
115
|
+
contextWindow,
|
|
116
|
+
optimistic,
|
|
117
|
+
onPin,
|
|
118
|
+
onEvict,
|
|
119
|
+
onRestore,
|
|
120
|
+
governanceLabels = {},
|
|
121
|
+
systemOrderedLabel = "System · ordered, not evictable",
|
|
122
|
+
titleLabel = "Context X-Ray",
|
|
123
|
+
}: {
|
|
124
|
+
manifest: ContextManifestViewData;
|
|
125
|
+
contextWindow: number;
|
|
126
|
+
optimistic: Map<string, ContextSegmentStatus>;
|
|
127
|
+
onPin: (segmentId: string) => void;
|
|
128
|
+
onEvict: (segmentId: string) => void;
|
|
129
|
+
onRestore: (segmentId: string) => void;
|
|
130
|
+
governanceLabels?: Partial<Record<"required" | "inherited" | "user", string>>;
|
|
131
|
+
systemOrderedLabel?: string;
|
|
132
|
+
titleLabel?: string;
|
|
133
|
+
}) {
|
|
134
|
+
const [mode, setMode] = useState<"list" | "map">("list");
|
|
135
|
+
const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
|
|
136
|
+
const segments = useMemo(
|
|
137
|
+
() => applyOptimisticStatus(manifest.segments, optimistic),
|
|
138
|
+
[manifest.segments, optimistic],
|
|
139
|
+
);
|
|
140
|
+
const groups = useMemo(() => groupedSegments(segments), [segments]);
|
|
141
|
+
const sections = manifest.systemSections ?? [];
|
|
142
|
+
const systemGroups = useMemo(
|
|
143
|
+
() =>
|
|
144
|
+
(["required", "inherited", "user"] as const).flatMap((governance) => {
|
|
145
|
+
const items = sections.filter(
|
|
146
|
+
(section) => section.governance === governance,
|
|
147
|
+
);
|
|
148
|
+
return items.length
|
|
149
|
+
? [
|
|
150
|
+
{
|
|
151
|
+
governance,
|
|
152
|
+
sections: items,
|
|
153
|
+
tokens: items.reduce(
|
|
154
|
+
(sum, section) => sum + section.tokenCount,
|
|
155
|
+
0,
|
|
156
|
+
),
|
|
157
|
+
},
|
|
158
|
+
]
|
|
159
|
+
: [];
|
|
160
|
+
}),
|
|
161
|
+
[sections],
|
|
162
|
+
);
|
|
163
|
+
const pct = Math.min(
|
|
164
|
+
100,
|
|
165
|
+
Math.round((manifest.totalTokens / contextWindow) * 100),
|
|
166
|
+
);
|
|
167
|
+
const headroom = Math.max(0, contextWindow - manifest.totalTokens);
|
|
168
|
+
const { conversationTokens: conversation, systemTokens: system } = manifest;
|
|
169
|
+
const pinned = segments.filter(
|
|
170
|
+
(segment) => segment.status === "pinned",
|
|
171
|
+
).length;
|
|
172
|
+
const evicted = segments.filter(
|
|
173
|
+
(segment) => segment.status === "evicted",
|
|
174
|
+
).length;
|
|
175
|
+
const details = [
|
|
176
|
+
`${formatContextTokens(headroom)} free`,
|
|
177
|
+
system > 0 ? `${formatContextTokens(system)} system` : null,
|
|
178
|
+
`${formatContextTokens(conversation)} conversation`,
|
|
179
|
+
pinned > 0 ? `${pinned} pinned` : null,
|
|
180
|
+
evicted > 0 ? `${evicted} evicted` : null,
|
|
181
|
+
manifest.tokenCountMethod === "estimate" ? "estimated" : null,
|
|
182
|
+
!manifest.enforceable ? "advisory" : null,
|
|
183
|
+
].filter((item): item is string => Boolean(item));
|
|
184
|
+
const toggle = (key: string) =>
|
|
185
|
+
setCollapsed((current) => {
|
|
186
|
+
const next = new Set(current);
|
|
187
|
+
if (next.has(key)) next.delete(key);
|
|
188
|
+
else next.add(key);
|
|
189
|
+
return next;
|
|
190
|
+
});
|
|
191
|
+
return (
|
|
192
|
+
<div className="flex max-h-[min(72vh,520px)] flex-col">
|
|
193
|
+
<div className="border-b border-border/60 px-3 py-2.5">
|
|
194
|
+
<div className="flex items-center justify-between gap-3">
|
|
195
|
+
<h2 className="min-w-0 truncate text-sm font-medium text-foreground">
|
|
196
|
+
{titleLabel}
|
|
197
|
+
</h2>
|
|
198
|
+
<div className="shrink-0 text-xs tabular-nums text-muted-foreground">
|
|
199
|
+
{formatContextTokens(manifest.totalTokens)} · {pct}%
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="mt-2 h-1 overflow-hidden rounded-full bg-muted/70">
|
|
203
|
+
<div
|
|
204
|
+
className="h-full w-full origin-left rounded-full bg-foreground transition-transform duration-200"
|
|
205
|
+
style={{ transform: `scaleX(${Math.min(1, pct / 100)})` }}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
<div className="mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-muted-foreground">
|
|
209
|
+
{details.map((detail) => (
|
|
210
|
+
<span key={detail}>{detail}</span>
|
|
211
|
+
))}
|
|
212
|
+
{manifest.reclaimedTokens > 0 ? (
|
|
213
|
+
<span className="font-medium text-emerald-600 dark:text-emerald-400">
|
|
214
|
+
-{formatContextTokens(manifest.reclaimedTokens)}
|
|
215
|
+
</span>
|
|
216
|
+
) : null}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="overflow-y-auto px-2 py-2">
|
|
220
|
+
<div className="mb-1 flex items-center justify-end">
|
|
221
|
+
<div className="inline-flex rounded-md bg-muted/40 p-0.5">
|
|
222
|
+
<Tooltip>
|
|
223
|
+
<TooltipTrigger asChild>
|
|
224
|
+
<button
|
|
225
|
+
type="button"
|
|
226
|
+
onClick={() => setMode("list")}
|
|
227
|
+
aria-label="Show context list"
|
|
228
|
+
className={cn(
|
|
229
|
+
"flex size-7 cursor-pointer items-center justify-center rounded text-muted-foreground",
|
|
230
|
+
mode === "list"
|
|
231
|
+
? "bg-background text-foreground shadow-sm"
|
|
232
|
+
: "hover:text-foreground",
|
|
233
|
+
)}
|
|
234
|
+
>
|
|
235
|
+
<IconListDetails className="h-3.5 w-3.5" />
|
|
236
|
+
</button>
|
|
237
|
+
</TooltipTrigger>
|
|
238
|
+
<TooltipContent>List</TooltipContent>
|
|
239
|
+
</Tooltip>
|
|
240
|
+
<Tooltip>
|
|
241
|
+
<TooltipTrigger asChild>
|
|
242
|
+
<button
|
|
243
|
+
type="button"
|
|
244
|
+
onClick={() => setMode("map")}
|
|
245
|
+
aria-label="Show context map"
|
|
246
|
+
className={cn(
|
|
247
|
+
"flex size-7 cursor-pointer items-center justify-center rounded text-muted-foreground",
|
|
248
|
+
mode === "map"
|
|
249
|
+
? "bg-background text-foreground shadow-sm"
|
|
250
|
+
: "hover:text-foreground",
|
|
251
|
+
)}
|
|
252
|
+
>
|
|
253
|
+
<IconChartTreemap className="h-3.5 w-3.5" />
|
|
254
|
+
</button>
|
|
255
|
+
</TooltipTrigger>
|
|
256
|
+
<TooltipContent>Map</TooltipContent>
|
|
257
|
+
</Tooltip>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
{mode === "map" ? (
|
|
261
|
+
<ContextTreemapView
|
|
262
|
+
segments={segments}
|
|
263
|
+
systemSections={sections}
|
|
264
|
+
onSelect={(id) => {
|
|
265
|
+
if (segments.some((segment) => segment.segmentId === id))
|
|
266
|
+
setCollapsed(new Set());
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
) : (
|
|
270
|
+
<div className="divide-y divide-border/60">
|
|
271
|
+
{systemGroups.length ? (
|
|
272
|
+
<div>
|
|
273
|
+
<div className="px-1.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.12em] text-muted-foreground">
|
|
274
|
+
{systemOrderedLabel}
|
|
275
|
+
</div>
|
|
276
|
+
{systemGroups.map((group) => {
|
|
277
|
+
const key = `system:${group.governance}`;
|
|
278
|
+
const isCollapsed = collapsed.has(key);
|
|
279
|
+
return (
|
|
280
|
+
<div key={key}>
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
onClick={() => toggle(key)}
|
|
284
|
+
className="flex w-full cursor-pointer items-center gap-2 rounded-sm px-1.5 py-2 text-left hover:bg-accent/35"
|
|
285
|
+
>
|
|
286
|
+
{isCollapsed ? (
|
|
287
|
+
<IconChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
|
|
288
|
+
) : (
|
|
289
|
+
<IconChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
|
|
290
|
+
)}
|
|
291
|
+
<IconLock className="h-3 w-3 text-muted-foreground" />
|
|
292
|
+
<span className="min-w-0 flex-1 truncate text-xs font-medium">
|
|
293
|
+
{governanceLabels[group.governance] ??
|
|
294
|
+
group.governance}
|
|
295
|
+
</span>
|
|
296
|
+
<span className="text-[11px] text-muted-foreground">
|
|
297
|
+
{formatContextTokens(group.tokens)} ·{" "}
|
|
298
|
+
{manifest.totalTokens > 0
|
|
299
|
+
? Math.round(
|
|
300
|
+
(group.tokens / manifest.totalTokens) * 100,
|
|
301
|
+
)
|
|
302
|
+
: 0}
|
|
303
|
+
%
|
|
304
|
+
</span>
|
|
305
|
+
</button>
|
|
306
|
+
{!isCollapsed ? (
|
|
307
|
+
<div className="pb-1">
|
|
308
|
+
{group.sections
|
|
309
|
+
.slice()
|
|
310
|
+
.sort((a, b) => b.tokenCount - a.tokenCount)
|
|
311
|
+
.map((section) => (
|
|
312
|
+
<SystemSectionRow
|
|
313
|
+
key={section.segmentId}
|
|
314
|
+
section={section}
|
|
315
|
+
totalTokens={manifest.totalTokens}
|
|
316
|
+
/>
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
) : null}
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
})}
|
|
323
|
+
</div>
|
|
324
|
+
) : null}
|
|
325
|
+
{groups.map((group) => {
|
|
326
|
+
const isCollapsed = collapsed.has(group.name);
|
|
327
|
+
return (
|
|
328
|
+
<div key={group.name}>
|
|
329
|
+
<button
|
|
330
|
+
type="button"
|
|
331
|
+
onClick={() => toggle(group.name)}
|
|
332
|
+
className="flex w-full cursor-pointer items-center gap-2 rounded-sm px-1.5 py-2 text-left hover:bg-accent/35"
|
|
333
|
+
>
|
|
334
|
+
{isCollapsed ? (
|
|
335
|
+
<IconChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
|
|
336
|
+
) : (
|
|
337
|
+
<IconChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
|
|
338
|
+
)}
|
|
339
|
+
<span
|
|
340
|
+
className={cn(
|
|
341
|
+
"h-2 w-2 rounded-full",
|
|
342
|
+
contextGroupColor(group.name),
|
|
343
|
+
)}
|
|
344
|
+
/>
|
|
345
|
+
<span className="min-w-0 flex-1 truncate text-xs font-medium">
|
|
346
|
+
{group.name}
|
|
347
|
+
</span>
|
|
348
|
+
<span className="text-[11px] text-muted-foreground">
|
|
349
|
+
{formatContextTokens(group.tokens)}
|
|
350
|
+
</span>
|
|
351
|
+
</button>
|
|
352
|
+
{!isCollapsed ? (
|
|
353
|
+
<div className="pb-1">
|
|
354
|
+
{group.segments
|
|
355
|
+
.slice()
|
|
356
|
+
.sort((a, b) => b.tokenCount - a.tokenCount)
|
|
357
|
+
.map((segment) => (
|
|
358
|
+
<ContextSegmentRowView
|
|
359
|
+
key={segment.segmentId}
|
|
360
|
+
segment={segment}
|
|
361
|
+
advisory={!manifest.enforceable}
|
|
362
|
+
onPin={() => onPin(segment.segmentId)}
|
|
363
|
+
onEvict={() => onEvict(segment.segmentId)}
|
|
364
|
+
onRestore={() => onRestore(segment.segmentId)}
|
|
365
|
+
/>
|
|
366
|
+
))}
|
|
367
|
+
</div>
|
|
368
|
+
) : null}
|
|
369
|
+
</div>
|
|
370
|
+
);
|
|
371
|
+
})}
|
|
372
|
+
</div>
|
|
373
|
+
)}
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ContextSegmentViewData } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function formatContextTokens(tokens: number | undefined): string {
|
|
4
|
+
const value = Math.max(0, Math.round(tokens ?? 0));
|
|
5
|
+
if (value >= 1000) {
|
|
6
|
+
const compact = value / 1000;
|
|
7
|
+
return `${compact >= 100 ? compact.toFixed(0) : compact.toFixed(1)}k`;
|
|
8
|
+
}
|
|
9
|
+
return String(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function contextStatusLabel(segment: ContextSegmentViewData): string {
|
|
13
|
+
if (segment.protected) return "Protected";
|
|
14
|
+
if (segment.status === "pinned") return "Pinned";
|
|
15
|
+
if (segment.status === "evicted") return "Evicted";
|
|
16
|
+
if (segment.status === "summarized") return "Summarized";
|
|
17
|
+
return "Active";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function contextGroupColor(group: string): string {
|
|
21
|
+
if (group === "Pinned") return "bg-emerald-500";
|
|
22
|
+
if (group === "Tool results") return "bg-amber-500";
|
|
23
|
+
if (group === "Files read") return "bg-sky-500";
|
|
24
|
+
if (group === "Thinking") return "bg-violet-500";
|
|
25
|
+
if (group === "Task & instructions") return "bg-rose-500";
|
|
26
|
+
if (group === "System · required") return "bg-slate-700";
|
|
27
|
+
if (group === "System · inherited") return "bg-indigo-500";
|
|
28
|
+
if (group === "System · user") return "bg-teal-500";
|
|
29
|
+
return "bg-slate-400";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function contextGroupFill(group: string): string {
|
|
33
|
+
if (group === "Pinned") return "#10b981";
|
|
34
|
+
if (group === "Tool results") return "#f59e0b";
|
|
35
|
+
if (group === "Files read") return "#0ea5e9";
|
|
36
|
+
if (group === "Thinking") return "#8b5cf6";
|
|
37
|
+
if (group === "Task & instructions") return "#f43f5e";
|
|
38
|
+
if (group === "System · required") return "#334155";
|
|
39
|
+
if (group === "System · inherited") return "#6366f1";
|
|
40
|
+
if (group === "System · user") return "#14b8a6";
|
|
41
|
+
return "#94a3b8";
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { ContextMeterView } from "./ContextMeter.js";
|
|
2
|
+
export {
|
|
3
|
+
ContextSegmentRowView,
|
|
4
|
+
SegmentProvenancePopoverView,
|
|
5
|
+
} from "./ContextSegmentRow.js";
|
|
6
|
+
export { ContextTreemapView } from "./ContextTreemap.js";
|
|
7
|
+
export { ContextXRayPanelView } from "./ContextXRayPanel.js";
|
|
8
|
+
export { formatContextTokens } from "./format.js";
|
|
9
|
+
export type {
|
|
10
|
+
ContextManifestViewData,
|
|
11
|
+
ContextSegmentStatus,
|
|
12
|
+
ContextSegmentViewData,
|
|
13
|
+
ContextSystemSectionViewData,
|
|
14
|
+
ContextTokenMethod,
|
|
15
|
+
} from "./types.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type ContextSegmentStatus =
|
|
2
|
+
| "active"
|
|
3
|
+
| "pinned"
|
|
4
|
+
| "evicted"
|
|
5
|
+
| "summarized";
|
|
6
|
+
|
|
7
|
+
export type ContextTokenMethod = "exact" | "estimate";
|
|
8
|
+
|
|
9
|
+
export type ContextGovernance = "required" | "inherited" | "user";
|
|
10
|
+
|
|
11
|
+
export interface ContextSegmentViewData {
|
|
12
|
+
segmentId: string;
|
|
13
|
+
group: string;
|
|
14
|
+
label: string;
|
|
15
|
+
tokenCount: number;
|
|
16
|
+
tokenMethod: ContextTokenMethod;
|
|
17
|
+
status: ContextSegmentStatus;
|
|
18
|
+
protected?: boolean;
|
|
19
|
+
msgIndex?: number;
|
|
20
|
+
partIndex?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ContextSystemSectionViewData {
|
|
24
|
+
segmentId: string;
|
|
25
|
+
label: string;
|
|
26
|
+
governance: ContextGovernance;
|
|
27
|
+
tokenCount: number;
|
|
28
|
+
tokenMethod: ContextTokenMethod;
|
|
29
|
+
sourceRef?: { resourceId?: string; path?: string; scope?: string };
|
|
30
|
+
preview?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ContextManifestViewData {
|
|
34
|
+
totalTokens: number;
|
|
35
|
+
rawTokens: number;
|
|
36
|
+
reclaimedTokens: number;
|
|
37
|
+
tokenCountMethod: ContextTokenMethod;
|
|
38
|
+
enforceable: boolean;
|
|
39
|
+
segments: ContextSegmentViewData[];
|
|
40
|
+
systemSections?: ContextSystemSectionViewData[];
|
|
41
|
+
systemTokens: number;
|
|
42
|
+
conversationTokens: number;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./visual-style-controls.js";
|