Skip to content

Commit 9681e35

Browse files
committed
Add prompt preview feature to inspect the compiled prompt in a temporary document
- handle [Fragment] by opening the overall message (the same as copied to clipboard by `apps/editor/src/views/prompt/backend/message-handlers/handle-copy-prompt.ts`) in an empty editor (not in a new window though) - in `apps/editor/src/views/prompt/backend/message-handlers/handle-preview-prompt.ts` save in tmp and open this temporary file - do we render '# Files' heading even without files in this section? should not - in 'ask' prompt type, place '---' separator above instructions ├── apps │ └── editor │ └── src │ ├── utils │ │ └── prompt-builder.ts │ └── views │ └── prompt │ ├── backend │ │ ├── message-handlers │ │ │ ├── handle-autofill.ts │ │ │ ├── handle-copy-prompt.ts │ │ │ ├── handle-open-prompt-image.ts │ │ │ ├── handle-open-prompt-pasted-text.ts │ │ │ ├── handle-open-website.ts │ │ │ ├── handle-paste-url.ts │ │ │ ├── handle-preview-generated-code.ts │ │ │ ├── handle-preview-prompt.ts │ │ │ ├── handle-preview-web-configuration.ts │ │ │ ├── handle-response-preview.ts │ │ │ ├── handle-save-prompt-image.ts │ │ │ ├── handle-save-prompt-pasted-text.ts │ │ │ └── index.ts │ │ └── prompt-view-provider.ts │ ├── frontend │ │ ├── Main │ │ │ ├── Main.tsx │ │ │ └── MainView │ │ │ └── MainView.tsx │ │ ├── Prompt.tsx │ │ └── hooks │ │ └── use-prompt │ │ └── use-prompt.ts │ └── types │ └── messages.ts └── packages └── ui └── src └── components └── editor └── prompt └── SelectedFiles ├── SelectedFiles.stories.tsx └── SelectedFiles.tsx
1 parent 07a138c commit 9681e35

11 files changed

Lines changed: 134 additions & 27 deletions

File tree

‎apps/editor/src/utils/prompt-builder.ts‎

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,44 +70,49 @@ export namespace PromptBuilder {
7070
}
7171

