opencode-translate 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/activation.ts +28 -15
package/package.json
CHANGED
package/src/activation.ts
CHANGED
|
@@ -134,12 +134,17 @@ function mergeTranslatedMetadata(state: TranslateState, part: TextPartLike, engl
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
// OpenCode's flag semantics
|
|
137
|
+
// OpenCode's flag semantics are not uniform across every UI path:
|
|
138
138
|
// synthetic: true -> hidden from the user UI, still sent to the LLM
|
|
139
|
-
// ignored: true -> hidden from the LLM, still shown in the
|
|
139
|
+
// ignored: true -> hidden from the LLM, still shown in the main message UI
|
|
140
140
|
// both true -> hidden from both (used for metadata-only marker
|
|
141
141
|
// parts like the activation banner that exist purely
|
|
142
142
|
// to carry state across reloads)
|
|
143
|
+
// Some secondary UIs, including the web `/fork` dialog, only consider text
|
|
144
|
+
// parts that are neither synthetic nor ignored. Persist translated user display
|
|
145
|
+
// parts as non-ignored so those flows can find them; before model serialization,
|
|
146
|
+
// `experimental.chat.messages.transform` marks those display parts ignored and
|
|
147
|
+
// lets the synthetic English twins carry the actual prompt text.
|
|
143
148
|
// The plugin's user-facing status text (translation preview, activation
|
|
144
149
|
// banner, failure notice) lives inline on the source-language user part
|
|
145
150
|
// itself rather than as sibling parts, because OpenCode's
|
|
@@ -148,8 +153,9 @@ function mergeTranslatedMetadata(state: TranslateState, part: TextPartLike, engl
|
|
|
148
153
|
|
|
149
154
|
// LLM-only text part: hidden from the TUI but the only LLM-visible
|
|
150
155
|
// representation of the user's source-language text. The original
|
|
151
|
-
// user-authored part is marked `ignored:true`
|
|
152
|
-
// and this synthetic English twin carries the actual
|
|
156
|
+
// user-authored part is marked `ignored:true` in the model-bound transform so
|
|
157
|
+
// the LLM never sees it, and this synthetic English twin carries the actual
|
|
158
|
+
// prompt content.
|
|
153
159
|
function createLlmOnlyTextPart(
|
|
154
160
|
sessionID: string,
|
|
155
161
|
messageID: string,
|
|
@@ -168,6 +174,11 @@ function createLlmOnlyTextPart(
|
|
|
168
174
|
}
|
|
169
175
|
}
|
|
170
176
|
|
|
177
|
+
function isTranslatedUserDisplayPart(part: TextPartLike): boolean {
|
|
178
|
+
if (!isTextPart(part) || part.synthetic === true) return false
|
|
179
|
+
return extractStateFromMetadata(asMetadata(part)) !== undefined
|
|
180
|
+
}
|
|
181
|
+
|
|
171
182
|
function escapeRegex(value: string): string {
|
|
172
183
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
173
184
|
}
|
|
@@ -367,15 +378,14 @@ export function createHooks(ctx: PluginInput, rawOptions: PluginOptions = {}, de
|
|
|
367
378
|
// ONE text part per user message — the first non-synthetic —
|
|
368
379
|
// so a sibling `synthetic:false + ignored:true` preview part
|
|
369
380
|
// would never reach the screen. Combining them here keeps
|
|
370
|
-
// the original visible alongside the English twin while
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
381
|
+
// the original visible alongside the English twin while still
|
|
382
|
+
// satisfying secondary UI filters like `/fork`, which require
|
|
383
|
+
// non-synthetic, non-ignored text. The transform hook marks this
|
|
384
|
+
// display part ignored in the model-bound copy.
|
|
374
385
|
part.text = `${part.text}\n\n_→ EN: ${english}_`
|
|
375
|
-
part.ignored = true
|
|
376
386
|
|
|
377
387
|
// LLM-only English twin. This is the actual prompt the model
|
|
378
|
-
// sees in place of the
|
|
388
|
+
// sees in place of the source-language display part.
|
|
379
389
|
nextParts.push(
|
|
380
390
|
createLlmOnlyTextPart(part.sessionID, part.messageID, english, {
|
|
381
391
|
translate_role: "llm_only_translation",
|
|
@@ -465,12 +475,15 @@ export function createHooks(ctx: PluginInput, rawOptions: PluginOptions = {}, de
|
|
|
465
475
|
const activeState = resolved.state
|
|
466
476
|
if (!activeState) return
|
|
467
477
|
|
|
468
|
-
// User parts need no in-place rewriting: the source-language text
|
|
469
|
-
// part is `ignored:true` so the LLM serializer skips it, and a
|
|
470
|
-
// synthetic English twin (created in `chat.message`) carries the
|
|
471
|
-
// actual prompt content. Only assistant parts still need their
|
|
472
|
-
// localized trailer stripped before re-entering the model.
|
|
473
478
|
for (const message of output.messages as MessageWithPartsLike[]) {
|
|
479
|
+
if (message.info.role === "user") {
|
|
480
|
+
for (const part of message.parts) {
|
|
481
|
+
if (!isTranslatedUserDisplayPart(part)) continue
|
|
482
|
+
part.ignored = true
|
|
483
|
+
}
|
|
484
|
+
continue
|
|
485
|
+
}
|
|
486
|
+
|
|
474
487
|
if (message.info.role !== "assistant") continue
|
|
475
488
|
for (const part of message.parts) {
|
|
476
489
|
if (!isTextPart(part)) continue
|