Skip to content
Merged
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
7 changes: 6 additions & 1 deletion cli/src/commands/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ function runBrokerServiceCommand(commandArgs: string[]): void {
stopBrokerUserService()
return
case 'restart':
if (commandArgs.length > 2) {
installBrokerUserService(commandArgs)
return
}
restartBrokerUserService()
return
case 'status':
Expand All @@ -252,7 +256,7 @@ ${chalk.bold('Usage:')}
maglev server service install [server args]
maglev server service start
maglev server service stop
maglev server service restart
maglev server service restart [server args]
maglev server service status
maglev server service logs [-f|--follow]
maglev server service uninstall
Expand Down Expand Up @@ -395,6 +399,7 @@ ${chalk.bold('Usage:')}
maglev server [options]
maglev server hubs
maglev server service install [options]
maglev server service restart [options]
maglev server service status
maglev server service logs --follow

Expand Down
27 changes: 26 additions & 1 deletion web/src/components/FilePreviewPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,29 @@ describe('FilePreviewPanel', () => {
expect(screen.getByPlaceholderText('Search in file')).toBeInTheDocument()
})

it('copies the review JSON path from the header', async () => {
const api = createApi({
readSessionFile: vi.fn().mockResolvedValue({
success: true,
content: encodeBase64('const value = 1'),
hash: 'file-hash'
}),
getSessionFileReviewThreads: vi.fn().mockResolvedValue({
success: true,
threads: []
})
})

renderPreview(api, '/repo/src/example.ts')

expect(await screen.findByText('/repo/src/example.ts')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Copy review JSON path' }))

await waitFor(() => {
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('.maglev-review/review.json')
})
})

it('uses rendered markdown in code mode and falls back to the code canvas in review mode', async () => {
const thread = makeThread({
id: 'thread-md',
Expand Down Expand Up @@ -212,7 +235,9 @@ describe('FilePreviewPanel', () => {
expect(screen.getByText('Consider tightening this heading.')).toBeInTheDocument()

await waitFor(() => {
expect(screen.queryByTestId('markdown-renderer')).not.toBeInTheDocument()
expect(screen.getAllByTestId('markdown-renderer').map((element) => element.textContent)).toEqual([
'Consider tightening this heading.'
])
})
})

Expand Down
17 changes: 17 additions & 0 deletions web/src/components/FilePreviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { isBinaryContent } from '@/lib/file-utils'
import type { FileReviewThread, WriteFileConflict } from '@/types/api'
import { MarkdownRenderer } from '@/components/MarkdownRenderer'
import { SourceReviewFileCard } from '@/components/review/SourceReviewFileCard'
import { CheckIcon, CopyIcon } from '@/components/icons'
import { useCopyToClipboard } from '@/hooks/useCopyToClipboard'
import { REVIEW_FILE_PATH } from '@/lib/review-file'

function CloseIcon() {
return (
Expand Down Expand Up @@ -149,6 +152,7 @@ export function FilePreviewPanel(props: {
const { scopeKey } = useAppContext()
const { sessionId, filePath, api, onClose, presentation = 'sidebar' } = props
const queryClient = useQueryClient()
const { copied: reviewPathCopied, copy: copyReviewPath } = useCopyToClipboard()
const isOverlay = presentation === 'overlay'

const fileQuery = useQuery({
Expand Down Expand Up @@ -442,6 +446,10 @@ export function FilePreviewPanel(props: {
}) ?? Promise.resolve({ success: false, error: 'API unavailable' }))
}, [api, runReviewMutation, sessionId])

const handleCopyReviewPath = useCallback(() => {
void copyReviewPath(REVIEW_FILE_PATH)
}, [copyReviewPath])

return (
<div className={`flex h-full w-full flex-col overflow-hidden ${isOverlay ? 'bg-[var(--app-surface-raised)]' : ''}`}>
<div className={`border-b border-[var(--app-border)] ${isOverlay ? 'px-4 pb-3 pt-[max(12px,env(safe-area-inset-top))]' : 'px-3 py-2.5'}`}>
Expand All @@ -452,6 +460,15 @@ export function FilePreviewPanel(props: {
</div>
</div>
<div className="flex shrink-0 items-center gap-2">
<button
type="button"
onClick={handleCopyReviewPath}
className="flex h-8 w-8 items-center justify-center rounded-full border border-[var(--app-border)] bg-[var(--app-surface-raised)] text-[var(--app-hint)] transition-colors hover:bg-[var(--app-subtle-bg)] hover:text-[var(--app-fg)]"
title={reviewPathCopied ? 'Copied review JSON path' : 'Copy review JSON path'}
aria-label={reviewPathCopied ? 'Copied review JSON path' : 'Copy review JSON path'}
>
{reviewPathCopied ? <CheckIcon className="h-4 w-4" /> : <CopyIcon className="h-4 w-4" />}
</button>
<button
type="button"
onClick={() => void handleRefresh()}
Expand Down
Loading