7272
if (params.context_text !== undefined) {
73-
if (params.context_text) {
74-
full_prompt += `# Files\n\n${params.context_text}`
75-
}
76-
if (active_file_context) {
77-
full_prompt += active_file_context
73+
if (params.context_text || active_file_context) {
74+
full_prompt += `# Files\n\n`
75+
if (params.context_text) {
76+
full_prompt += params.context_text
77+
}
78+
if (active_file_context) {
79+
full_prompt += active_file_context
80+
}
7881
}
7982
} else {
80-
part1 = `# Files\n\n${params.other_files || ''}`
81-
part2 += `${params.recent_files || ''}${active_file_context}`
82-
full_prompt = part1 + part2
83+
const has_files = !!(
84+
params.other_files ||
85+
params.recent_files ||
86+
active_file_context
87+
)
88+
if (has_files) {
89+
part1 = `# Files\n\n${params.other_files || ''}`
90+
part2 += `${params.recent_files || ''}${active_file_context}`
91+
full_prompt = part1 + part2
92+
}
8393
}
8494

8595
if (params.skill_definitions) {
8696
part2 += params.skill_definitions
8797
full_prompt += params.skill_definitions
8898
}
8999

90-
const has_system = !!params.system_instructions
91-
const separator = params.separator && has_system
92-
93100
if (params.system_instructions) {
94101
const sys = params.system_instructions.trimEnd()
95102
if (sys) {
96-
part2 += `${sys}\n`
97-
full_prompt += `${sys}\n`
98-
if (separator) {
99-
part2 += `\n---\n`
100-
full_prompt += `\n---\n\n`
101-
if (params.user_instructions) {
102-
part2 += `\n`
103-
}
104-
} else {
105-
part2 += `\n\n`
106-
full_prompt += `\n\n`
107-
}
103+
part2 += `${sys}\n\n`
104+
full_prompt += `${sys}\n\n`
108105
}
109106
}
110107

108+
if (
109+
params.separator &&
110+
(part1.length > 0 || part2.length > 0 || full_prompt.length > 0)
111+
) {
112+
part2 = part2.trimEnd() + '\n\n---\n\n'
113+
full_prompt = full_prompt.trimEnd() + '\n\n---\n\n'
114+
}
115+
111116
if (params.user_instructions) {
112117
part2 += params.user_instructions
113118
full_prompt += params.user_instructions
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { PromptViewProvider } from '@/views/prompt/backend/prompt-view-provider'
2+
import * as vscode from 'vscode'
3+
import { build_prompt_payload } from './utils/build-prompt-payload'
4+
import {
5+
EDIT_FORMAT_INSTRUCTIONS_WHOLE,
6+
EDIT_FORMAT_INSTRUCTIONS_TRUNCATED,
7+
EDIT_FORMAT_INSTRUCTIONS_SEARCH_REPLACE,
8+
EDIT_FORMAT_INSTRUCTIONS_DIFF
9+
} from '@/constants/edit-format-instructions'
10+
import { PromptBuilder } from '@/utils/prompt-builder'
11+
import * as os from 'os'
12+
import * as path from 'path'
13+
import * as fs from 'fs'
14+
import * as crypto from 'crypto'
15+
16+
export const handle_preview_prompt = async (params: {
17+
prompt_view_provider: PromptViewProvider
18+
}): Promise<void> => {
19+
const {
20+
other_files,
21+
recent_files,
22+
processed_instructions,
23+
skill_definitions
24+
} = await build_prompt_payload({
25+
prompt_view_provider: params.prompt_view_provider,
26+
remove_images: true
27+
})
28+
29+
let formatted_system_instructions = ''
30+
const user_instructions = processed_instructions
31+
32+
if (params.prompt_view_provider.prompt_type == 'edit-files') {
33+
const edit_format = params.prompt_view_provider.edit_format
34+
const edit_format_instructions = {
35+
whole: EDIT_FORMAT_INSTRUCTIONS_WHOLE,
36+
truncated: EDIT_FORMAT_INSTRUCTIONS_TRUNCATED,
37+
'search-replace': EDIT_FORMAT_INSTRUCTIONS_SEARCH_REPLACE,
38+
diff: EDIT_FORMAT_INSTRUCTIONS_DIFF
39+
}[edit_format]
40+
if (edit_format_instructions) {
41+
formatted_system_instructions = `# Output formatting\n\n${edit_format_instructions}`
42+
}
43+
}
44+
45+
const { full_prompt: text } = PromptBuilder.build_prompt({
46+
other_files,
47+
recent_files,
48+
skill_definitions,
49+
system_instructions: formatted_system_instructions,
50+
user_instructions,
51+
separator: true
52+
})
53+
54+
const hash = crypto.createHash('md5').update(`${Date.now()}`).digest('hex')
55+
const temp_file_path = path.join(os.tmpdir(), `cwc-prompt-${hash}.md`)
56+
57+
try {
58+
await fs.promises.writeFile(temp_file_path, text, 'utf8')
59+
} catch (error) {
60+
vscode.window.showErrorMessage(
61+
'Failed to create temporary file for preview.'
62+
)
63+
return
64+
}
65+
66+
try {
67+
const document = await vscode.workspace.openTextDocument(
68+
vscode.Uri.file(temp_file_path)
69+
)
70+
await vscode.window.showTextDocument(document, { preview: false })
71+
} catch (error: any) {
72+
vscode.window.showErrorMessage(
73+
`Failed to open view: ${error.message || 'Unknown error'}`
74+
)
75+
}
76+
}

‎apps/editor/src/views/prompt/backend/message-handlers/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ export * from './handle-delete-api-configuration'
6666
export * from './handle-update-last-used-web-configuration'
6767
export * from './handle-request-return-home'
6868
export * from './handle-pick-tasks-workspace'
69+
export * from './handle-preview-prompt'

‎apps/editor/src/views/prompt/backend/prompt-view-provider.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ import {
7979
handle_delete_api_configuration,
8080
handle_update_last_used_web_configuration,
8181
handle_request_return_home,
82-
handle_pick_tasks_workspace
82+
handle_pick_tasks_workspace,
83+
handle_preview_prompt
8384
} from './message-handlers'
8485
import { handle_update_api_configuration } from './message-handlers/handle-update-api-configuration'
8586
import { handle_pick_model_provider } from './message-handlers/handle-pick-model-provider'
@@ -861,6 +862,10 @@ export class PromptViewProvider implements vscode.WebviewViewProvider {
861862
command: 'INCLUDE_SELECTED_FILES',
862863
include: this.include_selected_files
863864
})
865+
} else if (message.command == 'PREVIEW_PROMPT') {
866+
await handle_preview_prompt({
867+
prompt_view_provider: this
868+
})
864869
} else if (message.command == 'TOGGLE_INCLUDE_SELECTED_FILES') {
865870
this.include_selected_files = message.include
866871
this.extension_context.workspaceState.update(

‎apps/editor/src/views/prompt/frontend/Main/Main.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type Props = {
7474
bottom_spacer_height?: number
7575
include_selected_files: boolean
7676
on_toggle_include_selected_files: (include: boolean) => void
77+
on_preview_prompt: () => void
7778
}
7879

7980
export const Main: React.FC<Props> = (props) => {
@@ -548,6 +549,7 @@ export const Main: React.FC<Props> = (props) => {
548549
bottom_spacer_height={props.bottom_spacer_height}
549550
include_selected_files={props.include_selected_files}
550551
on_toggle_include_selected_files={props.on_toggle_include_selected_files}
552+
on_preview_prompt={props.on_preview_prompt}
551553
/>
552554
)
553555
}

‎apps/editor/src/views/prompt/frontend/Main/MainView/MainView.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ type Props = {
111111
bottom_spacer_height?: number
112112
include_selected_files: boolean
113113
on_toggle_include_selected_files: (include: boolean) => void
114+
on_preview_prompt: () => void
114115
}
115116

116117
const chatbot_to_icon: Record<keyof typeof CHATBOTS, Icon.Variant> = {
@@ -378,6 +379,7 @@ export const MainView: React.FC<Props> = (props) => {
378379
on_toggle_include_selected_files={
379380
props.on_toggle_include_selected_files
380381
}
382+
on_preview={props.on_preview_prompt}
381383
translations={{
382384
attach_selected_files: t('main.attach-selected-files'),
383385
preview: t('main.preview' as any)

‎apps/editor/src/views/prompt/frontend/Prompt.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ export const Prompt = () => {
7777
edit_instructions_token_count,
7878
ask_instructions_token_count,
7979
include_selected_files,
80-
handle_toggle_include_selected_files
80+
handle_toggle_include_selected_files,
81+
handle_preview_prompt
8182
} = use_panel(vscode)
8283

8384
const {
@@ -360,6 +361,7 @@ export const Prompt = () => {
360361
on_toggle_include_selected_files={
361362
handle_toggle_include_selected_files
362363
}
364+
on_preview_prompt={handle_preview_prompt}
363365
/>
364366
</div>
365367
<div

‎apps/editor/src/views/prompt/frontend/hooks/use-prompt/use-prompt.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ export const use_prompt = (vscode: any) => {
9494
})
9595
}
9696

97+
const handle_preview_prompt = () => {
98+
post_message(vscode, {
99+
command: 'PREVIEW_PROMPT'
100+
})
101+
}
102+
97103
useEffect(() => {
98104
const handle_message = (event: MessageEvent<BackendMessage>) => {
99105
const message = event.data
@@ -260,6 +266,7 @@ export const use_prompt = (vscode: any) => {
260266
edit_instructions_token_count,
261267
ask_instructions_token_count,
262268
include_selected_files,
263-
handle_toggle_include_selected_files
269+
handle_toggle_include_selected_files,
270+
handle_preview_prompt
264271
}
265272
}

‎apps/editor/src/views/prompt/types/messages.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,10 @@ export interface GetIncludeSelectedFilesMessage extends BaseMessage {
517517
command: 'GET_INCLUDE_SELECTED_FILES'
518518
}
519519

520+
export interface PreviewPromptMessage extends BaseMessage {
521+
command: 'PREVIEW_PROMPT'
522+
}
523+
520524
export type FrontendMessage =
521525
| GetInstructionsMessage
522526
| SaveInstructionsMessage
@@ -607,6 +611,7 @@ export type FrontendMessage =
607611
| GetTokenCountMessage
608612
| ToggleIncludeSelectedFilesMessage
609613
| GetIncludeSelectedFilesMessage
614+
| PreviewPromptMessage
610615

611616
// === FROM BACKEND TO FRONTEND ===
612617
export interface InstructionsMessage extends BaseMessage {

‎packages/ui/src/components/editor/prompt/SelectedFiles/SelectedFiles.stories.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const Default = () => {
1313
selected_files_token_count={12500}
1414
include_selected_files={include}
1515
on_toggle_include_selected_files={set_include}
16+
on_preview={() => {}}
1617
translations={{
1718
attach_selected_files: 'Attach selected files',
1819
preview: 'Preview'
@@ -29,6 +30,7 @@ export const ZeroTokens = () => {
2930
selected_files_token_count={0}
3031
include_selected_files={include}
3132
on_toggle_include_selected_files={set_include}
33+
on_preview={() => {}}
3234
translations={{
3335
attach_selected_files: 'Attach selected files',
3436
preview: 'Preview'

0 commit comments

Comments
 (0)