@1outres/dsh-plugin-edit-message 0.0.0-stage → 0.6.0
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/LICENSE +21 -0
- package/README.md +87 -2
- package/cordis.patch.yml +6 -0
- package/icon.svg +8 -0
- package/lib/client.js +623 -0
- package/lib/index.js +6 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +58 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 1outres
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @1outres/dsh-plugin-edit-message
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Edit a message you already sent in the DeepSeek Harness Web UI, the way chat
|
|
4
|
+
apps do: click **Edit** beside Copy, change the text, and send. The
|
|
5
|
+
conversation restarts from just before that message with the edited text.
|
|
6
|
+
|
|
7
|
+
Install just this plugin:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
dsh plugin add @1outres/dsh-plugin-edit-message
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install it with every plugin in the repository through the
|
|
14
|
+
[`@1outres/dsh-plugins`](../dsh-plugins) bundle. Enable one or the other: both
|
|
15
|
+
insert the same `edit-message` row.
|
|
16
|
+
|
|
17
|
+
## Editing a message
|
|
18
|
+
|
|
19
|
+
Hover a message you sent and click the pencil next to Copy. The bubble turns
|
|
20
|
+
into an editor with the original text.
|
|
21
|
+
|
|
22
|
+
| Key | Action |
|
|
23
|
+
| ----------------------- | ----------------------- |
|
|
24
|
+
| `Ctrl+Enter` / `⌘Enter` | Send the edited message |
|
|
25
|
+
| `Esc` | Cancel; nothing changes |
|
|
26
|
+
| `Enter` | New line |
|
|
27
|
+
|
|
28
|
+
`Enter` does not send, whatever the composer setting says: sending replaces
|
|
29
|
+
the conversation, so it needs the deliberate shortcut or the Send button.
|
|
30
|
+
Sending unchanged text is allowed and regenerates the answer from that point.
|
|
31
|
+
|
|
32
|
+
The button is shown on the prompt that opens each turn. It is not shown on
|
|
33
|
+
steering messages sent while a turn was running, nor in subagent sessions. It
|
|
34
|
+
is dimmed, with the reason in its tooltip, while the session is running, when
|
|
35
|
+
the message has attached files, or when this browser may not fork the session.
|
|
36
|
+
|
|
37
|
+
## What sending does
|
|
38
|
+
|
|
39
|
+
DSH session logs are append-only, so an edit cannot shorten the current
|
|
40
|
+
session. Instead, Send:
|
|
41
|
+
|
|
42
|
+
1. Forks the session just before the edited message: through the end of the
|
|
43
|
+
previous turn and the events recorded after it (changed-file summaries,
|
|
44
|
+
titles, model choices, commands). Editing the first message keeps only the
|
|
45
|
+
session's setup.
|
|
46
|
+
2. Removes any queued prompt the fork inherited. A prompt queued while the
|
|
47
|
+
previous turn was running is logged before that turn ends, and would
|
|
48
|
+
otherwise run ahead of the edited one.
|
|
49
|
+
3. Keeps the original title on the fork when a later message is edited. When
|
|
50
|
+
the first message is edited, the title is generated again for the new
|
|
51
|
+
topic.
|
|
52
|
+
4. Sends the edited text, with the original message's images, in the fork.
|
|
53
|
+
5. Only after the fork accepts the prompt: places the fork where the original
|
|
54
|
+
sits in its workspace, opens it, and archives the original.
|
|
55
|
+
|
|
56
|
+
If any step fails, the original session is not touched, an unused fork is
|
|
57
|
+
archived, and the editor stays open with the error and your text.
|
|
58
|
+
|
|
59
|
+
The archived original stays available under the sidebar's archived filter and
|
|
60
|
+
can be unarchived. The new session has a new id and URL.
|
|
61
|
+
|
|
62
|
+
## Limits
|
|
63
|
+
|
|
64
|
+
- **Files are not reverted.** Only the conversation goes back; files the agent
|
|
65
|
+
changed after that point stay changed.
|
|
66
|
+
- Messages with attached files cannot be edited: the browser cannot read a
|
|
67
|
+
file attachment back to send it again. Images are sent again; v1 edits the
|
|
68
|
+
text only and cannot add or remove images.
|
|
69
|
+
- Each edit leaves the previous version in the archive. DSH offers no way to
|
|
70
|
+
delete a session.
|
|
71
|
+
- Renaming the fork pins its title, as a manual rename does, so it is no
|
|
72
|
+
longer generated automatically.
|
|
73
|
+
- The Edit button is added to the shipped message bubble, which offers no
|
|
74
|
+
slot for message actions. The plugin wraps the shipped `user` renderer of
|
|
75
|
+
`conversation.chat.node` and appends its button to that bubble's action
|
|
76
|
+
row. If a Harness update changes that row, the button is not shown; if the
|
|
77
|
+
wrapper throws, the slot runtime retires it and the shipped bubble returns.
|
|
78
|
+
- English only.
|
|
79
|
+
|
|
80
|
+
## How it works
|
|
81
|
+
|
|
82
|
+
The browser half registers a `user` renderer in `conversation.chat.node` at
|
|
83
|
+
priority `-100`, above the shipped one at `0`, and renders the shipped
|
|
84
|
+
renderer inside it unchanged. Its Edit button and editor use copied host
|
|
85
|
+
styles that share only theme tokens. The edit runs through the Session and
|
|
86
|
+
Workspace services the Web UI already uses (`ctx.sessions`,
|
|
87
|
+
`ctx.uiWorkspace`, `ctx.workspaces`). The host half has no behavior and no
|
|
88
|
+
settings.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Installs this plugin on its own (dsh plugin add @1outres/dsh-plugin-edit-message).
|
|
2
|
+
# The @1outres/dsh-plugins bundle inserts the same "edit-message" row, so select
|
|
3
|
+
# one bundle or the other, never both.
|
|
4
|
+
- insert:
|
|
5
|
+
- id: edit-message
|
|
6
|
+
name: "@1outres/dsh-plugin-edit-message"
|
package/icon.svg
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none" role="img" aria-labelledby="title">
|
|
2
|
+
<title id="title">Edit Message</title>
|
|
3
|
+
<rect x="2" y="2" width="60" height="60" rx="14" fill="#4D6BFE"/>
|
|
4
|
+
<path d="M15 18.5C15 16.567 16.567 15 18.5 15H40.5C42.433 15 44 16.567 44 18.5V32.5C44 34.433 42.433 36 40.5 36H27L20 42V36H18.5C16.567 36 15 34.433 15 32.5V18.5Z" fill="#FFFFFF" fill-opacity="0.92"/>
|
|
5
|
+
<path d="M21 22H37M21 28H31" stroke="#4D6BFE" stroke-width="2.6" stroke-linecap="round"/>
|
|
6
|
+
<path d="M46.6 30.4L50.6 34.4L36.8 48.2L31.6 49.4L32.8 44.2L46.6 30.4Z" fill="#FFFFFF" stroke="#2E47C9" stroke-width="2" stroke-linejoin="round"/>
|
|
7
|
+
<path d="M43.8 33.2L47.8 37.2" stroke="#2E47C9" stroke-width="2"/>
|
|
8
|
+
</svg>
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
/* Generated by scripts/build.mjs — do not edit. */
|
|
2
|
+
window.__ModuleLoader__.load({
|
|
3
|
+
id: "@1outres/dsh-plugin-edit-message",
|
|
4
|
+
factory: (require) => {
|
|
5
|
+
var module = { exports: {} };
|
|
6
|
+
var exports = module.exports;
|
|
7
|
+
"use strict";
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
26
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
27
|
+
|
|
28
|
+
// ../dsh-plugin-edit-message/src/client/index.tsx
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
NAMESPACE: () => NAMESPACE,
|
|
32
|
+
NODE_KEY: () => NODE_KEY,
|
|
33
|
+
NODE_PRIORITY: () => NODE_PRIORITY,
|
|
34
|
+
NODE_SLOT: () => NODE_SLOT,
|
|
35
|
+
apply: () => apply,
|
|
36
|
+
inject: () => inject
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// ../dsh-plugin-edit-message/src/client/EditableUserMessage.tsx
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
var import_react_dom = require("react-dom");
|
|
43
|
+
|
|
44
|
+
// ../dsh-plugin-edit-message/src/client/content.ts
|
|
45
|
+
function isImageRef(value) {
|
|
46
|
+
if (typeof value !== "object" || value === null) return false;
|
|
47
|
+
const ref = value;
|
|
48
|
+
return typeof ref.attachmentId === "string" && typeof ref.mediaType === "string";
|
|
49
|
+
}
|
|
50
|
+
function messageParts(content) {
|
|
51
|
+
const texts = [];
|
|
52
|
+
const images = [];
|
|
53
|
+
let hasFiles = false;
|
|
54
|
+
for (const block of content) {
|
|
55
|
+
if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
56
|
+
else if (block.type === "image" && isImageRef(block.attachment)) images.push(block.attachment);
|
|
57
|
+
else if (block.type === "file") hasFiles = true;
|
|
58
|
+
}
|
|
59
|
+
return { text: texts.join(""), images, hasFiles };
|
|
60
|
+
}
|
|
61
|
+
function bytesToBase64(bytes) {
|
|
62
|
+
let binary = "";
|
|
63
|
+
const chunk = 32768;
|
|
64
|
+
for (let index = 0; index < bytes.length; index += chunk) {
|
|
65
|
+
binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
|
|
66
|
+
}
|
|
67
|
+
return btoa(binary);
|
|
68
|
+
}
|
|
69
|
+
function promptContent(text, images) {
|
|
70
|
+
return [
|
|
71
|
+
{ type: "text", text },
|
|
72
|
+
...images.map(
|
|
73
|
+
(image) => ({
|
|
74
|
+
type: "image",
|
|
75
|
+
mediaType: image.mediaType,
|
|
76
|
+
data: image.data,
|
|
77
|
+
...image.name === void 0 ? {} : { name: image.name }
|
|
78
|
+
})
|
|
79
|
+
)
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ../dsh-plugin-edit-message/src/client/icons.tsx
|
|
84
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
85
|
+
function IconEdit({ size = 16 }) {
|
|
86
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
87
|
+
"svg",
|
|
88
|
+
{
|
|
89
|
+
width: size,
|
|
90
|
+
height: size,
|
|
91
|
+
viewBox: "0 0 16 16",
|
|
92
|
+
fill: "none",
|
|
93
|
+
"aria-hidden": "true",
|
|
94
|
+
strokeWidth: 1,
|
|
95
|
+
children: [
|
|
96
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
97
|
+
"path",
|
|
98
|
+
{
|
|
99
|
+
d: "M8.85596 2.69971H4.19971C3.37141 2.69971 2.69992 3.37146 2.69971 4.19971V11.8003C2.69992 12.6285 3.37141 13.3003 4.19971 13.3003H11.8003C12.6283 13.2999 13.3001 12.6283 13.3003 11.8003V7.89893H14.3003V11.8003C14.3001 13.1806 13.1806 14.2999 11.8003 14.3003H4.19971C2.81913 14.3003 1.69992 13.1808 1.69971 11.8003V4.19971C1.69992 2.81918 2.81913 1.69971 4.19971 1.69971H8.85596V2.69971Z",
|
|
100
|
+
fill: "currentColor"
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7.7849 8.23878L13.888 2.13574", stroke: "currentColor" })
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ../dsh-plugin-edit-message/src/client/EditableUserMessage.tsx
|
|
110
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
111
|
+
function editableMessage(node) {
|
|
112
|
+
if (node.kind !== "user") return void 0;
|
|
113
|
+
const location = node.location;
|
|
114
|
+
if (location?.kind !== "step" || location.step?.step !== 1) return void 0;
|
|
115
|
+
const turn = location.turn?.turn;
|
|
116
|
+
const turnStartSeq = location.turn?.start?.seq;
|
|
117
|
+
if (typeof turn !== "number" || turn < 1 || typeof turnStartSeq !== "number") return void 0;
|
|
118
|
+
const content = node.data?.content;
|
|
119
|
+
if (!Array.isArray(content)) return void 0;
|
|
120
|
+
return { turn, turnStartSeq, content, parts: messageParts(content) };
|
|
121
|
+
}
|
|
122
|
+
function wrappedRenderer(entries, self, priority) {
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
if (entry.options.key !== "user" || entry.component === self) continue;
|
|
125
|
+
if ((entry.options.priority ?? 0) <= priority) continue;
|
|
126
|
+
return entry.component;
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
function findActionRow(root) {
|
|
131
|
+
const rows = root.querySelectorAll("[data-clock]");
|
|
132
|
+
return rows.length > 0 ? rows[rows.length - 1] ?? null : null;
|
|
133
|
+
}
|
|
134
|
+
function EditButton({
|
|
135
|
+
t,
|
|
136
|
+
unavailable,
|
|
137
|
+
onEdit
|
|
138
|
+
}) {
|
|
139
|
+
const [tip, setTip] = (0, import_react.useState)(false);
|
|
140
|
+
const reasonId = (0, import_react.useId)();
|
|
141
|
+
const label = t("edit");
|
|
142
|
+
const reason = unavailable === void 0 ? void 0 : t(`edit.${unavailable}`);
|
|
143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
144
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
145
|
+
"button",
|
|
146
|
+
{
|
|
147
|
+
type: "button",
|
|
148
|
+
className: "dsh-em-action",
|
|
149
|
+
"aria-label": label,
|
|
150
|
+
"aria-disabled": unavailable === void 0 ? void 0 : true,
|
|
151
|
+
"aria-describedby": reason === void 0 ? void 0 : reasonId,
|
|
152
|
+
"data-unavailable": unavailable === void 0 ? void 0 : "",
|
|
153
|
+
onClick: unavailable === void 0 ? onEdit : void 0,
|
|
154
|
+
onMouseEnter: () => setTip(true),
|
|
155
|
+
onMouseLeave: () => setTip(false),
|
|
156
|
+
onFocus: () => setTip(true),
|
|
157
|
+
onBlur: () => setTip(false),
|
|
158
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconEdit, {})
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
reason === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { id: reasonId, className: "dsh-em-hidden", children: reason }),
|
|
162
|
+
tip ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-em-tip", role: "tooltip", children: reason ?? label }) : null
|
|
163
|
+
] });
|
|
164
|
+
}
|
|
165
|
+
function failureText(t, reason, detail) {
|
|
166
|
+
return t(`error.${reason}`, { detail: detail ?? "" });
|
|
167
|
+
}
|
|
168
|
+
function MessageEditor({
|
|
169
|
+
t,
|
|
170
|
+
message,
|
|
171
|
+
onCancel,
|
|
172
|
+
onSend
|
|
173
|
+
}) {
|
|
174
|
+
const [text, setText] = (0, import_react.useState)(message.parts.text);
|
|
175
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
176
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
177
|
+
const input = (0, import_react.useRef)(null);
|
|
178
|
+
const mounted = (0, import_react.useRef)(true);
|
|
179
|
+
(0, import_react.useEffect)(
|
|
180
|
+
() => () => {
|
|
181
|
+
mounted.current = false;
|
|
182
|
+
},
|
|
183
|
+
[]
|
|
184
|
+
);
|
|
185
|
+
const resize = (0, import_react.useCallback)(() => {
|
|
186
|
+
const element = input.current;
|
|
187
|
+
if (element === null) return;
|
|
188
|
+
element.style.height = "auto";
|
|
189
|
+
element.style.height = `${element.scrollHeight}px`;
|
|
190
|
+
}, []);
|
|
191
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
192
|
+
const element = input.current;
|
|
193
|
+
if (element === null) return;
|
|
194
|
+
resize();
|
|
195
|
+
element.focus();
|
|
196
|
+
element.setSelectionRange(element.value.length, element.value.length);
|
|
197
|
+
}, [resize]);
|
|
198
|
+
const empty = text.trim().length === 0;
|
|
199
|
+
const send = async () => {
|
|
200
|
+
if (busy || empty) return;
|
|
201
|
+
setBusy(true);
|
|
202
|
+
setError(null);
|
|
203
|
+
const outcome = await onSend(text);
|
|
204
|
+
if (!mounted.current) return;
|
|
205
|
+
setBusy(false);
|
|
206
|
+
if (!outcome.ok) setError(failureText(t, outcome.reason, outcome.detail));
|
|
207
|
+
};
|
|
208
|
+
const onKeyDown = (event) => {
|
|
209
|
+
if (event.nativeEvent.isComposing) return;
|
|
210
|
+
if (event.key === "Escape") {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
event.stopPropagation();
|
|
213
|
+
if (!busy) onCancel();
|
|
214
|
+
} else if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
event.stopPropagation();
|
|
217
|
+
void send();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const images = message.parts.images.length;
|
|
221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-em-editor", "data-plugin": "@1outres/dsh-plugin-edit-message", children: [
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-em-box", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
223
|
+
"textarea",
|
|
224
|
+
{
|
|
225
|
+
ref: input,
|
|
226
|
+
className: "dsh-em-input",
|
|
227
|
+
"aria-label": t("editor.aria"),
|
|
228
|
+
value: text,
|
|
229
|
+
rows: 1,
|
|
230
|
+
disabled: busy,
|
|
231
|
+
onChange: (event) => {
|
|
232
|
+
setText(event.target.value);
|
|
233
|
+
resize();
|
|
234
|
+
},
|
|
235
|
+
onKeyDown
|
|
236
|
+
}
|
|
237
|
+
) }),
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-em-footer", children: [
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-em-notes", children: [
|
|
240
|
+
error === null ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-em-error", role: "alert", children: error }),
|
|
241
|
+
images > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("editor.images", { count: images }) }) : null
|
|
242
|
+
] }),
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-em-buttons", children: [
|
|
244
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
245
|
+
"button",
|
|
246
|
+
{
|
|
247
|
+
type: "button",
|
|
248
|
+
className: "dsh-em-button dsh-em-cancel",
|
|
249
|
+
disabled: busy,
|
|
250
|
+
onClick: onCancel,
|
|
251
|
+
children: t("editor.cancel")
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
255
|
+
"button",
|
|
256
|
+
{
|
|
257
|
+
type: "button",
|
|
258
|
+
className: "dsh-em-button dsh-em-send",
|
|
259
|
+
disabled: busy || empty,
|
|
260
|
+
onClick: () => void send(),
|
|
261
|
+
children: busy ? t("editor.sending") : t("editor.send")
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
] })
|
|
265
|
+
] })
|
|
266
|
+
] });
|
|
267
|
+
}
|
|
268
|
+
function createEditableUserMessage(deps) {
|
|
269
|
+
function EditableUserMessage(props) {
|
|
270
|
+
const Wrapped = wrappedRenderer(deps.entries(), EditableUserMessage, deps.priority);
|
|
271
|
+
if (Wrapped === void 0) throw new Error("edit-message: no user message renderer to wrap");
|
|
272
|
+
const { node, sessionId, useSession, useAccess } = props;
|
|
273
|
+
const running = useSession((snapshot) => snapshot.running);
|
|
274
|
+
const subagent = useSession(
|
|
275
|
+
(snapshot) => snapshot.subagent !== null && snapshot.subagent !== void 0
|
|
276
|
+
);
|
|
277
|
+
const forkAllowed = useAccess("session/fork");
|
|
278
|
+
const [editing, setEditing] = (0, import_react.useState)(false);
|
|
279
|
+
const [seat, setSeat] = (0, import_react.useState)(null);
|
|
280
|
+
const wrap = (0, import_react.useRef)(null);
|
|
281
|
+
const message = subagent ? void 0 : editableMessage(node);
|
|
282
|
+
const visible = message !== void 0;
|
|
283
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
284
|
+
const root = wrap.current;
|
|
285
|
+
if (editing || !visible || root === null) return;
|
|
286
|
+
const span = document.createElement("span");
|
|
287
|
+
span.className = "dsh-em-seat";
|
|
288
|
+
const attach = () => {
|
|
289
|
+
const row = findActionRow(root);
|
|
290
|
+
if (row === null) span.remove();
|
|
291
|
+
else if (span.parentElement !== row) row.append(span);
|
|
292
|
+
};
|
|
293
|
+
attach();
|
|
294
|
+
setSeat(span);
|
|
295
|
+
const observer = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(attach);
|
|
296
|
+
observer?.observe(root, { childList: true, subtree: true });
|
|
297
|
+
return () => {
|
|
298
|
+
observer?.disconnect();
|
|
299
|
+
span.remove();
|
|
300
|
+
setSeat(null);
|
|
301
|
+
};
|
|
302
|
+
}, [editing, visible]);
|
|
303
|
+
if (editing && message !== void 0) {
|
|
304
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
305
|
+
MessageEditor,
|
|
306
|
+
{
|
|
307
|
+
t: deps.t,
|
|
308
|
+
message,
|
|
309
|
+
onCancel: () => setEditing(false),
|
|
310
|
+
onSend: async (text) => {
|
|
311
|
+
const outcome = await deps.edit({
|
|
312
|
+
sessionId,
|
|
313
|
+
turn: message.turn,
|
|
314
|
+
turnStartSeq: message.turnStartSeq,
|
|
315
|
+
content: message.content,
|
|
316
|
+
text
|
|
317
|
+
});
|
|
318
|
+
if (outcome.ok) setEditing(false);
|
|
319
|
+
return outcome;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
const unavailable = !forkAllowed ? "noAccess" : running ? "running" : message?.parts.hasFiles ? "files" : void 0;
|
|
325
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: wrap, style: { display: "contents" }, children: [
|
|
326
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapped, { ...props }),
|
|
327
|
+
seat !== null && message !== void 0 ? (0, import_react_dom.createPortal)(
|
|
328
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EditButton, { t: deps.t, unavailable, onEdit: () => setEditing(true) }),
|
|
329
|
+
seat
|
|
330
|
+
) : null
|
|
331
|
+
] });
|
|
332
|
+
}
|
|
333
|
+
return EditableUserMessage;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ../dsh-plugin-edit-message/src/client/boundary.ts
|
|
337
|
+
function durableEvents(entries) {
|
|
338
|
+
const events = [];
|
|
339
|
+
for (const entry of entries) if (entry.type === "event") events.push(entry.event);
|
|
340
|
+
return events;
|
|
341
|
+
}
|
|
342
|
+
function opensExchange(event) {
|
|
343
|
+
if (event.type === "turn/start" || event.type === "agent/inbox/spliced") return true;
|
|
344
|
+
return event.type === "user/message" && event.surfaceOp === "append";
|
|
345
|
+
}
|
|
346
|
+
function forkBoundary(events, turn, turnStartSeq) {
|
|
347
|
+
const first = events[0];
|
|
348
|
+
if (first === void 0 || first.seq > turnStartSeq) {
|
|
349
|
+
return { kind: "load", through: Math.max(0, turnStartSeq - 1) };
|
|
350
|
+
}
|
|
351
|
+
let anchor;
|
|
352
|
+
for (const event of events) {
|
|
353
|
+
if (event.seq >= turnStartSeq) break;
|
|
354
|
+
if (event.type === "turn/end") anchor = event.seq;
|
|
355
|
+
}
|
|
356
|
+
if (anchor === void 0) {
|
|
357
|
+
if (first.seq > 0) {
|
|
358
|
+
const through = first.seq > turnStartSeq - 1 ? turnStartSeq - 1 : 0;
|
|
359
|
+
return { kind: "load", through: Math.max(0, through) };
|
|
360
|
+
}
|
|
361
|
+
if (turn > 1) return { kind: "invalid" };
|
|
362
|
+
anchor = -1;
|
|
363
|
+
}
|
|
364
|
+
let cut = anchor;
|
|
365
|
+
for (const event of events) {
|
|
366
|
+
if (event.seq <= anchor) continue;
|
|
367
|
+
if (event.seq >= turnStartSeq || opensExchange(event)) break;
|
|
368
|
+
cut = event.seq;
|
|
369
|
+
}
|
|
370
|
+
return cut < 0 ? { kind: "invalid" } : { kind: "cut", seq: cut };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ../dsh-plugin-edit-message/src/client/edit.ts
|
|
374
|
+
var REFERENCE_SOURCE = "controllerOperation";
|
|
375
|
+
var MAX_HISTORY_LOADS = 3;
|
|
376
|
+
var EditError = class extends Error {
|
|
377
|
+
constructor(reason, detail) {
|
|
378
|
+
super(detail ?? reason);
|
|
379
|
+
__publicField(this, "reason", reason);
|
|
380
|
+
__publicField(this, "detail", detail);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
function describe(error) {
|
|
384
|
+
if (error instanceof Error) {
|
|
385
|
+
const rpc = error.rpcError;
|
|
386
|
+
if (typeof rpc?.message === "string" && rpc.message.length > 0) return rpc.message;
|
|
387
|
+
return error.message;
|
|
388
|
+
}
|
|
389
|
+
return String(error);
|
|
390
|
+
}
|
|
391
|
+
function fail(reason, detail) {
|
|
392
|
+
return detail === void 0 ? { ok: false, reason } : { ok: false, reason, detail };
|
|
393
|
+
}
|
|
394
|
+
async function resolveCut(source, request) {
|
|
395
|
+
for (let attempt = 0; attempt <= MAX_HISTORY_LOADS; attempt++) {
|
|
396
|
+
const events = durableEvents(source.eventSource.getSnapshot().entries);
|
|
397
|
+
const boundary = forkBoundary(events, request.turn, request.turnStartSeq);
|
|
398
|
+
if (boundary.kind === "cut") return boundary.seq;
|
|
399
|
+
if (boundary.kind === "invalid" || attempt === MAX_HISTORY_LOADS) break;
|
|
400
|
+
const before = events[0]?.seq;
|
|
401
|
+
await source.session.loadThrough(boundary.through);
|
|
402
|
+
const after = durableEvents(source.eventSource.getSnapshot().entries)[0]?.seq;
|
|
403
|
+
if (after === before) break;
|
|
404
|
+
}
|
|
405
|
+
throw new EditError("boundary");
|
|
406
|
+
}
|
|
407
|
+
async function loadImages(source, request) {
|
|
408
|
+
const images = [];
|
|
409
|
+
for (const ref of messageParts(request.content).images) {
|
|
410
|
+
const read = await source.session.readAttachment(ref.attachmentId);
|
|
411
|
+
if (!read.ok) throw new EditError("attachment", read.error.message);
|
|
412
|
+
const name = read.value.attachment.name ?? ref.name;
|
|
413
|
+
images.push({
|
|
414
|
+
mediaType: read.value.attachment.mediaType,
|
|
415
|
+
data: bytesToBase64(read.value.data),
|
|
416
|
+
...name === void 0 ? {} : { name }
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return images;
|
|
420
|
+
}
|
|
421
|
+
function pendingIds(inbox) {
|
|
422
|
+
if (typeof inbox !== "object" || inbox === null) return [];
|
|
423
|
+
const ids = [];
|
|
424
|
+
for (const target of ["next-turn", "next-step"]) {
|
|
425
|
+
const items = inbox[target];
|
|
426
|
+
if (!Array.isArray(items)) continue;
|
|
427
|
+
for (const item of items) {
|
|
428
|
+
const id = item?.id;
|
|
429
|
+
if (typeof id === "string" && id.length > 0) ids.push(id);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return ids;
|
|
433
|
+
}
|
|
434
|
+
async function clearInheritedQueue(sessions, child) {
|
|
435
|
+
let inbox = child.session.projections.faceOf("inbox").getSnapshot();
|
|
436
|
+
if (inbox === void 0) {
|
|
437
|
+
await sessions.refreshProjections(child.sessionId);
|
|
438
|
+
inbox = child.session.projections.faceOf("inbox").getSnapshot() ?? sessions.list.getSnapshot().projectionsBySession?.[child.sessionId]?.values?.inbox;
|
|
439
|
+
}
|
|
440
|
+
for (const id of pendingIds(inbox)) {
|
|
441
|
+
const removed = await child.session.updateQueue(id, { kind: "remove" });
|
|
442
|
+
if (!removed.ok) throw new EditError("prepare", removed.error.message);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function placeBeside(deps, sourceId, childId) {
|
|
446
|
+
const workspaces = deps.workspaces;
|
|
447
|
+
if (workspaces === void 0) return;
|
|
448
|
+
const owner = workspaces.list.getSnapshot().items.find((item) => item.sessionIds.includes(sourceId));
|
|
449
|
+
if (owner === void 0) return;
|
|
450
|
+
try {
|
|
451
|
+
await workspaces.insertSessionBefore(owner.workspaceId, childId, sourceId);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
deps.warn?.("edit-message: could not place the new session beside the original", error);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async function runEdit(deps, request) {
|
|
457
|
+
const source = deps.sessions.binding(request.sessionId);
|
|
458
|
+
if (source === void 0) return fail("unavailable");
|
|
459
|
+
if (source.session.getSnapshot().running) return fail("running");
|
|
460
|
+
if (request.text.trim().length === 0) return fail("empty");
|
|
461
|
+
if (messageParts(request.content).hasFiles) return fail("files");
|
|
462
|
+
let cut;
|
|
463
|
+
try {
|
|
464
|
+
cut = await resolveCut(source, request);
|
|
465
|
+
} catch {
|
|
466
|
+
return fail("boundary");
|
|
467
|
+
}
|
|
468
|
+
let images;
|
|
469
|
+
try {
|
|
470
|
+
images = await loadImages(source, request);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
return fail("attachment", error instanceof EditError ? error.detail : describe(error));
|
|
473
|
+
}
|
|
474
|
+
let childId;
|
|
475
|
+
try {
|
|
476
|
+
childId = await deps.sessions.fork({
|
|
477
|
+
sessionId: request.sessionId,
|
|
478
|
+
atSeq: cut,
|
|
479
|
+
increaseTitle: false
|
|
480
|
+
});
|
|
481
|
+
} catch (error) {
|
|
482
|
+
return fail("fork", describe(error));
|
|
483
|
+
}
|
|
484
|
+
const reference = deps.sessions.retain(childId, { source: REFERENCE_SOURCE });
|
|
485
|
+
try {
|
|
486
|
+
const child = await reference.ready;
|
|
487
|
+
await clearInheritedQueue(deps.sessions, child);
|
|
488
|
+
const title = deps.sessions.list.getSnapshot().byId[request.sessionId]?.title;
|
|
489
|
+
if (request.turn > 1 && title !== void 0 && title.length > 0) {
|
|
490
|
+
const renamed = await child.session.rename(title);
|
|
491
|
+
if (!renamed.ok) deps.warn?.("edit-message: could not keep the title", renamed.error);
|
|
492
|
+
}
|
|
493
|
+
const sent = await child.session.prompt(promptContent(request.text, images), "queue");
|
|
494
|
+
if (!sent.ok) throw new EditError("prompt", sent.error.message);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
reference.release();
|
|
497
|
+
try {
|
|
498
|
+
await deps.uiWorkspace.archiveSession(childId);
|
|
499
|
+
} catch (cleanup) {
|
|
500
|
+
deps.warn?.("edit-message: could not archive the unused fork", cleanup);
|
|
501
|
+
}
|
|
502
|
+
if (error instanceof EditError) return fail(error.reason, error.detail);
|
|
503
|
+
return fail("prepare", describe(error));
|
|
504
|
+
}
|
|
505
|
+
await placeBeside(deps, request.sessionId, childId);
|
|
506
|
+
deps.uiWorkspace.openSession(childId);
|
|
507
|
+
reference.release();
|
|
508
|
+
try {
|
|
509
|
+
await deps.uiWorkspace.archiveSession(request.sessionId);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
deps.warn?.("edit-message: could not archive the original session", error);
|
|
512
|
+
}
|
|
513
|
+
return { ok: true, sessionId: childId };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ../dsh-plugin-edit-message/src/client/i18n.ts
|
|
517
|
+
var en = {
|
|
518
|
+
edit: "Edit",
|
|
519
|
+
"edit.running": "Stop the running turn to edit this message",
|
|
520
|
+
"edit.files": "Messages with attached files can't be edited",
|
|
521
|
+
"edit.noAccess": "You can't branch this session",
|
|
522
|
+
"editor.aria": "Edit message",
|
|
523
|
+
"editor.cancel": "Cancel",
|
|
524
|
+
"editor.send": "Send",
|
|
525
|
+
"editor.sending": "Sending\u2026",
|
|
526
|
+
"editor.images": "{count} image(s) from the original message are sent again.",
|
|
527
|
+
"error.unavailable": "This session is not loaded.",
|
|
528
|
+
"error.running": "The session is running. Stop it, then send again.",
|
|
529
|
+
"error.empty": "Enter a message.",
|
|
530
|
+
"error.files": "Messages with attached files can't be edited.",
|
|
531
|
+
"error.boundary": "Could not find where this message starts in the session history.",
|
|
532
|
+
"error.attachment": "Could not read the original images: {detail}",
|
|
533
|
+
"error.fork": "Could not create the new session: {detail}",
|
|
534
|
+
"error.prepare": "Could not prepare the new session: {detail}",
|
|
535
|
+
"error.prompt": "The new session did not accept the message: {detail}"
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// ../dsh-plugin-edit-message/src/client/styles.ts
|
|
539
|
+
var EDIT_CSS = [
|
|
540
|
+
".dsh-em-seat{display:inline-flex;position:relative}",
|
|
541
|
+
".dsh-em-action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));border-radius:var(--dsw-radius-sm);color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:6px;display:inline-flex}",
|
|
542
|
+
".dsh-em-action svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px))}",
|
|
543
|
+
".dsh-em-action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}",
|
|
544
|
+
".dsh-em-action:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary))}",
|
|
545
|
+
".dsh-em-action[data-unavailable]{cursor:default;opacity:.4}",
|
|
546
|
+
".dsh-em-action[data-unavailable]:hover{color:var(--dsw-alias-label-tertiary);background:0 0}",
|
|
547
|
+
".dsh-em-tip{position:absolute;top:calc(100% + 4px);left:50%;transform:translate(-50%);z-index:100;width:max-content;max-width:280px;box-sizing:border-box;padding:3px 7px;border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-tooltip-bg);color:var(--dsw-alias-toast-label);font-size:13px;line-height:20px;white-space:pre-line;overflow-wrap:break-word;pointer-events:none;animation:dsh-em-tip-in .15s ease-in-out}",
|
|
548
|
+
"@keyframes dsh-em-tip-in{0%{opacity:0}to{opacity:1}}",
|
|
549
|
+
".dsh-em-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}",
|
|
550
|
+
".dsh-em-editor{box-sizing:border-box;width:100%;display:flex;flex-direction:column;align-items:stretch;gap:8px}",
|
|
551
|
+
".dsh-em-box{box-sizing:border-box;background:var(--dsw-specific-bubble);border-radius:var(--dsw-radius-xl);padding:10px 16px;border:.5px solid transparent}",
|
|
552
|
+
".dsh-em-box:focus-within{border-color:var(--dsw-alias-border-l3)}",
|
|
553
|
+
".dsh-em-input{display:block;box-sizing:border-box;width:100%;min-height:calc(22px + var(--dsh-content-font-delta,0px));max-height:50vh;margin:0;padding:0;border:none;outline:none;resize:none;overflow-y:auto;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));white-space:pre-wrap;word-break:break-word}",
|
|
554
|
+
".dsh-em-input:disabled{color:var(--dsw-alias-label-secondary)}",
|
|
555
|
+
".dsh-em-footer{display:flex;align-items:flex-start;gap:8px}",
|
|
556
|
+
".dsh-em-notes{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}",
|
|
557
|
+
".dsh-em-error{color:var(--dsw-alias-state-error-primary)}",
|
|
558
|
+
".dsh-em-buttons{flex:none;display:flex;align-items:center;gap:8px}",
|
|
559
|
+
".dsh-em-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;height:28px;padding:0 10px;border:none;border-radius:var(--dsw-radius-sm);cursor:pointer;font:inherit;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);background:transparent}",
|
|
560
|
+
".dsh-em-button:disabled{cursor:not-allowed;opacity:.4}",
|
|
561
|
+
".dsh-em-button:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary))}",
|
|
562
|
+
".dsh-em-cancel{border:.5px solid var(--dsw-alias-border-l3)}",
|
|
563
|
+
".dsh-em-cancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
|
|
564
|
+
".dsh-em-send{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}",
|
|
565
|
+
".dsh-em-send:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}",
|
|
566
|
+
"@media (prefers-reduced-motion:reduce){.dsh-em-tip{animation:none}}"
|
|
567
|
+
].join("\n");
|
|
568
|
+
|
|
569
|
+
// ../dsh-plugin-edit-message/src/client/index.tsx
|
|
570
|
+
var inject = ["slots", "locale", "sessions", "uiWorkspace", "workspaces"];
|
|
571
|
+
var PLUGIN_ID = "@1outres/dsh-plugin-edit-message";
|
|
572
|
+
var NAMESPACE = "edit-message";
|
|
573
|
+
var NODE_SLOT = "conversation.chat.node";
|
|
574
|
+
var NODE_KEY = "user";
|
|
575
|
+
var NODE_PRIORITY = -100;
|
|
576
|
+
var CHAT_NAMESPACE = "chat";
|
|
577
|
+
function apply(ctx) {
|
|
578
|
+
ctx.effect(() => ctx.locale.register(NAMESPACE, { en }), "edit-message: dictionaries");
|
|
579
|
+
ctx.effect(() => {
|
|
580
|
+
const tag = document.createElement("style");
|
|
581
|
+
tag.dataset.plugin = PLUGIN_ID;
|
|
582
|
+
tag.dataset.pluginCss = `${PLUGIN_ID}/edit.css`;
|
|
583
|
+
tag.textContent = EDIT_CSS;
|
|
584
|
+
document.head.append(tag);
|
|
585
|
+
return () => {
|
|
586
|
+
tag.remove();
|
|
587
|
+
};
|
|
588
|
+
}, "edit-message: stylesheet");
|
|
589
|
+
const slots = ctx.slots;
|
|
590
|
+
if (typeof slots.entries !== "function") {
|
|
591
|
+
console.warn("edit-message: this Harness exposes no slot entries; the plugin stays inactive");
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const entries = () => slots.entries?.(NODE_SLOT) ?? [];
|
|
595
|
+
const View = createEditableUserMessage({
|
|
596
|
+
entries,
|
|
597
|
+
priority: NODE_PRIORITY,
|
|
598
|
+
t: ctx.locale.bind(NAMESPACE),
|
|
599
|
+
edit: (request) => runEdit(
|
|
600
|
+
{
|
|
601
|
+
sessions: ctx.sessions,
|
|
602
|
+
uiWorkspace: ctx.uiWorkspace,
|
|
603
|
+
workspaces: ctx.workspaces,
|
|
604
|
+
warn: (message, error) => console.warn(message, error)
|
|
605
|
+
},
|
|
606
|
+
request
|
|
607
|
+
)
|
|
608
|
+
});
|
|
609
|
+
ctx.effect(
|
|
610
|
+
() => slots.inject(
|
|
611
|
+
NODE_SLOT,
|
|
612
|
+
() => slots.register(
|
|
613
|
+
{ name: NODE_SLOT, key: NODE_KEY, priority: NODE_PRIORITY, locale: CHAT_NAMESPACE },
|
|
614
|
+
View
|
|
615
|
+
)
|
|
616
|
+
),
|
|
617
|
+
"edit-message: user message renderer"
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return module.exports;
|
|
622
|
+
}
|
|
623
|
+
});
|
package/lib/index.js
ADDED
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1outres/dsh-plugin-edit-message",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Edit a sent message in the DeepSeek Harness Web UI: the conversation restarts from just before it with the edited text.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/1outres/my-dsh-plugins.git",
|
|
8
|
+
"directory": "packages/dsh-plugin-edit-message"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "lib/index.js",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./lib/index.js",
|
|
14
|
+
"./client": "./lib/client.js",
|
|
15
|
+
"./locale/*.json": "./locale/*.json",
|
|
16
|
+
"./icon": "./icon.svg",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib",
|
|
21
|
+
"cordis.patch.yml",
|
|
22
|
+
"locale/*.json",
|
|
23
|
+
"icon.svg"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"dsh",
|
|
27
|
+
"dsh-plugin",
|
|
28
|
+
"edit-message",
|
|
29
|
+
"rewind",
|
|
30
|
+
"deepseek-harness"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"dsh": ">=0.2.1-rc.5 <0.3.0",
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@deepseek-ai/cordis": "~4.0.5",
|
|
42
|
+
"react": "^18.2.0"
|
|
43
|
+
},
|
|
44
|
+
"dsh": {
|
|
45
|
+
"bundle": {
|
|
46
|
+
"patch": [
|
|
47
|
+
"./cordis.patch.yml"
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"client": {
|
|
51
|
+
"platform": "web",
|
|
52
|
+
"inject": [
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
54
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
55
|
+
"@deepseek-ai/dsh-api-workspace-controller",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-workspace",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-chat"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
6
61
|
}
|