Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
},
setMode: (mode) => setStore("mode", mode),
setPopover: (popover) => setStore("popover", popover),
editingID: () => prompt.editingID(),
setEditingID: (id) => prompt.setEditingID(id),
newSessionWorktree: () => props.newSessionWorktree,
onNewSessionWorktreeReset: props.onNewSessionWorktreeReset,
onSubmit: props.onSubmit,
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/components/prompt-input/submit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ describe("prompt submit worktree selection", () => {
resetHistoryNavigation: () => undefined,
setMode: () => undefined,
setPopover: () => undefined,
editingID: () => undefined,
setEditingID: () => undefined,
newSessionWorktree: () => selected,
onNewSessionWorktreeReset: () => undefined,
onSubmit: () => undefined,
Expand Down
13 changes: 13 additions & 0 deletions packages/app/src/components/prompt-input/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type PromptSubmitInput = {
resetHistoryNavigation: () => void
setMode: (mode: "normal" | "shell") => void
setPopover: (popover: "at" | "slash" | null) => void
editingID: Accessor<string | undefined>
setEditingID: (id: string | undefined) => void
newSessionWorktree?: Accessor<string | undefined>
onNewSessionWorktreeReset?: () => void
onSubmit?: () => void
Expand Down Expand Up @@ -132,6 +134,17 @@ export function createPromptSubmit(input: PromptSubmitInput) {
return
}

const editingID = input.editingID()
const sessionID = params.id

if (editingID && sessionID) {
if (input.working()) {
await sdk.client.session.abort({ sessionID }).catch(() => {})
}
await sdk.client.session.revert({ sessionID, messageID: editingID })
input.setEditingID(undefined)
}

input.addToHistory(currentPrompt, mode)
input.resetHistoryNavigation()

Expand Down
7 changes: 6 additions & 1 deletion packages/app/src/components/session-context-usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey))
const view = createMemo(() => layout.view(sessionKey))
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const messages = createMemo(() => {
const all = params.id ? (sync.data.message[params.id] ?? []) : []
const revert = sync.session.get(params.id ?? "")?.revert?.messageID
if (!revert) return all
return all.filter((m) => m.id < revert)
})

const usd = createMemo(
() =>
Expand Down
13 changes: 12 additions & 1 deletion packages/app/src/context/prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,14 @@ function createPromptSession(dir: string, id: string | undefined) {
createStore<{
prompt: Prompt
cursor?: number
editingID?: string
context: {
items: (ContextItem & { key: string })[]
}
}>({
prompt: clonePrompt(DEFAULT_PROMPT),
cursor: undefined,
editingID: undefined,
context: {
items: [],
},
Expand All @@ -178,6 +180,7 @@ function createPromptSession(dir: string, id: string | undefined) {
ready,
current: createMemo(() => store.prompt),
cursor: createMemo(() => store.cursor),
editingID: createMemo(() => store.editingID),
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)),
context: {
items: createMemo(() => store.context.items),
Expand All @@ -190,6 +193,9 @@ function createPromptSession(dir: string, id: string | undefined) {
setStore("context", "items", (items) => items.filter((x) => x.key !== key))
},
},
setEditingID(id: string | undefined) {
setStore("editingID", id)
},
set: actions.set,
reset: actions.reset,
}
Expand Down Expand Up @@ -246,14 +252,19 @@ export const { use: usePrompt, provider: PromptProvider } = createSimpleContext(
ready: () => session().ready(),
current: () => session().current(),
cursor: () => session().cursor(),
editingID: () => session().editingID(),
dirty: () => session().dirty(),
context: {
items: () => session().context.items(),
add: (item: ContextItem) => session().context.add(item),
remove: (key: string) => session().context.remove(key),
},
setEditingID: (id: string | undefined) => session().setEditingID(id),
set: (prompt: Prompt, cursorPosition?: number) => session().set(prompt, cursorPosition),
reset: () => session().reset(),
reset: () => {
session().setEditingID(undefined)
session().reset()
},
}
},
})
1 change: 1 addition & 0 deletions packages/app/src/i18n/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ export const dict = {
"common.delete": "حذف",
"common.close": "إغلاق",
"common.edit": "تحرير",
"ui.common.edit.message": "Editing message",
"common.loadMore": "تحميل المزيد",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "تبديل القائمة",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ export const dict = {
"common.delete": "Excluir",
"common.close": "Fechar",
"common.edit": "Editar",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Carregar mais",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "Alternar menu",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/bs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ export const dict = {
"common.delete": "Izbriši",
"common.close": "Zatvori",
"common.edit": "Uredi",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Učitaj još",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/da.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ export const dict = {
"common.delete": "Slet",
"common.close": "Luk",
"common.edit": "Rediger",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Indlæs flere",

"common.key.esc": "ESC",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ export const dict = {
"common.delete": "Löschen",
"common.close": "Schließen",
"common.edit": "Bearbeiten",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Mehr laden",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "Menü umschalten",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ export const dict = {
"common.delete": "Delete",
"common.close": "Close",
"common.edit": "Edit",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Load more",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ export const dict = {
"common.delete": "Eliminar",
"common.close": "Cerrar",
"common.edit": "Editar",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Cargar más",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ export const dict = {
"common.delete": "Supprimer",
"common.close": "Fermer",
"common.edit": "Modifier",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Charger plus",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "Basculer le menu",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ export const dict = {
"common.delete": "削除",
"common.close": "閉じる",
"common.edit": "編集",
"ui.common.edit.message": "Editing message",
"common.loadMore": "さらに読み込む",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "メニューを切り替え",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ export const dict = {
"common.delete": "삭제",
"common.close": "닫기",
"common.edit": "편집",
"ui.common.edit.message": "Editing message",
"common.loadMore": "더 불러오기",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "메뉴 토글",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/no.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ export const dict = {
"common.delete": "Slett",
"common.close": "Lukk",
"common.edit": "Rediger",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Last flere",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ export const dict = {
"common.delete": "Usuń",
"common.close": "Zamknij",
"common.edit": "Edytuj",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Załaduj więcej",
"common.key.esc": "ESC",
"sidebar.menu.toggle": "Przełącz menu",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ export const dict = {
"common.delete": "Удалить",
"common.close": "Закрыть",
"common.edit": "Редактировать",
"ui.common.edit.message": "Editing message",
"common.loadMore": "Загрузить ещё",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/th.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ export const dict = {
"common.delete": "ลบ",
"common.close": "ปิด",
"common.edit": "แก้ไข",
"ui.common.edit.message": "Editing message",
"common.loadMore": "โหลดเพิ่มเติม",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ export const dict = {
"common.delete": "删除",
"common.close": "关闭",
"common.edit": "编辑",
"ui.common.edit.message": "Editing message",
"common.loadMore": "加载更多",
"common.key.esc": "ESC",

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/i18n/zht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ export const dict = {
"common.delete": "刪除",
"common.close": "關閉",
"common.edit": "編輯",
"ui.common.edit.message": "Editing message",
"common.loadMore": "載入更多",

"common.key.esc": "ESC",
Expand Down
18 changes: 17 additions & 1 deletion packages/app/src/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { showToast } from "@opencode-ai/ui/toast"
import { SessionHeader, SessionContextTab, SortableTab, FileVisual, NewSessionView } from "@/components/session"
import { navMark, navParams } from "@/utils/perf"
import { same } from "@/utils/same"
import { extractPromptFromParts } from "@/utils/prompt"
import { createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "@/pages/session/helpers"
import { createScrollSpy } from "@/pages/session/scroll-spy"
import { createFileTabListSync } from "@/pages/session/file-tab-scroll"
Expand Down Expand Up @@ -306,6 +307,12 @@ export default function Page() {
const hasReview = createMemo(() => reviewCount() > 0)
const revertMessageID = createMemo(() => info()?.revert?.messageID)
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const visibleMessages = createMemo(() => {
const all = messages()
const revert = revertMessageID()
if (!revert) return all
return all.filter((m) => m.id < revert)
})
const messagesReady = createMemo(() => {
const id = params.id
if (!id) return true
Expand Down Expand Up @@ -1650,6 +1657,15 @@ export default function Page() {
lastUserMessageID={lastUserMessage()?.id}
expanded={store.expanded}
onToggleExpanded={(id) => setStore("expanded", id, (open: boolean | undefined) => !open)}
onEdit={async (messageID) => {
const parts = sync.data.part[messageID]
if (parts) {
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
prompt.set(restored)
prompt.setEditingID(messageID)
focusInput()
}
}}
/>
</Show>
</Match>
Expand Down Expand Up @@ -1729,7 +1745,7 @@ export default function Page() {
showAllFiles={showAllFiles}
reviewPanel={reviewPanel}
vm={{
messages,
messages: visibleMessages,
visibleUserMessages,
view,
info,
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/pages/session/message-timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export function MessageTimeline(props: {
lastUserMessageID?: string
expanded: Record<string, boolean>
onToggleExpanded: (id: string) => void
onEdit?: (messageID: string) => void
}) {
let touchGesture: number | undefined

Expand Down Expand Up @@ -318,6 +319,7 @@ export function MessageTimeline(props: {
lastUserMessageID={props.lastUserMessageID}
stepsExpanded={props.expanded[message.id] ?? false}
onStepsExpandedToggle={() => props.onToggleExpanded(message.id)}
onEdit={props.onEdit}
classes={{
root: "min-w-0 w-full relative",
content: "flex flex-col justify-between !overflow-visible",
Expand Down
20 changes: 20 additions & 0 deletions packages/app/src/pages/session/session-prompt-dock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { For, Show } from "solid-js"
import type { QuestionRequest } from "@opencode-ai/sdk/v2"
import { Button } from "@opencode-ai/ui/button"
import { BasicTool } from "@opencode-ai/ui/basic-tool"
import { Icon } from "@opencode-ai/ui/icon"
import { PromptInput } from "@/components/prompt-input"
import { QuestionDock } from "@/components/question-dock"
import { questionSubtitle } from "@/pages/session/session-prompt-helpers"
import { usePrompt } from "@/context/prompt"

export function SessionPromptDock(props: {
centered: boolean
Expand All @@ -22,6 +24,8 @@ export function SessionPromptDock(props: {
onSubmit: () => void
setPromptDockRef: (el: HTMLDivElement) => void
}) {
const prompt = usePrompt()

return (
<div
ref={props.setPromptDockRef}
Expand Down Expand Up @@ -122,6 +126,22 @@ export function SessionPromptDock(props: {
</div>
}
>
<Show when={prompt.editingID()}>
<div class="flex items-center justify-between bg-surface-base border border-border-base rounded-t-lg px-4 py-2 border-b-0 -mb-1">
<div class="flex items-center gap-2 text-12-medium text-text-weak">
<Icon name="edit" size="small" />
<span>{props.t("ui.common.edit.message")}</span>
</div>
<Button
variant="ghost"
size="small"
onClick={() => prompt.reset()}
class="h-6 text-12-medium hover:text-text-strong"
>
{props.t("ui.common.cancel")}
</Button>
</div>
</Show>
<PromptInput
ref={props.inputRef}
newSessionWorktree={props.newSessionWorktree}
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const icons = {
"circle-check": `<path d="M12.4987 7.91732L8.7487 12.5007L7.08203 10.834M17.9154 10.0007C17.9154 14.3729 14.371 17.9173 9.9987 17.9173C5.62644 17.9173 2.08203 14.3729 2.08203 10.0007C2.08203 5.6284 5.62644 2.08398 9.9987 2.08398C14.371 2.08398 17.9154 5.6284 17.9154 10.0007Z" stroke="currentColor" stroke-linecap="square"/>`,
copy: `<path d="M6.2513 6.24935V2.91602H17.0846V13.7493H13.7513M13.7513 6.24935V17.0827H2.91797V6.24935H13.7513Z" stroke="currentColor" stroke-linecap="round"/>`,
check: `<path d="M5 11.9657L8.37838 14.7529L15 5.83398" stroke="currentColor" stroke-linecap="square"/>`,
undo: `<path d="M7.08333 11.25L4.16667 14.1667L7.08333 17.0833M4.16667 14.1667H11.6667C14.4281 14.1667 16.6667 11.9281 16.6667 9.16667C16.6667 6.40525 14.4281 4.16667 11.6667 4.16667H3.33333" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>`,
photo: `<path d="M16.6665 16.6666L11.6665 11.6666L9.99984 13.3333L6.6665 9.99996L3.08317 13.5833M2.9165 2.91663H17.0832V17.0833H2.9165V2.91663ZM13.3332 7.49996C13.3332 8.30537 12.6803 8.95829 11.8748 8.95829C11.0694 8.95829 10.4165 8.30537 10.4165 7.49996C10.4165 6.69454 11.0694 6.04163 11.8748 6.04163C12.6803 6.04163 13.3332 6.69454 13.3332 7.49996Z" stroke="currentColor" stroke-linecap="square"/>`,
share: `<path d="M10.0013 12.0846L10.0013 3.33464M13.7513 6.66797L10.0013 2.91797L6.2513 6.66797M17.0846 10.418V17.0846H2.91797V10.418" stroke="currentColor" stroke-linecap="square"/>`,
download: `<path d="M13.9583 10.6257L10 14.584L6.04167 10.6257M10 2.08398V13.959M16.25 17.9173H3.75" stroke="currentColor" stroke-linecap="square"/>`,
Expand Down
Loading
Loading