Skip to content

Commit 3c6a9eb

Browse files
authored
fix(web-react): channels phone mockup, sidebar filters, settings tabs, copy icons, welcome page, theme colors (#124)
2 parents b6bedc9 + b9ca7e7 commit 3c6a9eb

9 files changed

Lines changed: 1489 additions & 459 deletions

File tree

packages/jcode-ui-core/src/primitives/MessageView.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ export interface MessageViewRenderSlots {
2020
renderContent?: (htmlOrText: string, message: Message) => ReactNode
2121
/** Render the avatar glyph for a role. */
2222
renderAvatar?: (role: Message['role']) => ReactNode
23+
/**
24+
* Render the hover action row (copy / edit). When provided, this replaces the
25+
* default text buttons entirely — the styled wrapper supplies icon buttons
26+
* while MessageView still owns the copy/edit state and handlers.
27+
*/
28+
renderActions?: (actions: MessageActions) => ReactNode
29+
}
30+
31+
/** Action handles passed to `renderActions` so the caller can wire its own UI. */
32+
export interface MessageActions {
33+
/** Whether the copy action just fired (show a "copied" affordance). */
34+
copied: boolean
35+
/** Copy the message content to the clipboard. */
36+
onCopy: () => void
37+
/** Whether the user may edit this message (role==='user' && !isRunning). */
38+
canEdit: boolean
39+
/** Enter edit mode for this message. */
40+
onEdit: () => void
41+
/** Whether the message is currently being edited (hide actions while true). */
42+
editing: boolean
2343
}
2444

2545
export interface MessageViewProps extends MessageViewRenderSlots {
@@ -39,6 +59,7 @@ export function MessageView({
3959
className,
4060
renderContent,
4161
renderAvatar,
62+
renderActions,
4263
}: MessageViewProps): ReactNode {
4364
const actions = useRuntimeActions()
4465
const [editing, setEditing] = useState(false)
@@ -115,10 +136,14 @@ export function MessageView({
115136
</div>
116137
)}
117138
{showCopy && !editing && (
118-
<div style={{ opacity: 0.6 }}>
119-
<button type="button" onClick={copy}>{copied ? 'Copied' : 'Copy'}</button>
120-
{canEdit && <button type="button" onClick={startEdit}>Edit</button>}
121-
</div>
139+
renderActions ? (
140+
renderActions({ copied, onCopy: copy, canEdit: !!canEdit, onEdit: startEdit, editing })
141+
) : (
142+
<div style={{ opacity: 0.6 }}>
143+
<button type="button" onClick={copy}>{copied ? 'Copied' : 'Copy'}</button>
144+
{canEdit && <button type="button" onClick={startEdit}>Edit</button>}
145+
</div>
146+
)
122147
)}
123148
</div>
124149
</div>

packages/jcode-ui/src/components/Message.tsx

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
*/
88

99
import { memo, useMemo } from 'react'
10+
import { CheckIcon, PencilSquareIcon, Square2StackIcon } from '@heroicons/react/24/outline'
1011
import type { Message as MessageData } from 'jcode-ui-core'
1112
import { MessageView } from 'jcode-ui-core/primitives'
13+
import type { MessageActions } from 'jcode-ui-core/primitives'
1214
import { renderMarkdown } from '../lib/markdown.js'
1315
import { Reasoning } from './Reasoning.js'
1416
import { Sources } from './Sources.js'
@@ -46,17 +48,59 @@ export const Message = memo(function Message({ message, canEdit }: MessageProps)
4648

4749
function Bubble({ message, canEdit, isUser }: { message: MessageData; canEdit?: boolean; isUser: boolean }) {
4850
return (
49-
<MessageView
50-
message={message}
51-
canEdit={canEdit}
52-
className={`jcode-selectable relative rounded-[var(--radius-lg)] px-3.5 py-2.5 text-[0.9rem] leading-relaxed ${
53-
isUser
54-
? 'bg-[var(--color-primary)] text-[var(--color-on-primary)]'
55-
: 'bg-[var(--color-surface)] text-[var(--color-foreground)] border border-[var(--color-border)]'
56-
}`}
57-
renderContent={(content) => <MarkdownBody html={content} />}
58-
renderAvatar={() => null}
59-
/>
51+
<div className="group/msg">
52+
<MessageView
53+
message={message}
54+
canEdit={canEdit}
55+
className={`jcode-selectable relative rounded-[var(--radius-lg)] px-3.5 py-2.5 text-[0.9rem] leading-relaxed ${
56+
isUser
57+
? 'bg-[var(--color-primary)] text-[var(--color-on-primary)]'
58+
: 'bg-[var(--color-surface)] text-[var(--color-foreground)] border border-[var(--color-border)]'
59+
}`}
60+
renderContent={(content) => <MarkdownBody html={content} />}
61+
renderAvatar={() => null}
62+
renderActions={(actions) => <MessageActionsRow {...actions} />}
63+
/>
64+
</div>
65+
)
66+
}
67+
68+
/**
69+
* Icon-based hover actions (mirrors ChatMessage.vue lines 172-189): a copy
70+
* button (Square2StackIcon → CheckIcon when copied) and, for editable user
71+
* messages, an edit button (PencilSquareIcon). Both render inside a row that
72+
* fades in on group hover/focus.
73+
*/
74+
function MessageActionsRow({ copied, onCopy, canEdit, onEdit }: MessageActions) {
75+
return (
76+
<div className="flex items-center gap-0.5 pl-9 opacity-0 transition-opacity duration-150 group-hover/msg:opacity-100 group-focus-within/msg:opacity-100">
77+
<button
78+
type="button"
79+
onClick={onCopy}
80+
title={copied ? 'Copied' : 'Copy'}
81+
aria-label={copied ? 'Copied' : 'Copy'}
82+
className="flex h-5 w-5 items-center justify-center rounded-[var(--radius-sm)] cursor-pointer transition-all hover:bg-[var(--color-secondary)] active:scale-90"
83+
style={{ color: 'var(--color-muted-foreground)' }}
84+
>
85+
{copied ? (
86+
<CheckIcon className="h-3 w-3" style={{ color: 'var(--color-accent-neutral)' }} />
87+
) : (
88+
<Square2StackIcon className="h-3 w-3" />
89+
)}
90+
</button>
91+
{canEdit && (
92+
<button
93+
type="button"
94+
onClick={onEdit}
95+
title="Edit"
96+
aria-label="Edit"
97+
className="flex h-5 w-5 items-center justify-center rounded-[var(--radius-sm)] cursor-pointer transition-all hover:bg-[var(--color-secondary)] active:scale-90"
98+
style={{ color: 'var(--color-muted-foreground)' }}
99+
>
100+
<PencilSquareIcon className="h-3 w-3" />
101+
</button>
102+
)}
103+
</div>
60104
)
61105
}
62106

web-react/src/App.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ function Shell({ activeView }: { activeView: 'chat' | 'automations' | 'channels'
231231
return (
232232
<RuntimeProvider runtime={runtime}>
233233
<ToolRegistryProvider registry={registry}>
234-
<div className="relative flex h-[100dvh] overflow-hidden bg-[var(--color-background)] text-[var(--color-foreground)]">
234+
<div className="app-shell relative flex h-[100dvh] overflow-hidden bg-[var(--color-background)] text-[var(--color-foreground)]">
235+
{/* Native title-bar drag strip — hidden in browser, shown in Tauri (CSS). */}
236+
<div className="titlebar-drag" data-tauri-drag-region aria-hidden="true" />
235237
{/* TopBar — floated top-right (only on chat view, like Vue). */}
236238
{activeView === 'chat' && (
237239
<TopBar

0 commit comments

Comments
 (0)