diff --git a/packages/app/src/components/comment-chip.tsx b/packages/app/src/components/comment-chip.tsx new file mode 100644 index 0000000000..e2e227786d --- /dev/null +++ b/packages/app/src/components/comment-chip.tsx @@ -0,0 +1,85 @@ +import { Show, type Component } from "solid-js" +import { FileIcon } from "@opencode-ai/ui/file-icon" +import { Icon } from "@opencode-ai/ui/icon" + +type Range = { + start: number + end: number +} + +type CommentChipProps = { + variant?: "preview" | "full" + path: string + label: string + selection?: Range + comment?: string + class?: string + onOpen?: () => void + onRemove?: () => void + removeLabel?: string +} + +const removeClass = + "absolute top-0 right-0 size-6 opacity-0 pointer-events-none transition-opacity group-hover:opacity-100 group-hover:pointer-events-auto group-focus-within:opacity-100 group-focus-within:pointer-events-auto" +const removeIconClass = + "absolute top-1 right-1 size-3.5 rounded-[var(--radius-sm)] flex items-center justify-center bg-transparent group-hover/remove:bg-surface-base-hover group-active/remove:bg-surface-base-active" + +export const CommentChip: Component = (props) => { + const variant = () => props.variant ?? "preview" + const range = () => { + const sel = props.selection + if (!sel) return + const start = Math.min(sel.start, sel.end) + const end = Math.max(sel.start, sel.end) + return { start, end } + } + + const pad = () => (props.onRemove ? "pr-7" : "pr-2") + + return ( +
props.onOpen?.()} + > +
+ +
+ {props.label} + + {(sel) => ( + + {sel().start === sel().end ? `:${sel().start}` : `:${sel().start}-${sel().end}`} + + )} + +
+
+ 0}> +
+ {props.comment} +
+
+ + + +
+ ) +} diff --git a/packages/app/src/components/prompt-input/context-items.tsx b/packages/app/src/components/prompt-input/context-items.tsx index 7ab8860dd5..522a64e7d4 100644 --- a/packages/app/src/components/prompt-input/context-items.tsx +++ b/packages/app/src/components/prompt-input/context-items.tsx @@ -1,10 +1,9 @@ import { Component, For, Show, createMemo } from "solid-js" -import { FileIcon } from "@opencode-ai/ui/file-icon" -import { Icon } from "@opencode-ai/ui/icon" import { Tooltip } from "@opencode-ai/ui/tooltip" import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path" import type { ContextItem, ImageAttachmentPart } from "@/context/prompt" import { PromptImageAttachment } from "./image-attachments" +import { CommentChip } from "@/components/comment-chip" type PromptContextItem = ContextItem & { key: string } @@ -73,46 +72,24 @@ export const PromptContextItems: Component = (props) => { openDelay={2000} class="shrink-0" > -
props.openComment(row.item)} - > -
- -
- {label} - - {(sel) => ( - - {sel().startLine === sel().endLine - ? `:${sel().startLine}` - : `:${sel().startLine}-${sel().endLine}`} - - )} - -
-
- - {(comment) =>
{comment()}
} -
- -
+ props.openComment(row.item)} + onRemove={() => props.remove(row.item)} + removeLabel={props.t("prompt.context.removeFile")} + /> ) }} diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index 74f2e8c2c1..efab52b46a 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -2,7 +2,6 @@ import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX } f import { createStore, produce } from "solid-js/store" import { useNavigate } from "@solidjs/router" import { Button } from "@opencode-ai/ui/button" -import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" @@ -15,7 +14,7 @@ import { TextField } from "@opencode-ai/ui/text-field" import type { AssistantMessage, Message as MessageType, Part, TextPart, UserMessage } from "@opencode-ai/sdk/v2" import { showToast } from "@opencode-ai/ui/toast" import { Binary } from "@opencode-ai/util/binary" -import { getFilename } from "@opencode-ai/util/path" +import { getFilenameTruncated } from "@opencode-ai/util/path" import { Popover as KobaltePopover } from "@kobalte/core/popover" import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture" import { SessionContextUsage } from "@/components/session-context-usage" @@ -29,6 +28,7 @@ import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" import { messageAgentColor } from "@/utils/agent" import { parseCommentNote, readCommentMetadata } from "@/utils/comment-note" +import { CommentChip } from "@/components/comment-chip" type MessageComment = { path: string @@ -960,40 +960,36 @@ export function MessageTimeline(props: { > 0}>
-
-
- - {(commentAccessor: () => MessageComment) => { - const comment = createMemo(() => commentAccessor()) - return ( - - {(c) => ( -
-
- - {getFilename(c().path)} - - {(selection) => ( - - {selection().startLine === selection().endLine - ? `:${selection().startLine}` - : `:${selection().startLine}-${selection().endLine}`} - - )} - -
-
- {c().comment} -
-
- )} -
- ) - }} -
+
+
+
+ + {(commentAccessor: () => MessageComment) => { + const comment = createMemo(() => commentAccessor()) + return ( + + {(c) => ( + + )} + + ) + }} + +