Skip to content

Commit 3be0d0c

Browse files
Merge pull request #3494 from opral/sherlock-v2-improvements
Sherlock v2 improvements
2 parents f08f1f8 + 0dbca1e commit 3be0d0c

File tree

16 files changed

+888
-233
lines changed

16 files changed

+888
-233
lines changed

.changeset/fair-fishes-pretend.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"sherlock-editor-app": patch
3+
"@inlang/editor-component": patch
4+
"vs-code-extension": patch
5+
---
6+
7+
Sherlock v2 bugfixes & improvements

.changeset/silly-moles-judge.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"vs-code-extension": patch
3+
---
4+
5+
bugfixes & small improvements

inlang/packages/sherlock/assets/styles.css

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ body {
1717
background-color: var(--vscode-sideBar-background);
1818
font-size: 13px;
1919
cursor: pointer;
20-
padding: 5px 10px;
20+
padding: 5px 8px;
2121
border: none;
2222
text-align: left;
2323
outline: none;
@@ -39,6 +39,14 @@ body {
3939
width: 100%;
4040
}
4141

42+
.collapsible .chevron {
43+
transition: transform 0.2s ease;
44+
}
45+
46+
.collapsible.active .chevron {
47+
transform: rotate(90deg);
48+
}
49+
4250
.collapsible .messageId span {
4351
text-overflow: ellipsis;
4452
white-space: nowrap;
@@ -64,7 +72,7 @@ body {
6472
gap: 5px;
6573
width: 100%;
6674
height: 18px;
67-
padding: 5px 10px 4px 10px;
75+
padding: 5px 8px 4px 8px;
6876
border-bottom: 1px solid var(--vscode-sideBar-border);
6977
font-size: 13px;
7078
cursor: pointer;

inlang/packages/sherlock/package.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,13 @@
106106
},
107107
{
108108
"command": "sherlock.extractMessage",
109-
"title": "Sherlock: Extract Message"
109+
"title": "Sherlock: Extract Message",
110+
"icon": "$(arrow-down)"
110111
},
111112
{
112113
"command": "sherlock.createMessage",
113-
"title": "Sherlock: Create Message"
114+
"title": "Sherlock: Create Message",
115+
"icon": "$(add)"
114116
}
115117
],
116118
"menus": {
@@ -130,6 +132,18 @@
130132
"command": "sherlock.copyError",
131133
"group": "inline"
132134
}
135+
],
136+
"view/title": [
137+
{
138+
"command": "sherlock.createMessage",
139+
"when": "view == messageView",
140+
"group": "navigation"
141+
},
142+
{
143+
"command": "sherlock.extractMessage",
144+
"when": "view == messageView",
145+
"group": "navigation"
146+
}
133147
]
134148
},
135149
"configuration": {

inlang/packages/sherlock/src/commands/extractMessage.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ describe("extractMessageCommand", () => {
242242
// @ts-expect-error
243243
await extractMessageCommand.callback(mockTextEditor)
244244
expect(msg).toHaveBeenCalledWith(
245-
"Please select a text to extract in your text editor.",
245+
"Please select a text in your text editor to extract a message.",
246246
"warn",
247247
"notification"
248248
)

inlang/packages/sherlock/src/commands/extractMessage.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ export const extractMessageCommand = {
5656
}
5757

5858
if (textEditor.selection.isEmpty) {
59-
return msg("Please select a text to extract in your text editor.", "warn", "notification")
59+
return msg(
60+
"Please select a text in your text editor to extract a message.",
61+
"warn",
62+
"notification"
63+
)
6064
}
6165

6266
const autoHumanId = await getSetting("extract.autoHumanId.enabled").catch(() => true)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as vscode from "vscode"
2+
import { handleError } from "../utilities/utils.js"
3+
import { state } from "../utilities/state.js"
4+
import { main } from "../main.js"
5+
import { createFileSystemMapper } from "../utilities/fs/createFileSystemMapper.js"
6+
import fs from "node:fs/promises"
7+
8+
export const reloadProjectCommand = {
9+
command: "sherlock.reloadProject",
10+
title: "Sherlock: Reload project",
11+
register: vscode.commands.registerCommand,
12+
callback: async () => {
13+
try {
14+
console.log("Reloading project...")
15+
16+
// Get current workspace
17+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
18+
if (!workspaceFolder) {
19+
console.warn("No workspace folder found.")
20+
return
21+
}
22+
23+
// Create file system mapper
24+
const mappedFs = createFileSystemMapper(workspaceFolder.uri.fsPath, fs)
25+
26+
// Get context from extension
27+
const extension = vscode.extensions.getExtension("inlang.vs-code-extension")
28+
if (!extension) {
29+
throw new Error("Could not find Sherlock extension")
30+
}
31+
32+
// Get API from extension (returns context)
33+
const api = await extension.activate()
34+
if (!api?.context) {
35+
throw new Error("Could not get extension context")
36+
}
37+
38+
// Update project if we have a selected project
39+
if (state().selectedProjectPath) {
40+
await main({ context: api.context, workspaceFolder, fs: mappedFs })
41+
console.log("Project reloaded successfully")
42+
} else {
43+
console.warn("No project selected, nothing to reload")
44+
}
45+
} catch (error) {
46+
console.error("Failed to reload project:", error)
47+
handleError(error)
48+
}
49+
}
50+
}

inlang/packages/sherlock/src/configuration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { machineTranslateMessageCommand } from "./commands/machineTranslate.js"
1414
import { openSettingsViewCommand } from "./commands/openSettingsView.js"
1515
import { toggleInlineAnnotationsCommand } from "./commands/toggleInlineAnnotation.js"
1616
import { openEditorViewCommand } from "./commands/openEditorView.js"
17+
import { reloadProjectCommand } from "./commands/reloadProject.js"
1718

1819
export const CONFIGURATION = {
1920
EVENTS: {
@@ -41,6 +42,7 @@ export const CONFIGURATION = {
4142
COPY_ERROR: copyErrorCommand,
4243
MACHINE_TRANSLATE_MESSAGE: machineTranslateMessageCommand,
4344
TOGGLE_INLINE_ANNOTATIONS: toggleInlineAnnotationsCommand,
45+
RELOAD_PROJECT: reloadProjectCommand,
4446
},
4547
FILES: {
4648
// TODO: remove this hardcoded assumption for multi project support

inlang/packages/sherlock/src/main.ts

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ import { gettingStartedView } from "./utilities/getting-started/gettingStarted.j
1313
import { closestInlangProject } from "./utilities/project/closestInlangProject.js"
1414
import { recommendationBannerView } from "./utilities/recommendation/recommendation.js"
1515
import { capture } from "./services/telemetry/index.js"
16-
import packageJson from "../package.json" assert { type: "json" }
16+
import packageJson from "../package.json" with { type: "json" }
1717
import { statusBar } from "./utilities/settings/statusBar.js"
1818
import fg from "fast-glob"
1919
import { saveProjectToDirectory, type IdeExtensionConfig } from "@inlang/sdk"
2020
import path from "node:path"
2121
import { linterDiagnostics } from "./diagnostics/linterDiagnostics.js"
22+
import { setupDirectMessageWatcher } from "./utilities/fs/experimental/directMessageHandler.js"
2223
//import { initErrorMonitoring } from "./services/error-monitoring/implementation.js"
2324

2425
// Entry Point
@@ -60,7 +61,7 @@ export async function activate(
6061
}
6162

6263
// Main Function
63-
async function main(args: {
64+
export async function main(args: {
6465
context: vscode.ExtensionContext
6566
workspaceFolder: vscode.WorkspaceFolder
6667
fs: FileSystem
@@ -95,37 +96,21 @@ async function main(args: {
9596
await registerExtensionComponents(args)
9697
await handleInlangErrors()
9798

98-
// TODO: Replace by reactive settings API?
99-
setupFileSystemWatcher(args)
99+
// Set up both file system watchers
100+
// setupFileSystemWatcher(args)
101+
102+
// Set up direct message watcher as a fallback
103+
setupDirectMessageWatcher({
104+
context: args.context,
105+
workspaceFolder: args.workspaceFolder,
106+
})
100107

101108
return
102109
} else {
103110
await gettingStartedView(args)
104111
}
105112
}
106113

107-
function setupFileSystemWatcher(args: {
108-
context: vscode.ExtensionContext
109-
workspaceFolder: vscode.WorkspaceFolder
110-
fs: FileSystem
111-
}) {
112-
const watcher = vscode.workspace.createFileSystemWatcher(
113-
new vscode.RelativePattern(
114-
args.workspaceFolder,
115-
state().selectedProjectPath || CONFIGURATION.FILES.PROJECT
116-
)
117-
)
118-
119-
watcher.onDidChange(async () => {
120-
// reload project
121-
await main({
122-
context: args.context,
123-
workspaceFolder: args.workspaceFolder,
124-
fs: args.fs,
125-
})
126-
})
127-
}
128-
129114
async function registerExtensionComponents(args: {
130115
context: vscode.ExtensionContext
131116
workspaceFolder: vscode.WorkspaceFolder

inlang/packages/sherlock/src/utilities/editor/editorView.ts

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ import { deleteBundleNested } from "./helper/deleteBundleNested.js"
1313
import { handleUpdateBundle } from "./helper/handleBundleUpdate.js"
1414
import { createMessage } from "./helper/createMessage.js"
1515
import { saveProject } from "../../main.js"
16-
import { createFileSystemMapper } from "../fs/createFileSystemMapper.js"
17-
import path from "path"
18-
import fs from "node:fs/promises"
16+
import { rpc } from "@inlang/rpc"
1917

2018
// Same interface as before
2119
export interface UpdateBundleMessage {
@@ -148,6 +146,90 @@ export function editorView(args: { context: vscode.ExtensionContext; initialBund
148146

149147
updateView()
150148
return
149+
case "translate-bundle":
150+
// Handle the translated bundle
151+
try {
152+
// Update the bundle in the database
153+
await handleUpdateBundle({
154+
db: state().project?.db,
155+
message: {
156+
command: "translate-bundle",
157+
change: message.translatedBundle,
158+
},
159+
})
160+
updateView()
161+
} catch (error) {
162+
console.error("Failed to update translated bundle", error)
163+
vscode.window.showErrorMessage(`Failed to update translations: ${String(error)}`)
164+
}
165+
return
166+
case "machine-translate-bundle":
167+
// Handle machine translation directly in extension host
168+
try {
169+
const sourceBundle = message.bundle
170+
const sourceLocale = message.sourceLocale
171+
const targetLocale = message.targetLocale
172+
173+
// Show info message
174+
msg("Translating...", "info", "statusBar", vscode.StatusBarAlignment.Right, 3000)
175+
176+
// Use the RPC translation service
177+
const result = await rpc.machineTranslateBundle({
178+
bundle: sourceBundle,
179+
sourceLocale,
180+
targetLocales: [targetLocale],
181+
})
182+
183+
if (result.error) {
184+
throw new Error(result.error)
185+
}
186+
187+
// Check if result.data is undefined
188+
if (!result.data) {
189+
throw new Error("Translation result is empty")
190+
}
191+
192+
// We need to ensure the result.data has the expected structure
193+
// Create a properly formatted bundle with required fields to match expected type
194+
const typedBundle = {
195+
...result.data,
196+
id: result.data.id || sourceBundle.id, // Ensure id is never undefined
197+
declarations: result.data.declarations || [], // Ensure declarations is always an array
198+
// Add any other required fields that might be missing
199+
}
200+
201+
// Update the bundle in the database with the properly typed data
202+
await handleUpdateBundle({
203+
db: state().project?.db,
204+
message: {
205+
command: "translate-bundle",
206+
change: {
207+
entityId: sourceBundle.id,
208+
entity: "bundle",
209+
newData: typedBundle,
210+
},
211+
},
212+
})
213+
214+
updateView()
215+
msg("Translation complete", "info", "statusBar", vscode.StatusBarAlignment.Right, 3000)
216+
} catch (error) {
217+
console.error("Failed to translate bundle", error)
218+
msg(
219+
`Translation error: ${String(error)}`,
220+
"error",
221+
"statusBar",
222+
vscode.StatusBarAlignment.Right,
223+
3000
224+
)
225+
}
226+
return
227+
case "show-info-message":
228+
msg(message.message, "info", "statusBar", vscode.StatusBarAlignment.Right, 3000)
229+
return
230+
case "show-error-message":
231+
msg(message.message, "error", "statusBar", vscode.StatusBarAlignment.Right, 3000)
232+
return
151233

152234
default:
153235
console.error("Unknown command from webview:", command)
@@ -245,8 +327,8 @@ export function editorView(args: { context: vscode.ExtensionContext; initialBund
245327
// Allow media from safe sources (if needed)
246328
`media-src ${webview.cspSource} https://* data:;`,
247329

248-
// Allow connections to APIs, WebSockets, and data URIs
249-
`connect-src https://* data: ${
330+
// Allow connections to APIs, WebSockets, and data URIs - include http://localhost:3000 for RPC
331+
`connect-src https://* http://localhost:3000 data: ${
250332
isProd
251333
? ``
252334
: `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`

0 commit comments

Comments
 (0)