mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-21 08:14:31 +00:00
Compare commits
3 Commits
production
...
opencode/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
184b6d0117 | ||
|
|
6d58d899f7 | ||
|
|
b75a27d43e |
@@ -20,11 +20,8 @@ export const settingsNotificationsAgentSelector = '[data-action="settings-notifi
|
||||
export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]'
|
||||
export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]'
|
||||
export const settingsSoundsAgentSelector = '[data-action="settings-sounds-agent"]'
|
||||
export const settingsSoundsAgentEnabledSelector = '[data-action="settings-sounds-agent-enabled"]'
|
||||
export const settingsSoundsPermissionsSelector = '[data-action="settings-sounds-permissions"]'
|
||||
export const settingsSoundsPermissionsEnabledSelector = '[data-action="settings-sounds-permissions-enabled"]'
|
||||
export const settingsSoundsErrorsSelector = '[data-action="settings-sounds-errors"]'
|
||||
export const settingsSoundsErrorsEnabledSelector = '[data-action="settings-sounds-errors-enabled"]'
|
||||
export const settingsUpdatesStartupSelector = '[data-action="settings-updates-startup"]'
|
||||
export const settingsReleaseNotesSelector = '[data-action="settings-release-notes"]'
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
settingsNotificationsPermissionsSelector,
|
||||
settingsReleaseNotesSelector,
|
||||
settingsSoundsAgentSelector,
|
||||
settingsSoundsAgentEnabledSelector,
|
||||
settingsSoundsErrorsSelector,
|
||||
settingsSoundsPermissionsSelector,
|
||||
settingsThemeSelector,
|
||||
@@ -336,21 +335,19 @@ test("changing sound agent selection persists in localStorage", async ({ page, g
|
||||
expect(stored?.sounds?.agent).not.toBe("staplebops-01")
|
||||
})
|
||||
|
||||
test("disabling agent sound disables sound selection", async ({ page, gotoSession }) => {
|
||||
test("selecting none disables agent sound", async ({ page, gotoSession }) => {
|
||||
await gotoSession()
|
||||
|
||||
const dialog = await openSettings(page)
|
||||
const select = dialog.locator(settingsSoundsAgentSelector)
|
||||
const switchContainer = dialog.locator(settingsSoundsAgentEnabledSelector)
|
||||
const trigger = select.locator('[data-slot="select-select-trigger"]')
|
||||
await expect(select).toBeVisible()
|
||||
await expect(switchContainer).toBeVisible()
|
||||
await expect(trigger).toBeEnabled()
|
||||
|
||||
await switchContainer.locator('[data-slot="switch-control"]').click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await expect(trigger).toBeDisabled()
|
||||
await trigger.click()
|
||||
const items = page.locator('[data-slot="select-select-item"]')
|
||||
await expect(items.first()).toBeVisible()
|
||||
await items.first().click()
|
||||
|
||||
const stored = await page.evaluate((key) => {
|
||||
const raw = localStorage.getItem(key)
|
||||
|
||||
@@ -1101,6 +1101,7 @@ export function Session() {
|
||||
</Match>
|
||||
<Match when={message.role === "assistant"}>
|
||||
<AssistantMessage
|
||||
index={index()}
|
||||
last={lastAssistant()?.id === message.id}
|
||||
message={message as AssistantMessage}
|
||||
parts={sync.data.part[message.id] ?? []}
|
||||
@@ -1269,7 +1270,7 @@ function UserMessage(props: {
|
||||
)
|
||||
}
|
||||
|
||||
function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; last: boolean }) {
|
||||
function AssistantMessage(props: { index: number; message: AssistantMessage; parts: Part[]; last: boolean }) {
|
||||
const local = useLocal()
|
||||
const { theme } = useTheme()
|
||||
const sync = useSync()
|
||||
@@ -1279,12 +1280,32 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
|
||||
return props.message.finish && !["tool-calls", "unknown"].includes(props.message.finish)
|
||||
})
|
||||
|
||||
const duration = createMemo(() => {
|
||||
if (!final()) return 0
|
||||
if (!props.message.time.completed) return 0
|
||||
const user = messages().find((x) => x.role === "user" && x.id === props.message.parentID)
|
||||
if (!user || !user.time) return 0
|
||||
return props.message.time.completed - user.time.created
|
||||
const stats = createMemo(() => {
|
||||
if (!final() || !props.message.time.completed) return null
|
||||
|
||||
const list = messages()
|
||||
let tokens = 0
|
||||
let active = 0
|
||||
|
||||
for (let i = props.index; i >= 0; i--) {
|
||||
const msg = list[i]
|
||||
if (msg.role === "assistant") {
|
||||
tokens += msg.tokens?.output || 0
|
||||
if (msg.time.started && msg.time.streamed) {
|
||||
const delta = msg.time.streamed - msg.time.started
|
||||
if (delta > 0) active += delta
|
||||
}
|
||||
}
|
||||
if (msg.role === "user" && msg.id === props.message.parentID) {
|
||||
if (!msg.time?.created) return null
|
||||
const total = props.message.time.completed - msg.time.created
|
||||
if (total <= 0 || active <= 0) return null
|
||||
const tps = tokens > 0 ? tokens / (active / 1000) : null
|
||||
return { total, tps }
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -1334,8 +1355,14 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
|
||||
</span>{" "}
|
||||
<span style={{ fg: theme.text }}>{Locale.titlecase(props.message.mode)}</span>
|
||||
<span style={{ fg: theme.textMuted }}> · {props.message.modelID}</span>
|
||||
<Show when={duration()}>
|
||||
<span style={{ fg: theme.textMuted }}> · {Locale.duration(duration())}</span>
|
||||
<Show when={stats()}>
|
||||
{(s) => (
|
||||
<span style={{ fg: theme.textMuted }}>
|
||||
{" "}
|
||||
· {Locale.duration(s().total)}
|
||||
<Show when={s().tps !== null}> · {s().tps!.toFixed(1)} tok/s</Show>
|
||||
</span>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={props.message.error?.name === "MessageAbortedError"}>
|
||||
<span style={{ fg: theme.textMuted }}> · interrupted</span>
|
||||
|
||||
@@ -392,7 +392,9 @@ export namespace MessageV2 {
|
||||
role: z.literal("assistant"),
|
||||
time: z.object({
|
||||
created: z.number(),
|
||||
started: z.number().optional(),
|
||||
completed: z.number().optional(),
|
||||
streamed: z.number().optional(),
|
||||
}),
|
||||
error: z
|
||||
.discriminatedUnion("name", [
|
||||
|
||||
@@ -137,7 +137,9 @@ export namespace Message {
|
||||
.object({
|
||||
time: z.object({
|
||||
created: z.number(),
|
||||
started: z.number().optional(),
|
||||
completed: z.number().optional(),
|
||||
streamed: z.number().optional(),
|
||||
}),
|
||||
error: z
|
||||
.discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema])
|
||||
|
||||
@@ -57,6 +57,10 @@ export namespace SessionProcessor {
|
||||
switch (value.type) {
|
||||
case "start":
|
||||
SessionStatus.set(input.sessionID, { type: "busy" })
|
||||
if (!input.assistantMessage.time.started) {
|
||||
input.assistantMessage.time.started = Date.now()
|
||||
await Session.updateMessage(input.assistantMessage)
|
||||
}
|
||||
break
|
||||
|
||||
case "reasoning-start":
|
||||
@@ -337,6 +341,10 @@ export namespace SessionProcessor {
|
||||
break
|
||||
|
||||
case "finish":
|
||||
if (!input.assistantMessage.time.streamed) {
|
||||
input.assistantMessage.time.streamed = Date.now()
|
||||
await Session.updateMessage(input.assistantMessage)
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
|
||||
@@ -490,8 +490,10 @@
|
||||
}
|
||||
|
||||
[data-component="edit-content"] {
|
||||
border-radius: inherit;
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
max-height: 420px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
scrollbar-width: none;
|
||||
@@ -500,15 +502,24 @@
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-component="diff"] {
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="write-content"] {
|
||||
border-radius: inherit;
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
max-height: 240px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
[data-component="code"] {
|
||||
padding-bottom: 0px !important;
|
||||
padding-bottom: 0 !important;
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Hide scrollbar */
|
||||
@@ -1285,6 +1296,8 @@
|
||||
}
|
||||
|
||||
[data-component="apply-patch-file-diff"] {
|
||||
border-radius: inherit;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
@@ -1292,6 +1305,11 @@
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-component="diff"] {
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="tool-loaded-file"] {
|
||||
|
||||
Reference in New Issue
Block a user