mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-19 01:51:21 +08:00
Merge pull request #11241 from Kilo-Org/stone-mind
feat(agent-manager): track feature button usage
This commit is contained in:
@@ -67,6 +67,7 @@ export enum TelemetryEventName {
|
||||
// Kilo-specific
|
||||
COMMIT_MSG_GENERATED = "Commit Message Generated",
|
||||
AGENT_MANAGER_OPENED = "Agent Manager Opened",
|
||||
AGENT_MANAGER_BUTTON_CLICKED = "Agent Manager Button Clicked",
|
||||
AGENT_MANAGER_SESSION_STARTED = "Agent Manager Session Started",
|
||||
AGENT_MANAGER_SESSION_COMPLETED = "Agent Manager Session Completed",
|
||||
AGENT_MANAGER_SESSION_STOPPED = "Agent Manager Session Stopped",
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import type { TelemetryRequest } from "../../webview-ui/src/types/messages/webview-messages"
|
||||
import { capture, tracker } from "../../webview-ui/agent-manager/telemetry"
|
||||
import { TelemetryEventName } from "../../src/services/telemetry/types"
|
||||
|
||||
describe("Agent Manager telemetry", () => {
|
||||
it("uses one stable event with low-cardinality button metadata", () => {
|
||||
const messages: TelemetryRequest[] = []
|
||||
|
||||
capture({ postMessage: (message) => messages.push(message) }, "fullscreen_review", "tab_toolbar", {
|
||||
action: "open",
|
||||
fileCount: 3,
|
||||
})
|
||||
|
||||
expect(messages).toEqual([
|
||||
{
|
||||
type: "telemetry",
|
||||
event: TelemetryEventName.AGENT_MANAGER_BUTTON_CLICKED,
|
||||
properties: {
|
||||
action: "open",
|
||||
fileCount: 3,
|
||||
source: "agent-manager",
|
||||
button: "fullscreen_review",
|
||||
surface: "tab_toolbar",
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("does not allow callers to override event dimensions", () => {
|
||||
const messages: TelemetryRequest[] = []
|
||||
|
||||
capture({ postMessage: (message) => messages.push(message) }, "apply_to_local", "apply_dialog", {
|
||||
source: "other",
|
||||
button: "other",
|
||||
surface: "other",
|
||||
})
|
||||
|
||||
expect(messages[0]?.properties).toMatchObject({
|
||||
source: "agent-manager",
|
||||
button: "apply_to_local",
|
||||
surface: "apply_dialog",
|
||||
})
|
||||
})
|
||||
|
||||
it("resolves current properties before running wrapped actions", () => {
|
||||
const messages: TelemetryRequest[] = []
|
||||
const order: string[] = []
|
||||
const metrics = tracker({
|
||||
postMessage: (message) => {
|
||||
messages.push(message)
|
||||
order.push("telemetry")
|
||||
},
|
||||
})
|
||||
const state = { action: "run" }
|
||||
const click = metrics.click(
|
||||
"run_script",
|
||||
"tab_toolbar",
|
||||
() => order.push("action"),
|
||||
() => state,
|
||||
)
|
||||
state.action = "stop"
|
||||
|
||||
click()
|
||||
|
||||
expect(messages[0]?.properties?.action).toBe("stop")
|
||||
expect(order).toEqual(["telemetry", "action"])
|
||||
})
|
||||
})
|
||||
@@ -134,6 +134,7 @@ import { createSidebarCollapse } from "./sidebar-collapse"
|
||||
import { SidebarToggleButton } from "./SidebarToggleButton"
|
||||
import { setTabWidths } from "./tab-widths"
|
||||
import { buildShortcutCategories } from "./shortcuts"
|
||||
import { tracker } from "./telemetry"
|
||||
import "./agent-manager.css"
|
||||
import "./agent-manager-review.css"
|
||||
const REVIEW_TAB_ID = "review"
|
||||
@@ -203,6 +204,7 @@ const AgentManagerContent: Component = () => {
|
||||
const [worktrees, setWorktrees] = createSignal<WorktreeState[]>([])
|
||||
const [managedSessions, setManagedSessions] = createSignal<ManagedSessionState[]>([])
|
||||
const [selection, setSelection] = createSignal<SidebarSelection>(LOCAL)
|
||||
const metrics = tracker(vscode)
|
||||
const [repoBranch, setRepoBranch] = createSignal<string | undefined>()
|
||||
const [busyWorktrees, setBusyWorktrees] = createSignal<Map<string, WorktreeBusyState>>(new Map())
|
||||
const [staleWorktreeIds, setStaleWorktreeIds] = createSignal<Set<string>>(new Set())
|
||||
@@ -447,6 +449,7 @@ const AgentManagerContent: Component = () => {
|
||||
if (!target) return
|
||||
if (!applyHasSelection()) return
|
||||
if (applyBusyForTarget()) return
|
||||
metrics.track("apply_to_local", "apply_dialog", { fileCount: applySelectedFiles().length })
|
||||
applyToLocal(target, applySelectedFiles())
|
||||
}
|
||||
|
||||
@@ -497,6 +500,7 @@ const AgentManagerContent: Component = () => {
|
||||
if (!sel || sel === LOCAL) return
|
||||
vscode.postMessage({ type: "agentManager.openWorktree", worktreeId: sel })
|
||||
}
|
||||
const openWindow = metrics.click("open_worktree_window", "tab_toolbar", openWorktreeDirectory)
|
||||
|
||||
const runWorktree = (id: string) => {
|
||||
const state = runStatuses()[id]?.state ?? "idle"
|
||||
@@ -1600,6 +1604,7 @@ const AgentManagerContent: Component = () => {
|
||||
const handleConfigureSetupScript = () => {
|
||||
vscode.postMessage({ type: "agentManager.configureSetupScript" })
|
||||
}
|
||||
const setupScript = metrics.click("configure_setup_script", "worktree_settings", handleConfigureSetupScript)
|
||||
|
||||
const handleChangeDefaultBaseBranch = () => {
|
||||
const [search, setSearch] = createSignal("")
|
||||
@@ -1735,6 +1740,7 @@ const AgentManagerContent: Component = () => {
|
||||
expandSidebar()
|
||||
vscode.postMessage({ type: "agentManager.createWorktree" })
|
||||
}
|
||||
const createWorktree = metrics.click("new_worktree", "worktrees", handleCreateWorktree)
|
||||
|
||||
// Advanced worktree dialog — opens a full dialog with prompt, versions, model, mode
|
||||
const showAdvancedWorktreeDialog = () => {
|
||||
@@ -1829,6 +1835,7 @@ const AgentManagerContent: Component = () => {
|
||||
const handlePromote = (sessionId: string, e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (!loaded()) return
|
||||
metrics.track("promote_session", "unassigned_session")
|
||||
vscode.postMessage({ type: "agentManager.promoteSession", sessionId })
|
||||
}
|
||||
|
||||
@@ -2204,7 +2211,7 @@ const AgentManagerContent: Component = () => {
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={t("agentManager.worktree.new")}
|
||||
onClick={handleCreateWorktree}
|
||||
onClick={createWorktree}
|
||||
disabled={!loaded()}
|
||||
/>
|
||||
<DropdownMenu gutter={4} placement="bottom-end">
|
||||
@@ -2217,7 +2224,7 @@ const AgentManagerContent: Component = () => {
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="am-split-menu">
|
||||
<DropdownMenu.Item onSelect={handleCreateWorktree}>
|
||||
<DropdownMenu.Item onSelect={createWorktree}>
|
||||
<span class="am-worktree-menu-gap" aria-hidden="true" />
|
||||
<DropdownMenu.ItemLabel class="am-worktree-menu-label">
|
||||
<span>{t("sidebar.session.newWorktree.from")}</span>
|
||||
@@ -2260,7 +2267,7 @@ const AgentManagerContent: Component = () => {
|
||||
size="small"
|
||||
variant="ghost"
|
||||
label={t("agentManager.shortcuts.title")}
|
||||
onClick={handleShowKeyboardShortcuts}
|
||||
onClick={metrics.click("keyboard_shortcuts", "worktrees_header", handleShowKeyboardShortcuts)}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
<DropdownMenu gutter={4} placement="bottom-end">
|
||||
@@ -2273,7 +2280,7 @@ const AgentManagerContent: Component = () => {
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="am-split-menu">
|
||||
<DropdownMenu.Item onSelect={handleConfigureSetupScript}>
|
||||
<DropdownMenu.Item onSelect={setupScript}>
|
||||
<DropdownMenu.ItemLabel>{t("agentManager.worktree.setupScript")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
@@ -2427,13 +2434,13 @@ const AgentManagerContent: Component = () => {
|
||||
prStatuses()[wt.id] !== undefined ? (prStatuses()[wt.id] ?? undefined) : undefined
|
||||
}
|
||||
runStatus={runStatuses()[wt.id]}
|
||||
onOpenPR={() =>
|
||||
vscode.postMessage({ type: "agentManager.openPR", worktreeId: wt.id })
|
||||
}
|
||||
onOpenPR={metrics.click("open_pull_request", "worktree_menu", () =>
|
||||
vscode.postMessage({ type: "agentManager.openPR", worktreeId: wt.id }),
|
||||
)}
|
||||
sections={sections()}
|
||||
currentSectionId={wt.sectionId}
|
||||
onMoveToSection={(secId) => moveToSection([wt.id], secId)}
|
||||
onMoveToNewSection={() => newSection()}
|
||||
onMoveToNewSection={metrics.click("new_section", "worktree_menu", () => newSection())}
|
||||
onClick={() => {
|
||||
if (pendingDelete() === wt.id) {
|
||||
confirmDeleteWorktree(wt.id)
|
||||
@@ -2448,9 +2455,9 @@ const AgentManagerContent: Component = () => {
|
||||
onCancelRename={cancelRename}
|
||||
onRemoveStale={() => confirmRemoveStaleWorktree(wt.id)}
|
||||
onCopyPath={() => navigator.clipboard.writeText(wt.path)}
|
||||
onOpen={() =>
|
||||
vscode.postMessage({ type: "agentManager.openWorktree", worktreeId: wt.id })
|
||||
}
|
||||
onOpen={metrics.click("open_worktree_window", "worktree_menu", () =>
|
||||
vscode.postMessage({ type: "agentManager.openWorktree", worktreeId: wt.id }),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -2518,7 +2525,7 @@ const AgentManagerContent: Component = () => {
|
||||
)
|
||||
})()}
|
||||
<Show when={worktrees().length === 0}>
|
||||
<button class="am-worktree-create" onClick={handleCreateWorktree}>
|
||||
<button class="am-worktree-create" onClick={createWorktree}>
|
||||
<Icon name="plus" size="small" />
|
||||
<span>{t("agentManager.worktree.new")}</span>
|
||||
</button>
|
||||
@@ -2607,7 +2614,11 @@ const AgentManagerContent: Component = () => {
|
||||
<Icon name="branch" size="small" />
|
||||
<ContextMenu.ItemLabel>{t("agentManager.session.openInWorktree")}</ContextMenu.ItemLabel>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item onSelect={() => openLocally(s.id)}>
|
||||
<ContextMenu.Item
|
||||
onSelect={metrics.click("open_session_locally", "unassigned_session_menu", () =>
|
||||
openLocally(s.id),
|
||||
)}
|
||||
>
|
||||
<Icon name="folder" size="small" />
|
||||
<ContextMenu.ItemLabel>{t("agentManager.session.openLocally")}</ContextMenu.ItemLabel>
|
||||
</ContextMenu.Item>
|
||||
@@ -2711,8 +2722,8 @@ const AgentManagerContent: Component = () => {
|
||||
newTerminalLabel: t("agentManager.terminal.new"),
|
||||
newSessionMenuLabel: t("agentManager.session.newSession"),
|
||||
moreOptionsLabel: t("agentManager.tab.newOptions"),
|
||||
onNewSession: handleAddSession,
|
||||
onNewTerminal: () => termHandlers.requestNew(),
|
||||
onNewSession: metrics.click("new_session", "tab_bar", handleAddSession),
|
||||
onNewTerminal: metrics.click("embedded_terminal", "new_tab_menu", () => termHandlers.requestNew()),
|
||||
})}
|
||||
</div>
|
||||
</Show>
|
||||
@@ -2738,7 +2749,7 @@ const AgentManagerContent: Component = () => {
|
||||
<Show when={isWorktree()}>
|
||||
<>
|
||||
<Tooltip value={t("agentManager.open.tooltip")} placement="bottom">
|
||||
<Button size="small" variant="ghost" icon="folder" onClick={openWorktreeDirectory}>
|
||||
<Button size="small" variant="ghost" icon="folder" onClick={openWindow}>
|
||||
{t("agentManager.open.button")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@@ -2774,7 +2785,14 @@ const AgentManagerContent: Component = () => {
|
||||
variant="ghost"
|
||||
icon={active() ? "stop" : "play"}
|
||||
disabled={rs()?.state === "stopping"}
|
||||
onClick={() => runWorktree(rid())}
|
||||
onClick={metrics.click(
|
||||
"run_script",
|
||||
"tab_toolbar",
|
||||
() => runWorktree(rid()),
|
||||
() => ({
|
||||
action: active() ? "stop" : configured() ? "run" : "configure",
|
||||
}),
|
||||
)}
|
||||
>
|
||||
{active() ? "Stop" : "Run"}
|
||||
</Button>
|
||||
@@ -2794,7 +2812,9 @@ const AgentManagerContent: Component = () => {
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="am-split-menu">
|
||||
<DropdownMenu.Item onSelect={configureRunScript}>
|
||||
<DropdownMenu.Item
|
||||
onSelect={metrics.click("configure_run_script", "run_menu", configureRunScript)}
|
||||
>
|
||||
<Icon name="settings-gear" size="small" />
|
||||
<DropdownMenu.ItemLabel>{t("agentManager.run.configure")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
@@ -2813,6 +2833,9 @@ const AgentManagerContent: Component = () => {
|
||||
<button
|
||||
class={`am-diff-toggle-btn ${diffOpen() && !reviewActive() ? "am-tab-diff-btn-active" : ""} ${hasChanges() ? "am-diff-toggle-has-changes" : ""}`}
|
||||
onClick={() => {
|
||||
metrics.track("side_review", "tab_toolbar", {
|
||||
action: diffOpen() && !reviewActive() ? "close" : "open",
|
||||
})
|
||||
if (reviewActive()) {
|
||||
closeReviewTab()
|
||||
setSidePanel("diff")
|
||||
@@ -2845,7 +2868,7 @@ const AgentManagerContent: Component = () => {
|
||||
variant="ghost"
|
||||
label={t("command.review.toggle")}
|
||||
class={reviewActive() ? "am-tab-diff-btn-active" : ""}
|
||||
onClick={toggleReviewTab}
|
||||
onClick={metrics.click("fullscreen_review", "tab_toolbar", toggleReviewTab)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
@@ -2864,6 +2887,7 @@ const AgentManagerContent: Component = () => {
|
||||
variant="ghost"
|
||||
label={t("agentManager.tab.openTerminal")}
|
||||
onClick={() => {
|
||||
metrics.track("vscode_terminal", "tab_toolbar")
|
||||
const id = session.currentSessionID()
|
||||
if (id) vscode.postMessage({ type: "agentManager.showTerminal", sessionId: id })
|
||||
else if (selection() === LOCAL) vscode.postMessage({ type: "agentManager.showLocalTerminal" })
|
||||
@@ -3020,6 +3044,7 @@ const AgentManagerContent: Component = () => {
|
||||
if (!loaded()) return
|
||||
const sid = session.currentSessionID()
|
||||
if (!sid) return
|
||||
metrics.track("open_session_locally", "readonly_banner")
|
||||
openLocally(sid)
|
||||
}}
|
||||
>
|
||||
@@ -3031,7 +3056,9 @@ const AgentManagerContent: Component = () => {
|
||||
onClick={() => {
|
||||
if (!loaded()) return
|
||||
const sid = session.currentSessionID()
|
||||
if (sid) vscode.postMessage({ type: "agentManager.promoteSession", sessionId: sid })
|
||||
if (!sid) return
|
||||
metrics.track("promote_session", "readonly_banner")
|
||||
vscode.postMessage({ type: "agentManager.promoteSession", sessionId: sid })
|
||||
}}
|
||||
>
|
||||
{t("agentManager.session.openInWorktree")}
|
||||
@@ -3073,8 +3100,13 @@ const AgentManagerContent: Component = () => {
|
||||
comments={reviewComments()}
|
||||
onCommentsChange={setReviewCommentsForSelection}
|
||||
composer={reviewComposer}
|
||||
onClose={() => setSidePanel(null)}
|
||||
onExpand={selection() !== null ? openReviewTab : undefined}
|
||||
onSendClick={() => metrics.track("send_review_comments", "side_review")}
|
||||
onClose={metrics.click("side_review_close", "side_review", () => setSidePanel(null))}
|
||||
onExpand={
|
||||
selection() !== null
|
||||
? metrics.click("fullscreen_review", "side_review", openReviewTab, { action: "open" })
|
||||
: undefined
|
||||
}
|
||||
onRequestDiff={requestDiffFile}
|
||||
onOpenFile={(file, line) => {
|
||||
const id = currentDiffSessionId()
|
||||
@@ -3082,7 +3114,7 @@ const AgentManagerContent: Component = () => {
|
||||
vscode.postMessage({ type: "agentManager.openFile", sessionId: id, filePath: file, line })
|
||||
else if (selection() === LOCAL) vscode.postMessage({ type: "openFile", filePath: file, line })
|
||||
}}
|
||||
onRevertFile={revertCtl.revert}
|
||||
onRevertFile={metrics.use("revert_file", "side_review", revertCtl.revert)}
|
||||
revertingFiles={revertCtl.reverting()}
|
||||
activeTerminalId={terms.activeId()}
|
||||
/>
|
||||
@@ -3104,6 +3136,7 @@ const AgentManagerContent: Component = () => {
|
||||
onCommentsChange={setReviewCommentsForSelection}
|
||||
composer={reviewComposer}
|
||||
onSendAll={closeReviewTab}
|
||||
onSendClick={() => metrics.track("send_review_comments", "fullscreen_review")}
|
||||
diffStyle={reviewDiffStyle()}
|
||||
onDiffStyleChange={setSharedDiffStyle}
|
||||
markdownRender={markdown.render()}
|
||||
@@ -3114,10 +3147,10 @@ const AgentManagerContent: Component = () => {
|
||||
if (id) vscode.postMessage({ type: "agentManager.openFile", sessionId: id, filePath: file, line })
|
||||
else if (selection() === LOCAL) vscode.postMessage({ type: "openFile", filePath: file, line })
|
||||
}}
|
||||
onRevertFile={revertCtl.revert}
|
||||
onRevertFile={metrics.use("revert_file", "fullscreen_review", revertCtl.revert)}
|
||||
revertingFiles={revertCtl.reverting()}
|
||||
activeTerminalId={terms.activeId()}
|
||||
onClose={closeReviewTab}
|
||||
onClose={metrics.click("fullscreen_review", "fullscreen_review", closeReviewTab, { action: "close" })}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
@@ -75,6 +75,7 @@ interface DiffPanelProps {
|
||||
onCommentsChange: (comments: ReviewComment[]) => void
|
||||
composer?: ReviewComposer
|
||||
onSendAll?: () => void
|
||||
onSendClick?: () => void
|
||||
onClose: () => void
|
||||
onExpand?: () => void
|
||||
onRequestDiff?: (file: string) => void
|
||||
@@ -439,6 +440,11 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
|
||||
props.onSendAll?.()
|
||||
}
|
||||
|
||||
const sendAllClick = () => {
|
||||
props.onSendClick?.()
|
||||
sendAllToChat()
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key !== "Enter") return
|
||||
if (!(e.metaKey || e.ctrlKey)) return
|
||||
@@ -731,7 +737,7 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
|
||||
{comments().length} comment{comments().length !== 1 ? "s" : ""}
|
||||
</span>
|
||||
<TooltipKeybind title={t("agentManager.review.sendAllToChat")} keybind={sendAllKeybind()} placement="top">
|
||||
<Button variant="primary" size="small" onClick={sendAllToChat}>
|
||||
<Button variant="primary" size="small" onClick={sendAllClick}>
|
||||
{t("agentManager.review.sendAllToChat")}
|
||||
</Button>
|
||||
</TooltipKeybind>
|
||||
|
||||
@@ -34,6 +34,7 @@ import { useSpeechToText } from "../src/components/speech-to-text/useSpeechToTex
|
||||
import { convertToMentionPath } from "../src/utils/path-mentions"
|
||||
import { insertSpacedText } from "../src/components/chat/prompt-input-utils"
|
||||
import { BranchSelect, BranchSelectPopover } from "../src/components/shared/BranchSelect"
|
||||
import { tracker } from "./telemetry"
|
||||
|
||||
type VersionCount = 1 | 2 | 3 | 4
|
||||
const VERSION_OPTIONS: VersionCount[] = [1, 2, 3, 4]
|
||||
@@ -71,6 +72,10 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
const session = useSession()
|
||||
const provider = useProvider()
|
||||
const { config } = useConfig()
|
||||
const metrics = tracker(vscode)
|
||||
const track = (button: string, properties?: Record<string, string | number | boolean | undefined>) =>
|
||||
metrics.track(button, "configure_worktree_dialog", properties)
|
||||
const click = metrics.click
|
||||
|
||||
const [tab, setTab] = createSignal<DialogTab>("new")
|
||||
|
||||
@@ -209,6 +214,8 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
if (compareMode() && totalAllocations(modelAllocations()) === 0) return false
|
||||
return true
|
||||
}
|
||||
const total = () => (compareMode() ? totalAllocations(modelAllocations()) : versions())
|
||||
const mode = () => (compareMode() ? "compare_models" : versions() > 1 ? "multiple_versions" : "single")
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!canSubmit()) return
|
||||
@@ -224,7 +231,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
|
||||
const isCompare = compareMode()
|
||||
const allocations = isCompare ? allocationsToArray(modelAllocations()) : undefined
|
||||
const count = isCompare ? totalAllocations(modelAllocations()) : versions()
|
||||
const count = total()
|
||||
const sel = isCompare ? null : model()
|
||||
|
||||
vscode.postMessage({
|
||||
@@ -320,6 +327,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
|
||||
const handleBranchSelect = (name: string) => {
|
||||
if (isPending()) return
|
||||
track("import_branch")
|
||||
setImportPending(true)
|
||||
setBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
@@ -333,7 +341,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
<button
|
||||
class="am-tab-switcher-pill"
|
||||
classList={{ "am-tab-switcher-pill-active": tab() === "new" }}
|
||||
onClick={() => setTab("new")}
|
||||
onClick={click("switch_dialog_tab", "configure_worktree_dialog", () => setTab("new"), { tab: "new" })}
|
||||
type="button"
|
||||
>
|
||||
{t("agentManager.dialog.tab.new")}
|
||||
@@ -341,7 +349,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
<button
|
||||
class="am-tab-switcher-pill"
|
||||
classList={{ "am-tab-switcher-pill-active": tab() === "import" }}
|
||||
onClick={() => setTab("import")}
|
||||
onClick={click("switch_dialog_tab", "configure_worktree_dialog", () => setTab("import"), { tab: "import" })}
|
||||
type="button"
|
||||
>
|
||||
{t("agentManager.dialog.tab.import")}
|
||||
@@ -460,7 +468,16 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
</div>
|
||||
|
||||
{/* Advanced options toggle */}
|
||||
<button class="am-advanced-toggle" onClick={() => setShowAdvanced(!showAdvanced())} type="button">
|
||||
<button
|
||||
class="am-advanced-toggle"
|
||||
onClick={click(
|
||||
"advanced_options",
|
||||
"configure_worktree_dialog",
|
||||
() => setShowAdvanced(!showAdvanced()),
|
||||
() => ({ action: showAdvanced() ? "close" : "open" }),
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
<Icon name={showAdvanced() ? "chevron-down" : "chevron-right"} size="small" />
|
||||
<span>{t("agentManager.dialog.advancedOptions")}</span>
|
||||
</button>
|
||||
@@ -584,7 +601,9 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
<button
|
||||
class="am-nv-pill"
|
||||
classList={{ "am-nv-pill-active": versions() === count }}
|
||||
onClick={() => setVersions(count)}
|
||||
onClick={click("version_count", "configure_worktree_dialog", () => setVersions(count), {
|
||||
count,
|
||||
})}
|
||||
type="button"
|
||||
>
|
||||
{count}
|
||||
@@ -595,7 +614,13 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
placement="top"
|
||||
contentClass="am-tooltip-wrap"
|
||||
>
|
||||
<button class="am-nv-pill am-nv-pill-compare" onClick={() => setCompareMode(true)} type="button">
|
||||
<button
|
||||
class="am-nv-pill am-nv-pill-compare"
|
||||
onClick={click("compare_models", "configure_worktree_dialog", () => setCompareMode(true), {
|
||||
action: "open",
|
||||
})}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="layers" size="small" />
|
||||
<span class="am-nv-pill-compare-label">{t("agentManager.dialog.compareModels")}</span>
|
||||
</button>
|
||||
@@ -622,6 +647,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
<button
|
||||
class="am-nv-pill-back"
|
||||
onClick={() => {
|
||||
track("compare_models", { action: "close" })
|
||||
setCompareMode(false)
|
||||
setModelAllocations(new Map())
|
||||
}}
|
||||
@@ -670,7 +696,21 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
</div>
|
||||
{/* Submit button — fixed footer, always visible */}
|
||||
<div class="am-nv-dialog-footer">
|
||||
<Button variant="primary" size="large" class="am-nv-submit" onClick={handleSubmit} disabled={!canSubmit()}>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="large"
|
||||
class="am-nv-submit"
|
||||
onClick={click("create_worktree", "configure_worktree_dialog", handleSubmit, () => ({
|
||||
mode: mode(),
|
||||
versionCount: total(),
|
||||
advanced: showAdvanced(),
|
||||
customBranch: showAdvanced() && !!branchName().trim(),
|
||||
customBase: showAdvanced() && !!baseBranch(),
|
||||
hasPrompt: !!prompt().trim(),
|
||||
hasAttachments: imageAttach.images().length > 0,
|
||||
}))}
|
||||
disabled={!canSubmit()}
|
||||
>
|
||||
<Show
|
||||
when={!starting()}
|
||||
fallback={
|
||||
@@ -714,7 +754,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
onClick={handlePRSubmit}
|
||||
onClick={click("import_pull_request", "configure_worktree_dialog", handlePRSubmit)}
|
||||
disabled={!prUrl().trim() || isPending()}
|
||||
>
|
||||
<Show when={prPending()} fallback={t("agentManager.import.open")}>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { TelemetryEventName } from "../../src/services/telemetry/types"
|
||||
import type { TelemetryRequest } from "../src/types/messages/webview-messages"
|
||||
|
||||
interface Target {
|
||||
postMessage(message: TelemetryRequest): void
|
||||
}
|
||||
|
||||
type Value = string | number | boolean | undefined
|
||||
type Properties = Record<string, Value>
|
||||
type Input = Properties | (() => Properties)
|
||||
|
||||
export function capture(target: Target, button: string, surface: string, properties: Properties = {}) {
|
||||
target.postMessage({
|
||||
type: "telemetry",
|
||||
event: TelemetryEventName.AGENT_MANAGER_BUTTON_CLICKED,
|
||||
properties: {
|
||||
...properties,
|
||||
source: "agent-manager",
|
||||
button,
|
||||
surface,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function clicked(target: Target, button: string, surface: string, action: () => void, properties: Input = {}) {
|
||||
return () => {
|
||||
capture(target, button, surface, typeof properties === "function" ? properties() : properties)
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
function used<T>(target: Target, button: string, surface: string, action: (value: T) => void) {
|
||||
return (value: T) => {
|
||||
capture(target, button, surface)
|
||||
action(value)
|
||||
}
|
||||
}
|
||||
|
||||
export function tracker(target: Target) {
|
||||
return {
|
||||
track: (button: string, surface: string, properties?: Properties) => capture(target, button, surface, properties),
|
||||
click: (button: string, surface: string, action: () => void, properties?: Input) =>
|
||||
clicked(target, button, surface, action, properties),
|
||||
use: <T>(button: string, surface: string, action: (value: T) => void) => used(target, button, surface, action),
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ interface FullScreenDiffViewProps {
|
||||
onCommentsChange: (comments: ReviewComment[]) => void
|
||||
composer?: ReviewComposer
|
||||
onSendAll?: () => void
|
||||
onSendClick?: () => void
|
||||
diffStyle: DiffStyle
|
||||
onDiffStyleChange: (style: DiffStyle) => void
|
||||
markdownRender?: boolean
|
||||
@@ -443,6 +444,11 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
|
||||
props.onSendAll?.()
|
||||
}
|
||||
|
||||
const sendAllClick = () => {
|
||||
props.onSendClick?.()
|
||||
sendAllToChat()
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key !== "Enter") return
|
||||
if (!(e.metaKey || e.ctrlKey)) return
|
||||
@@ -573,7 +579,7 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
|
||||
keybind={sendAllKeybind()}
|
||||
placement="bottom"
|
||||
>
|
||||
<Button variant="primary" size="small" onClick={sendAllToChat}>
|
||||
<Button variant="primary" size="small" onClick={sendAllClick}>
|
||||
{t("agentManager.review.sendAllToChatWithCount", { count: comments().length })}
|
||||
</Button>
|
||||
</TooltipKeybind>
|
||||
|
||||
Reference in New Issue
Block a user