ktzeeeeee commited on
Commit
d419a3c
·
1 Parent(s): 233d22e

[UX] click shortcut in chat to go to source file in workbench

Browse files
app/components/chat/Artifact.tsx CHANGED
@@ -7,6 +7,7 @@ import type { ActionState } from '~/lib/runtime/action-runner';
7
  import { workbenchStore } from '~/lib/stores/workbench';
8
  import { classNames } from '~/utils/classNames';
9
  import { cubicEasingFn } from '~/utils/easings';
 
10
 
11
  const highlighterOptions = {
12
  langs: ['shell'],
@@ -129,6 +130,14 @@ const actionVariants = {
129
  visible: { opacity: 1, y: 0 },
130
  };
131
 
 
 
 
 
 
 
 
 
132
  const ActionList = memo(({ actions }: ActionListProps) => {
133
  return (
134
  <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
@@ -169,7 +178,10 @@ const ActionList = memo(({ actions }: ActionListProps) => {
169
  {type === 'file' ? (
170
  <div>
171
  Create{' '}
172
- <code className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md">
 
 
 
173
  {action.filePath}
174
  </code>
175
  </div>
 
7
  import { workbenchStore } from '~/lib/stores/workbench';
8
  import { classNames } from '~/utils/classNames';
9
  import { cubicEasingFn } from '~/utils/easings';
10
+ import { WORK_DIR } from '~/utils/constants';
11
 
12
  const highlighterOptions = {
13
  langs: ['shell'],
 
130
  visible: { opacity: 1, y: 0 },
131
  };
132
 
133
+ function openArtifactInWorkbench(filePath: any) {
134
+ if (workbenchStore.currentView.get() !== 'code') {
135
+ workbenchStore.currentView.set('code');
136
+ }
137
+
138
+ workbenchStore.setSelectedFile(`${WORK_DIR}/${filePath}`);
139
+ }
140
+
141
  const ActionList = memo(({ actions }: ActionListProps) => {
142
  return (
143
  <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
 
178
  {type === 'file' ? (
179
  <div>
180
  Create{' '}
181
+ <code
182
+ className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md text-bolt-elements-item-contentAccent hover:underline cursor-pointer"
183
+ onClick={() => openArtifactInWorkbench(action.filePath)}
184
+ >
185
  {action.filePath}
186
  </code>
187
  </div>
app/components/workbench/Workbench.client.tsx CHANGED
@@ -180,8 +180,8 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
180
  alert("GitHub token is required. Push to GitHub cancelled.");
181
  return;
182
  }
183
-
184
- workbenchStore.pushToGitHub(repoName, githubUsername, githubToken);
185
  }}
186
  >
187
  <div className="i-ph:github-logo" />
 
180
  alert("GitHub token is required. Push to GitHub cancelled.");
181
  return;
182
  }
183
+
184
+ workbenchStore.pushToGitHub(repoName, githubUsername, githubToken);
185
  }}
186
  >
187
  <div className="i-ph:github-logo" />
app/lib/.server/llm/prompts.ts CHANGED
@@ -39,20 +39,20 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
39
  - rm: Remove files
40
  - rmdir: Remove empty directories
41
  - touch: Create empty file/update timestamp
42
-
43
  System Information:
44
  - hostname: Show system name
45
  - ps: Display running processes
46
  - pwd: Print working directory
47
  - uptime: Show system uptime
48
  - env: Environment variables
49
-
50
  Development Tools:
51
  - node: Execute Node.js code
52
  - python3: Run Python scripts
53
  - code: VSCode operations
54
  - jq: Process JSON
55
-
56
  Other Utilities:
57
  - curl, head, sort, tail, clear, which, export, chmod, scho, hostname, kill, ln, xxd, alias, false, getconf, true, loadenv, wasm, xdg-open, command, exit, source
58
  </system_constraints>
@@ -88,7 +88,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
88
  Example:
89
 
90
  <${MODIFICATIONS_TAG_NAME}>
91
- <diff path="/home/project/src/main.js">
92
  @@ -2,7 +2,10 @@
93
  return a + b;
94
  }
@@ -103,7 +103,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
103
  +
104
  +console.log('The End');
105
  </diff>
106
- <file path="/home/project/package.json">
107
  // full file content here
108
  </file>
109
  </${MODIFICATIONS_TAG_NAME}>
@@ -124,7 +124,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
124
  2. Create TodoList and TodoItem components
125
  3. Implement localStorage for persistence
126
  4. Add CRUD operations
127
-
128
  Let's start now.
129
 
130
  [Rest of response...]"
@@ -134,7 +134,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
134
  1. Check network requests
135
  2. Verify API endpoint format
136
  3. Examine error handling
137
-
138
  [Rest of response...]"
139
 
140
  </chain_of_thought_instructions>
 
39
  - rm: Remove files
40
  - rmdir: Remove empty directories
41
  - touch: Create empty file/update timestamp
42
+
43
  System Information:
44
  - hostname: Show system name
45
  - ps: Display running processes
46
  - pwd: Print working directory
47
  - uptime: Show system uptime
48
  - env: Environment variables
49
+
50
  Development Tools:
51
  - node: Execute Node.js code
52
  - python3: Run Python scripts
53
  - code: VSCode operations
54
  - jq: Process JSON
55
+
56
  Other Utilities:
57
  - curl, head, sort, tail, clear, which, export, chmod, scho, hostname, kill, ln, xxd, alias, false, getconf, true, loadenv, wasm, xdg-open, command, exit, source
58
  </system_constraints>
 
88
  Example:
89
 
90
  <${MODIFICATIONS_TAG_NAME}>
91
+ <diff path="${WORK_DIR}/src/main.js">
92
  @@ -2,7 +2,10 @@
93
  return a + b;
94
  }
 
103
  +
104
  +console.log('The End');
105
  </diff>
106
+ <file path="${WORK_DIR}/package.json">
107
  // full file content here
108
  </file>
109
  </${MODIFICATIONS_TAG_NAME}>
 
124
  2. Create TodoList and TodoItem components
125
  3. Implement localStorage for persistence
126
  4. Add CRUD operations
127
+
128
  Let's start now.
129
 
130
  [Rest of response...]"
 
134
  1. Check network requests
135
  2. Verify API endpoint format
136
  3. Examine error handling
137
+
138
  [Rest of response...]"
139
 
140
  </chain_of_thought_instructions>
app/lib/stores/workbench.ts CHANGED
@@ -14,6 +14,7 @@ import { saveAs } from 'file-saver';
14
  import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
15
  import * as nodePath from 'node:path';
16
  import type { WebContainerProcess } from '@webcontainer/api';
 
17
 
18
  export interface ArtifactState {
19
  id: string;
@@ -312,8 +313,7 @@ export class WorkbenchStore {
312
 
313
  for (const [filePath, dirent] of Object.entries(files)) {
314
  if (dirent?.type === 'file' && !dirent.isBinary) {
315
- // remove '/home/project/' from the beginning of the path
316
- const relativePath = filePath.replace(/^\/home\/project\//, '');
317
 
318
  // split the path into segments
319
  const pathSegments = relativePath.split('/');
@@ -343,7 +343,7 @@ export class WorkbenchStore {
343
 
344
  for (const [filePath, dirent] of Object.entries(files)) {
345
  if (dirent?.type === 'file' && !dirent.isBinary) {
346
- const relativePath = filePath.replace(/^\/home\/project\//, '');
347
  const pathSegments = relativePath.split('/');
348
  let currentHandle = targetHandle;
349
 
@@ -417,7 +417,7 @@ export class WorkbenchStore {
417
  content: Buffer.from(dirent.content).toString('base64'),
418
  encoding: 'base64',
419
  });
420
- return { path: filePath.replace(/^\/home\/project\//, ''), sha: blob.sha };
421
  }
422
  })
423
  );
 
14
  import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
15
  import * as nodePath from 'node:path';
16
  import type { WebContainerProcess } from '@webcontainer/api';
17
+ import { extractRelativePath } from '~/utils/diff';
18
 
19
  export interface ArtifactState {
20
  id: string;
 
313
 
314
  for (const [filePath, dirent] of Object.entries(files)) {
315
  if (dirent?.type === 'file' && !dirent.isBinary) {
316
+ const relativePath = extractRelativePath(filePath);
 
317
 
318
  // split the path into segments
319
  const pathSegments = relativePath.split('/');
 
343
 
344
  for (const [filePath, dirent] of Object.entries(files)) {
345
  if (dirent?.type === 'file' && !dirent.isBinary) {
346
+ const relativePath = extractRelativePath(filePath);
347
  const pathSegments = relativePath.split('/');
348
  let currentHandle = targetHandle;
349
 
 
417
  content: Buffer.from(dirent.content).toString('base64'),
418
  encoding: 'base64',
419
  });
420
+ return { path: extractRelativePath(filePath), sha: blob.sha };
421
  }
422
  })
423
  );
app/utils/diff.spec.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from 'vitest';
2
+ import { extractRelativePath } from './diff';
3
+ import { WORK_DIR } from './constants';
4
+
5
+ describe('Diff', () => {
6
+ it('should strip out Work_dir', () => {
7
+ const filePath = `${WORK_DIR}/index.js`;
8
+ const result = extractRelativePath(filePath);
9
+ expect(result).toBe('index.js');
10
+ });
11
+ });
app/utils/diff.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { createTwoFilesPatch } from 'diff';
2
  import type { FileMap } from '~/lib/stores/files';
3
- import { MODIFICATIONS_TAG_NAME } from './constants';
4
 
5
  export const modificationsRegex = new RegExp(
6
  `^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
@@ -75,6 +75,15 @@ export function diffFiles(fileName: string, oldFileContent: string, newFileConte
75
  return unifiedDiff;
76
  }
77
 
 
 
 
 
 
 
 
 
 
78
  /**
79
  * Converts the unified diff to HTML.
80
  *
 
1
  import { createTwoFilesPatch } from 'diff';
2
  import type { FileMap } from '~/lib/stores/files';
3
+ import { MODIFICATIONS_TAG_NAME, WORK_DIR } from './constants';
4
 
5
  export const modificationsRegex = new RegExp(
6
  `^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
 
75
  return unifiedDiff;
76
  }
77
 
78
+ const regex = new RegExp(`^${WORK_DIR}\/`);
79
+
80
+ /**
81
+ * Strips out the work directory from the file path.
82
+ */
83
+ export function extractRelativePath(filePath: string) {
84
+ return filePath.replace(regex, '');
85
+ }
86
+
87
  /**
88
  * Converts the unified diff to HTML.
89
  *