@norman-else/dsh-claude 0.1.41 → 0.1.43
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/README.md +3 -1
- package/lib/bin.mjs +1 -1
- package/lib/client.d.ts +33 -1
- package/lib/client.js +1026 -107
- package/lib/client.js.map +1 -1
- package/lib/{events-B-FPMzI7.mjs → events-oovRTmX7.mjs} +5 -2
- package/lib/events-oovRTmX7.mjs.map +1 -0
- package/lib/index.d.mts +1 -0
- package/lib/index.mjs +823 -75
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-BV42EKkB.mjs → presenters-BVWj7u0a.mjs} +2 -2
- package/lib/{presenters-BV42EKkB.mjs.map → presenters-BVWj7u0a.mjs.map} +1 -1
- package/lib/{preset-installer-yRGfkGjd.mjs → preset-installer-JUktnfwS.mjs} +2 -2
- package/lib/{preset-installer-yRGfkGjd.mjs.map → preset-installer-JUktnfwS.mjs.map} +1 -1
- package/lib/preset-route.mjs +2 -2
- package/package.json +1 -1
- package/lib/events-B-FPMzI7.mjs.map +0 -1
package/lib/client.js
CHANGED
|
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
module.exports;
|
|
6
6
|
var { Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } = require("react");
|
|
7
|
-
var { DiffBlock, DisclosureRow, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconEllipsisOutline16, IconFullscreenOutline16, IconQueueOutline14, IconRefreshOutline14, IconRightUpOutline14, IconSearchOutline16, IconSendOutline14, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Toast, Tooltip, useDismissOnOutsidePointer } = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
7
|
+
var { Button, DiffBlock, DisclosureRow, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconEllipsisOutline16, IconFullscreenOutline16, IconListPenOutline16, IconLoadingOutline16, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconSearchOutline16, IconSendOutline14, IconSparkle16, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Toast, Tooltip, useDismissOnOutsidePointer } = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
8
8
|
var { Fragment: Fragment$1, jsx, jsxs } = require("react/jsx-runtime");
|
|
9
9
|
var { createPortal } = require("react-dom");
|
|
10
10
|
/** Claude's subagent dispatch tools; rendered as plugin-owned group cards
|
|
@@ -28,6 +28,9 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
const CLAUDE_EDITOR_OPEN_PATH = "/plugins/dsh-claude/editor/open";
|
|
29
29
|
const CLAUDE_REWIND_PATH = "/plugins/dsh-claude/rewind";
|
|
30
30
|
const CLAUDE_PLAN_FEEDBACK_PATH = "/plugins/dsh-claude/plan/feedback";
|
|
31
|
+
const CLAUDE_PROMPTS_PATH = "/plugins/dsh-claude/prompts";
|
|
32
|
+
const CLAUDE_PROMPT_NAME_PATH = "/plugins/dsh-claude/prompts/name";
|
|
33
|
+
const CLAUDE_PROMPT_REFINE_PATH = "/plugins/dsh-claude/prompts/refine";
|
|
31
34
|
function isClaudeRenderMode(value) {
|
|
32
35
|
return value === "plugin" || value === "native";
|
|
33
36
|
}
|
|
@@ -349,6 +352,22 @@ window.__ModuleLoader__.load({
|
|
|
349
352
|
function nativelyRenderedStep(activities, turn, step) {
|
|
350
353
|
return activities.some((activity) => activity.turn === turn && activity.step === step && activity.renderer === "native");
|
|
351
354
|
}
|
|
355
|
+
/** The turn's own accounting, drawn as a footer.
|
|
356
|
+
*
|
|
357
|
+
* It hangs off the turn rather than the step that reported it: a turn waiting
|
|
358
|
+
* on background tasks reports usage per settled segment, and the task badge
|
|
359
|
+
* the plugin draws at the turn's foot would otherwise sit *under* a line that
|
|
360
|
+
* reads as a closing total. Every report is cumulative, so the newest one
|
|
361
|
+
* supersedes the ones before it. Natively drawn steps keep the Host's footer.
|
|
362
|
+
*/
|
|
363
|
+
function latestTurnUsage(activities, turn) {
|
|
364
|
+
let latest;
|
|
365
|
+
for (const activity of activities) {
|
|
366
|
+
if (activity.turn !== turn || activity.kind !== "usage" || activity.renderer === "native") continue;
|
|
367
|
+
if (activity.usage !== void 0) latest = activity.usage;
|
|
368
|
+
}
|
|
369
|
+
return latest;
|
|
370
|
+
}
|
|
352
371
|
/** Fold one step's shared ordinal stream into Claude Code-style prose and tool groups. */
|
|
353
372
|
function transcriptItemsForStep(activities, turn, step, tasks = []) {
|
|
354
373
|
if (nativelyRenderedStep(activities, turn, step)) return [];
|
|
@@ -410,15 +429,6 @@ window.__ModuleLoader__.load({
|
|
|
410
429
|
});
|
|
411
430
|
continue;
|
|
412
431
|
}
|
|
413
|
-
if (activity.kind === "usage" && activity.usage !== void 0) {
|
|
414
|
-
flushGroup();
|
|
415
|
-
items.push({
|
|
416
|
-
kind: "usage",
|
|
417
|
-
ordinal: activity.ordinal,
|
|
418
|
-
usage: activity.usage
|
|
419
|
-
});
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
432
|
if (activity.kind === "tool-call" && activity.toolUseId !== void 0 && activity.toolName !== void 0) {
|
|
423
433
|
group ??= {
|
|
424
434
|
ordinal: activity.ordinal,
|
|
@@ -1834,7 +1844,7 @@ window.__ModuleLoader__.load({
|
|
|
1834
1844
|
whiteSpace: "nowrap"
|
|
1835
1845
|
};
|
|
1836
1846
|
const repositoryBarFrame = {
|
|
1837
|
-
width: "calc(100% -
|
|
1847
|
+
width: "calc(100% - 2 * var(--dsh-composer-side-clearance, 16px))",
|
|
1838
1848
|
maxWidth: "var(--dsh-composer-card-max-width, 782px)",
|
|
1839
1849
|
minWidth: 0,
|
|
1840
1850
|
margin: "0 auto",
|
|
@@ -2231,6 +2241,14 @@ window.__ModuleLoader__.load({
|
|
|
2231
2241
|
whiteSpace: "nowrap",
|
|
2232
2242
|
cursor: "pointer"
|
|
2233
2243
|
};
|
|
2244
|
+
/** A stopped rebase is the one bar control that reports a blocked checkout
|
|
2245
|
+
* rather than an available action, so it carries the warning tone. */
|
|
2246
|
+
const repositoryConflictTrigger = {
|
|
2247
|
+
...repositoryUpdateTrigger,
|
|
2248
|
+
borderColor: "color-mix(in srgb, var(--dsw-alias-state-warning-primary, #d69e2e) 55%, transparent)",
|
|
2249
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warning-primary, #d69e2e) 14%, transparent)",
|
|
2250
|
+
color: "var(--dsw-alias-state-warning-primary, #d69e2e)"
|
|
2251
|
+
};
|
|
2234
2252
|
const repositoryChecksFrame = {
|
|
2235
2253
|
position: "relative",
|
|
2236
2254
|
display: "inline-flex",
|
|
@@ -2994,7 +3012,7 @@ window.__ModuleLoader__.load({
|
|
|
2994
3012
|
fontSize: 11
|
|
2995
3013
|
};
|
|
2996
3014
|
const reviewCommentBarFrame = {
|
|
2997
|
-
width: "calc(100% -
|
|
3015
|
+
width: "calc(100% - 2 * var(--dsh-composer-side-clearance, 16px))",
|
|
2998
3016
|
maxWidth: "var(--dsh-composer-card-max-width, 782px)",
|
|
2999
3017
|
margin: "0 auto",
|
|
3000
3018
|
boxSizing: "border-box"
|
|
@@ -3085,6 +3103,113 @@ window.__ModuleLoader__.load({
|
|
|
3085
3103
|
whiteSpace: "pre-wrap",
|
|
3086
3104
|
overflowWrap: "anywhere"
|
|
3087
3105
|
};
|
|
3106
|
+
const promptSaveTriggerClass = "dshClaudePromptSaveTrigger";
|
|
3107
|
+
const promptSpinClass = "dshClaudePromptSpin";
|
|
3108
|
+
const promptUndoClass = "dshClaudePromptUndo";
|
|
3109
|
+
const promptSaveFieldClass = "dshClaudePromptSaveField";
|
|
3110
|
+
/** The composer's own attach button geometry (InputBar.module.css `.add`:
|
|
3111
|
+
* 28x28 circle on --dsw-specific-selector, 14px glyph). The primitives'
|
|
3112
|
+
* Button has no icon-only form — its `sm` is a 36px-wide capsule around a
|
|
3113
|
+
* 16px slot, which is why this reads oversized beside its neighbours. */
|
|
3114
|
+
const promptSaveTriggerCss = `
|
|
3115
|
+
.${promptSaveTriggerClass} {
|
|
3116
|
+
width: 28px;
|
|
3117
|
+
height: 28px;
|
|
3118
|
+
flex: none;
|
|
3119
|
+
display: grid;
|
|
3120
|
+
place-items: center;
|
|
3121
|
+
padding: 0;
|
|
3122
|
+
border: none;
|
|
3123
|
+
border-radius: 999px;
|
|
3124
|
+
background: var(--dsw-specific-selector);
|
|
3125
|
+
color: var(--dsw-alias-label-primary);
|
|
3126
|
+
cursor: pointer;
|
|
3127
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
3128
|
+
}
|
|
3129
|
+
/* With no draft there is nothing to keep, so the control drops its seat
|
|
3130
|
+
* entirely rather than sitting there as a filled-but-dead circle. */
|
|
3131
|
+
.${promptSaveTriggerClass}:disabled {
|
|
3132
|
+
cursor: default;
|
|
3133
|
+
background: transparent;
|
|
3134
|
+
color: var(--dsw-alias-label-tertiary);
|
|
3135
|
+
}
|
|
3136
|
+
.${promptSaveTriggerClass}:focus-visible { outline: none; }
|
|
3137
|
+
.${promptSaveFieldClass} {
|
|
3138
|
+
box-sizing: border-box;
|
|
3139
|
+
width: 100%;
|
|
3140
|
+
height: 30px;
|
|
3141
|
+
padding: 0 9px;
|
|
3142
|
+
border: 1px solid var(--dsw-alias-border-inverted);
|
|
3143
|
+
border-radius: 7px;
|
|
3144
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
3145
|
+
color: var(--dsw-alias-label-primary);
|
|
3146
|
+
font-family: var(--dsw-font-family);
|
|
3147
|
+
font-size: 13px;
|
|
3148
|
+
line-height: 20px;
|
|
3149
|
+
}
|
|
3150
|
+
/* Not --dsw-alias-brand-primary: in the dark theme that alias resolves to
|
|
3151
|
+
* neutral-bluish-50, so a focused field draws a near-white slab around
|
|
3152
|
+
* itself. The focus ring is a step up in the same greys the card is made of. */
|
|
3153
|
+
.${promptSaveFieldClass}:focus-visible {
|
|
3154
|
+
outline: none;
|
|
3155
|
+
border-color: var(--dsw-alias-label-tertiary);
|
|
3156
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
3157
|
+
}
|
|
3158
|
+
.${promptSaveFieldClass}:disabled { opacity: 0.6; }
|
|
3159
|
+
/* ic_ds_loading_outline_16 is a bare open ring and nothing in the primitives
|
|
3160
|
+
* turns it, so a four-second rewrite sat behind a frozen glyph that reads as
|
|
3161
|
+
* the letter C. The spin is what makes it a spinner. */
|
|
3162
|
+
.${promptSpinClass} { animation: dshClaudePromptSpin 900ms linear infinite; }
|
|
3163
|
+
@keyframes dshClaudePromptSpin { to { transform: rotate(360deg); } }
|
|
3164
|
+
/* The icon set has no undo arrow, and ic_ds_refresh_outline_16 turns
|
|
3165
|
+
* clockwise — the redo direction. Mirrored, it is the ordinary undo glyph. */
|
|
3166
|
+
.${promptUndoClass} { transform: scaleX(-1); }
|
|
3167
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3168
|
+
.${promptSpinClass} { animation: none; }
|
|
3169
|
+
}
|
|
3170
|
+
`;
|
|
3171
|
+
/** The naming card hangs off the composer tool row, portaled so the composer
|
|
3172
|
+
* card cannot clip it and positioned by useAnchoredPosition. */
|
|
3173
|
+
const promptSaveCard = {
|
|
3174
|
+
position: "fixed",
|
|
3175
|
+
zIndex: 120,
|
|
3176
|
+
boxSizing: "border-box",
|
|
3177
|
+
display: "flex",
|
|
3178
|
+
flexDirection: "column",
|
|
3179
|
+
gap: 8,
|
|
3180
|
+
width: 340,
|
|
3181
|
+
maxWidth: "calc(100vw - 32px)",
|
|
3182
|
+
padding: "10px 12px",
|
|
3183
|
+
border: "1px solid var(--dsw-alias-border-inverted)",
|
|
3184
|
+
borderRadius: 11,
|
|
3185
|
+
background: "var(--dsw-specific-menu)",
|
|
3186
|
+
boxShadow: "var(--dsw-shadow-lv3)",
|
|
3187
|
+
color: "var(--dsw-alias-label-primary)",
|
|
3188
|
+
fontSize: 12,
|
|
3189
|
+
lineHeight: "20px"
|
|
3190
|
+
};
|
|
3191
|
+
const promptSaveHeading = {
|
|
3192
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3193
|
+
fontSize: 11,
|
|
3194
|
+
lineHeight: "16px"
|
|
3195
|
+
};
|
|
3196
|
+
const promptSaveActions = {
|
|
3197
|
+
display: "flex",
|
|
3198
|
+
justifyContent: "flex-end",
|
|
3199
|
+
gap: 6
|
|
3200
|
+
};
|
|
3201
|
+
/** The saved file, wrapped rather than truncated: a path the user cannot read
|
|
3202
|
+
* in full does not tell them where to go and edit it. */
|
|
3203
|
+
const promptSaveLocation = {
|
|
3204
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3205
|
+
fontSize: 11,
|
|
3206
|
+
lineHeight: "16px",
|
|
3207
|
+
overflowWrap: "anywhere"
|
|
3208
|
+
};
|
|
3209
|
+
const promptSaveError = {
|
|
3210
|
+
color: "var(--dsw-static-red-450, #d64545)",
|
|
3211
|
+
overflowWrap: "anywhere"
|
|
3212
|
+
};
|
|
3088
3213
|
const diffAhead = { color: "var(--dsw-static-blue-450)" };
|
|
3089
3214
|
const diffAheadMuted = { color: "color-mix(in srgb, var(--dsw-static-blue-450) 62%, var(--dsw-alias-label-tertiary))" };
|
|
3090
3215
|
const diffAddMuted = { color: "color-mix(in srgb, var(--dsw-alias-state-success-primary) 62%, var(--dsw-alias-label-tertiary))" };
|
|
@@ -4008,6 +4133,16 @@ window.__ModuleLoader__.load({
|
|
|
4008
4133
|
laneHeld[lane] -= 1;
|
|
4009
4134
|
pump();
|
|
4010
4135
|
}
|
|
4136
|
+
/** Runs at most once, so a release can be wired to every ending of a stream
|
|
4137
|
+
* without any of them having to know about the others. */
|
|
4138
|
+
function once(action) {
|
|
4139
|
+
let done = false;
|
|
4140
|
+
return () => {
|
|
4141
|
+
if (done) return;
|
|
4142
|
+
done = true;
|
|
4143
|
+
action();
|
|
4144
|
+
};
|
|
4145
|
+
}
|
|
4011
4146
|
function acquire(lane) {
|
|
4012
4147
|
let released = false;
|
|
4013
4148
|
const releaseOnce = () => {
|
|
@@ -4111,11 +4246,53 @@ window.__ModuleLoader__.load({
|
|
|
4111
4246
|
writeId += 1;
|
|
4112
4247
|
return writeId;
|
|
4113
4248
|
}
|
|
4249
|
+
/**
|
|
4250
|
+
* A reader that gives its permit back however the stream ends.
|
|
4251
|
+
*
|
|
4252
|
+
* The permit used to be released by one event only -- the caller's abort --
|
|
4253
|
+
* and a stream has three endings, two of which the caller does not cause: the
|
|
4254
|
+
* server closing the response, and the body failing mid-read. Either one left
|
|
4255
|
+
* the permit held. For the reserved carrier that permit is a single boolean,
|
|
4256
|
+
* so one such ending closed the transcript stream for the life of the page and
|
|
4257
|
+
* every reopen answered `starved`, silently, forever.
|
|
4258
|
+
*
|
|
4259
|
+
* Binding the release to the reader rather than to the signal is what makes
|
|
4260
|
+
* that unrepresentable: a caller cannot hold the permit past the stream it is
|
|
4261
|
+
* reading, because every way of leaving the read passes through here. `free`
|
|
4262
|
+
* is idempotent, so the abort listener below stays as the belt to this brace.
|
|
4263
|
+
*/
|
|
4264
|
+
function releasingReader(reader, free) {
|
|
4265
|
+
return {
|
|
4266
|
+
get closed() {
|
|
4267
|
+
return reader.closed;
|
|
4268
|
+
},
|
|
4269
|
+
read: async () => {
|
|
4270
|
+
try {
|
|
4271
|
+
const chunk = await reader.read();
|
|
4272
|
+
if (chunk.done) free();
|
|
4273
|
+
return chunk;
|
|
4274
|
+
} catch (error) {
|
|
4275
|
+
free();
|
|
4276
|
+
throw error;
|
|
4277
|
+
}
|
|
4278
|
+
},
|
|
4279
|
+
cancel: async (reason) => {
|
|
4280
|
+
try {
|
|
4281
|
+
await reader.cancel(reason);
|
|
4282
|
+
} finally {
|
|
4283
|
+
free();
|
|
4284
|
+
}
|
|
4285
|
+
},
|
|
4286
|
+
releaseLock: () => {
|
|
4287
|
+
reader.releaseLock();
|
|
4288
|
+
}
|
|
4289
|
+
};
|
|
4290
|
+
}
|
|
4114
4291
|
async function openStream(lane, path, cancel, options, reserved) {
|
|
4115
4292
|
const url = withQuery(path, options?.query);
|
|
4116
|
-
const free = reserved ? () => {
|
|
4293
|
+
const free = reserved ? once(() => {
|
|
4117
4294
|
projectionHeld = false;
|
|
4118
|
-
} : await acquire(lane);
|
|
4295
|
+
}) : await acquire(lane);
|
|
4119
4296
|
try {
|
|
4120
4297
|
const response = await send(url, {
|
|
4121
4298
|
method: options?.method ?? "GET",
|
|
@@ -4133,7 +4310,7 @@ window.__ModuleLoader__.load({
|
|
|
4133
4310
|
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
4134
4311
|
}
|
|
4135
4312
|
cancel.addEventListener("abort", free, { once: true });
|
|
4136
|
-
return response.body.getReader();
|
|
4313
|
+
return releasingReader(response.body.getReader(), free);
|
|
4137
4314
|
} catch (error) {
|
|
4138
4315
|
free();
|
|
4139
4316
|
throw failureOf(error, cancel);
|
|
@@ -4188,6 +4365,7 @@ window.__ModuleLoader__.load({
|
|
|
4188
4365
|
const MAX_COMMANDS = 2e3;
|
|
4189
4366
|
const MAX_REPOSITORY_TEXT_CHARS = 1024;
|
|
4190
4367
|
const MAX_DIFF_CHARS = 262144;
|
|
4368
|
+
const MAX_CONFLICT_PATHS = 100;
|
|
4191
4369
|
const MAX_REVIEW_COMMENTS = 50;
|
|
4192
4370
|
const MAX_REVIEW_COMMENT_CHARS = 2e3;
|
|
4193
4371
|
const MAX_TRANSCRIPT_CHARS = 64e3;
|
|
@@ -4206,7 +4384,12 @@ window.__ModuleLoader__.load({
|
|
|
4206
4384
|
"ready",
|
|
4207
4385
|
"not-repository",
|
|
4208
4386
|
"unavailable"
|
|
4209
|
-
].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind)
|
|
4387
|
+
].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind) || repository.operation !== void 0 && ![
|
|
4388
|
+
"rebase",
|
|
4389
|
+
"merge",
|
|
4390
|
+
"cherry-pick",
|
|
4391
|
+
"revert"
|
|
4392
|
+
].includes(String(repository.operation)) || repository.conflicts !== void 0 && (!Array.isArray(repository.conflicts) || repository.conflicts.length > MAX_CONFLICT_PATHS || repository.conflicts.some((path) => typeof path !== "string" || path.length > MAX_REPOSITORY_TEXT_CHARS))) return false;
|
|
4210
4393
|
if (repository.diff !== void 0) {
|
|
4211
4394
|
const diff = record$7(repository.diff);
|
|
4212
4395
|
if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
|
|
@@ -4598,6 +4781,10 @@ window.__ModuleLoader__.load({
|
|
|
4598
4781
|
#report;
|
|
4599
4782
|
#resyncCooldownMs;
|
|
4600
4783
|
#resyncedAt = 0;
|
|
4784
|
+
/** Whether the carrier is in a run of failures. One report per outage: the
|
|
4785
|
+
* retry loop runs every couple of seconds and a beacon per attempt would
|
|
4786
|
+
* be the plugin reporting its own noise. */
|
|
4787
|
+
#carrierFailing = false;
|
|
4601
4788
|
#controller;
|
|
4602
4789
|
#settle;
|
|
4603
4790
|
#running = false;
|
|
@@ -4683,6 +4870,7 @@ window.__ModuleLoader__.load({
|
|
|
4683
4870
|
let superseded = false;
|
|
4684
4871
|
try {
|
|
4685
4872
|
const reader = await this.#open(`${CLAUDE_PROJECTION_PATH}/multi?sessions=${lanes.map(encodeURIComponent).join(",")}`, controller.signal);
|
|
4873
|
+
this.#carrierFailing = false;
|
|
4686
4874
|
const stop = () => {
|
|
4687
4875
|
superseded = true;
|
|
4688
4876
|
reader.cancel().catch(() => void 0);
|
|
@@ -4705,6 +4893,10 @@ window.__ModuleLoader__.load({
|
|
|
4705
4893
|
if (this.#controller !== controller) continue;
|
|
4706
4894
|
return;
|
|
4707
4895
|
}
|
|
4896
|
+
if (!this.#carrierFailing) {
|
|
4897
|
+
this.#carrierFailing = true;
|
|
4898
|
+
this.#report("projection-carrier-unavailable", error instanceof Error ? error.message : String(error));
|
|
4899
|
+
}
|
|
4708
4900
|
} finally {
|
|
4709
4901
|
if (this.#controller === controller) this.#controller = void 0;
|
|
4710
4902
|
}
|
|
@@ -5208,8 +5400,10 @@ window.__ModuleLoader__.load({
|
|
|
5208
5400
|
return parts;
|
|
5209
5401
|
}
|
|
5210
5402
|
/** The footer the Host draws under its own assistant message, drawn here for
|
|
5211
|
-
* the steps the Host never had a message for.
|
|
5403
|
+
* the steps the Host never had a message for. Mounted at the turn's foot, so
|
|
5404
|
+
* it closes the turn under the task badge rather than above it. */
|
|
5212
5405
|
function ClaudeTurnUsage({ usage, t }) {
|
|
5406
|
+
ensureCss$4();
|
|
5213
5407
|
const parts = turnUsageParts(usage, t);
|
|
5214
5408
|
if (parts.length === 0) return null;
|
|
5215
5409
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -5251,10 +5445,7 @@ window.__ModuleLoader__.load({
|
|
|
5251
5445
|
}, `text:${item.ordinal}`) : item.kind === "compaction" ? /* @__PURE__ */ jsx(ClaudeCompactionDivider, {
|
|
5252
5446
|
compaction: item.compaction,
|
|
5253
5447
|
t
|
|
5254
|
-
}, `compaction:${item.ordinal}`) : item.kind === "
|
|
5255
|
-
usage: item.usage,
|
|
5256
|
-
t
|
|
5257
|
-
}, `usage:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
|
|
5448
|
+
}, `compaction:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
|
|
5258
5449
|
tools: item.tools,
|
|
5259
5450
|
...item.additions === void 0 ? {} : { additions: item.additions },
|
|
5260
5451
|
...item.deletions === void 0 ? {} : { deletions: item.deletions },
|
|
@@ -5549,6 +5740,7 @@ window.__ModuleLoader__.load({
|
|
|
5549
5740
|
//#endregion
|
|
5550
5741
|
//#region src/client/ClaudeActivityTail.tsx
|
|
5551
5742
|
const MAX_HOVER_TASKS = 6;
|
|
5743
|
+
const EMPTY_TASKS = [];
|
|
5552
5744
|
function taskGlyph(status) {
|
|
5553
5745
|
if (status === "failed") return {
|
|
5554
5746
|
glyph: "×",
|
|
@@ -5671,24 +5863,36 @@ window.__ModuleLoader__.load({
|
|
|
5671
5863
|
})
|
|
5672
5864
|
});
|
|
5673
5865
|
}
|
|
5866
|
+
/** Everything that closes a turn, in the order it reads: what the turn is
|
|
5867
|
+
* still doing, then what it cost. */
|
|
5868
|
+
function ClaudeTurnFooter({ turn, useClaudeProjection, t, openTasks }) {
|
|
5869
|
+
const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
|
|
5870
|
+
const usage = useClaudeProjection((value) => latestTurnUsage(value.activities, turn));
|
|
5871
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ClaudeTaskLauncher, {
|
|
5872
|
+
turn,
|
|
5873
|
+
tasks,
|
|
5874
|
+
t,
|
|
5875
|
+
openTasks
|
|
5876
|
+
}), usage === void 0 ? null : /* @__PURE__ */ jsx(ClaudeTurnUsage, {
|
|
5877
|
+
usage,
|
|
5878
|
+
t
|
|
5879
|
+
})] });
|
|
5880
|
+
}
|
|
5674
5881
|
function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
|
|
5675
|
-
|
|
5676
|
-
return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
|
|
5882
|
+
return /* @__PURE__ */ jsx(ClaudeTurnFooter, {
|
|
5677
5883
|
turn: matched.turn,
|
|
5678
|
-
|
|
5884
|
+
useClaudeProjection,
|
|
5679
5885
|
t,
|
|
5680
5886
|
openTasks
|
|
5681
5887
|
});
|
|
5682
5888
|
}
|
|
5683
5889
|
//#endregion
|
|
5684
5890
|
//#region src/client/ClaudeActiveTasksNode.tsx
|
|
5685
|
-
|
|
5686
|
-
/** Render the task launcher while the owning DSH turn is still open. */
|
|
5891
|
+
/** Render the turn footer while the owning DSH turn is still open. */
|
|
5687
5892
|
function ClaudeActiveTasksNode({ node, useClaudeProjection, t, openTasks }) {
|
|
5688
|
-
|
|
5689
|
-
return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
|
|
5893
|
+
return /* @__PURE__ */ jsx(ClaudeTurnFooter, {
|
|
5690
5894
|
turn: node.data.turn,
|
|
5691
|
-
|
|
5895
|
+
useClaudeProjection,
|
|
5692
5896
|
t,
|
|
5693
5897
|
openTasks
|
|
5694
5898
|
});
|
|
@@ -5806,6 +6010,22 @@ window.__ModuleLoader__.load({
|
|
|
5806
6010
|
}
|
|
5807
6011
|
return checks;
|
|
5808
6012
|
}
|
|
6013
|
+
/** Review bots sign a comment with an attribution line and an actions checklist
|
|
6014
|
+
* aimed at the bot itself; in a prompt both are noise, and "apply fix" reads as
|
|
6015
|
+
* an instruction Claude cannot follow. */
|
|
6016
|
+
function commentText(body) {
|
|
6017
|
+
const lines = body.replaceAll(/<!--[\s\S]*?-->/g, "").split("\n").filter((line) => !/^\s*<sup>[\s\S]*<\/sup>\s*$/.test(line));
|
|
6018
|
+
let end = lines.length;
|
|
6019
|
+
while (end > 0 && /^\s*(?:-{3,}|\*\*[^*]+\*\*|[-*] \[[ xX]\].*)?\s*$/.test(lines[end - 1] ?? "")) end -= 1;
|
|
6020
|
+
return (lines.slice(end).some((line) => /^\s*[-*] \[[ xX]\]/.test(line)) ? lines.slice(0, end) : lines).join("\n").trim();
|
|
6021
|
+
}
|
|
6022
|
+
/** A one-line comment sits after the author; anything longer starts on its own
|
|
6023
|
+
* line, so its headings and lists keep meaning instead of running into ours. */
|
|
6024
|
+
function attributed(prefix, body) {
|
|
6025
|
+
const text = commentText(body);
|
|
6026
|
+
const indented = text.split("\n").map((line) => line.length === 0 ? line : ` ${line}`).join("\n");
|
|
6027
|
+
return text.includes("\n") ? `${prefix}\n\n${indented}` : `${prefix} ${text}`;
|
|
6028
|
+
}
|
|
5809
6029
|
/** Draft handed to Claude when the user forwards GitHub review comments. A
|
|
5810
6030
|
* resolved thread is a settled conversation: forwarding it would ask Claude to
|
|
5811
6031
|
* redo work the reviewers already signed off. */
|
|
@@ -5815,8 +6035,8 @@ window.__ModuleLoader__.load({
|
|
|
5815
6035
|
return `Please address the following GitHub pull request review comments. Make the requested changes, or explain briefly when a comment should not be applied.\n\n${open.map((thread) => {
|
|
5816
6036
|
const [first, ...rest] = thread.comments;
|
|
5817
6037
|
if (first === void 0) return "";
|
|
5818
|
-
return [`- ${`${thread.path}${thread.line === void 0 ? "" : `:${thread.line}`}`} (@${first.author})
|
|
5819
|
-
}).filter((block) => block.length > 0).join("\n")}`;
|
|
6038
|
+
return [attributed(`- ${`${thread.path}${thread.line === void 0 ? "" : `:${thread.line}`}`} (@${first.author}):`, first.body), ...rest.map((reply) => attributed(` (@${reply.author}):`, reply.body))].join("\n");
|
|
6039
|
+
}).filter((block) => block.length > 0).join("\n\n")}`;
|
|
5820
6040
|
}
|
|
5821
6041
|
/** Draft handed to Claude when the user forwards failing CI checks. */
|
|
5822
6042
|
function composeChecksPrompt(checks) {
|
|
@@ -5824,11 +6044,13 @@ window.__ModuleLoader__.load({
|
|
|
5824
6044
|
return `${`## ${check.name}${check.link === void 0 ? "" : ` (${check.link})`}`}${check.description === void 0 ? "" : `\n${check.description}`}${check.log === void 0 ? "" : `\n\n\`\`\`\n${check.log}\n\`\`\``}`;
|
|
5825
6045
|
}).join("\n\n")}`;
|
|
5826
6046
|
}
|
|
5827
|
-
/** Draft handed to Claude
|
|
5828
|
-
|
|
6047
|
+
/** Draft handed to Claude for a stopped merge, rebase, cherry-pick or revert --
|
|
6048
|
+
* from the update-branch dialog that caused one, or from the repository bar
|
|
6049
|
+
* for one already in the tree, where the base branch is not always known. */
|
|
6050
|
+
function composeConflictsPrompt(conflicts, operation = "merge", baseBranch) {
|
|
5829
6051
|
const list = conflicts.map((file) => `- ${file}`).join("\n");
|
|
5830
|
-
|
|
5831
|
-
return `Merging
|
|
6052
|
+
const base = baseBranch === void 0 ? void 0 : `origin/${baseBranch}`;
|
|
6053
|
+
return `${operation === "rebase" ? `Rebasing the current branch${base === void 0 ? "" : ` onto ${base}`} stopped on conflicts in the files below.` : operation === "merge" ? `Merging ${base ?? "the base branch"} into the current branch left conflicts in the files below.` : `A ${operation} stopped on conflicts in the files below.`} Resolve each conflict preserving the intent of both sides, stage the resolved files, then run \`git ${operation} --continue\` until the ${operation} finishes.${operation === "rebase" && baseBranch !== void 0 ? " Once it finishes, push with `git push --force-with-lease`." : ""}\n\n${list}`;
|
|
5832
6054
|
}
|
|
5833
6055
|
//#endregion
|
|
5834
6056
|
//#region src/client/auto-fix.ts
|
|
@@ -5883,6 +6105,15 @@ window.__ModuleLoader__.load({
|
|
|
5883
6105
|
};
|
|
5884
6106
|
}
|
|
5885
6107
|
//#endregion
|
|
6108
|
+
//#region src/client/branch-label.ts
|
|
6109
|
+
/** `Detached HEAD` names a git implementation detail, not the user's checkout:
|
|
6110
|
+
* a stopped rebase still knows which branch it parked, and that name is what
|
|
6111
|
+
* every panel should show. Only a genuinely nameless HEAD falls back. */
|
|
6112
|
+
function branchLabel(repository, t) {
|
|
6113
|
+
if (repository.branch !== void 0) return repository.branch;
|
|
6114
|
+
return repository.detached === true ? t("repositoryDetached") : t("repositoryUnknownBranch");
|
|
6115
|
+
}
|
|
6116
|
+
//#endregion
|
|
5886
6117
|
//#region src/client/session-preset.ts
|
|
5887
6118
|
/**
|
|
5888
6119
|
* Resolve one row's preset id, newest seat first.
|
|
@@ -6020,7 +6251,7 @@ window.__ModuleLoader__.load({
|
|
|
6020
6251
|
}) : rows.map((row) => {
|
|
6021
6252
|
const repository = row.cwd === void 0 ? void 0 : statuses[row.cwd];
|
|
6022
6253
|
const pullRequest = repository?.pullRequest;
|
|
6023
|
-
const branch = repository?.status === "ready" ?
|
|
6254
|
+
const branch = repository?.status === "ready" ? branchLabel(repository, t) : repository === void 0 ? t("overviewLoading") : t("repositoryUnavailable");
|
|
6024
6255
|
return /* @__PURE__ */ jsxs("button", {
|
|
6025
6256
|
type: "button",
|
|
6026
6257
|
style: overviewRow,
|
|
@@ -8421,6 +8652,169 @@ window.__ModuleLoader__.load({
|
|
|
8421
8652
|
}) : null]
|
|
8422
8653
|
});
|
|
8423
8654
|
}
|
|
8655
|
+
/** The way out of a stopped merge or rebase. A rebase detaches HEAD, which
|
|
8656
|
+
* hides every other control on this bar, and the update-branch dialog that
|
|
8657
|
+
* started it takes its conflict list along when it closes -- so this one is
|
|
8658
|
+
* mounted from repository state instead, and survives being dismissed. */
|
|
8659
|
+
function ConflictControl({ sessionId, repository, t, report, submitPrompt }) {
|
|
8660
|
+
const [dialog, setDialog] = useState();
|
|
8661
|
+
const operation = repository.operation;
|
|
8662
|
+
if (operation === void 0) return null;
|
|
8663
|
+
const conflicts = repository.conflicts ?? [];
|
|
8664
|
+
const operationName = t(`conflictOperation_${operation}`);
|
|
8665
|
+
const closeDialog = () => {
|
|
8666
|
+
if (dialog?.submitting !== true) setDialog(void 0);
|
|
8667
|
+
};
|
|
8668
|
+
const run = (action) => {
|
|
8669
|
+
if (dialog === void 0 || dialog.submitting) return;
|
|
8670
|
+
const { error: _error, ...pending } = dialog;
|
|
8671
|
+
setDialog({
|
|
8672
|
+
...pending,
|
|
8673
|
+
submitting: true
|
|
8674
|
+
});
|
|
8675
|
+
executeRepositoryAction(sessionId, {
|
|
8676
|
+
action,
|
|
8677
|
+
fingerprint: "",
|
|
8678
|
+
message: "",
|
|
8679
|
+
includeUnstaged: false,
|
|
8680
|
+
push: dialog.push
|
|
8681
|
+
}).then((result) => {
|
|
8682
|
+
if (result.conflicts !== void 0 && result.conflicts.length > 0) {
|
|
8683
|
+
setDialog({
|
|
8684
|
+
...pending,
|
|
8685
|
+
submitting: false,
|
|
8686
|
+
confirmAbort: false
|
|
8687
|
+
});
|
|
8688
|
+
return;
|
|
8689
|
+
}
|
|
8690
|
+
report(action === "resolve-abort" ? t("conflictAborted", { operation: operationName }) : t(result.pushed ? "conflictPushed" : "conflictContinued", { operation: operationName }));
|
|
8691
|
+
setDialog(void 0);
|
|
8692
|
+
}, (reason) => {
|
|
8693
|
+
setDialog({
|
|
8694
|
+
...pending,
|
|
8695
|
+
submitting: false,
|
|
8696
|
+
error: reason instanceof Error ? reason.message : t("diffActionFailed")
|
|
8697
|
+
});
|
|
8698
|
+
});
|
|
8699
|
+
};
|
|
8700
|
+
const openDialog = () => {
|
|
8701
|
+
setDialog({
|
|
8702
|
+
submitting: false,
|
|
8703
|
+
confirmAbort: false,
|
|
8704
|
+
push: repository.remote !== void 0 && repository.pullRequest?.state === "open"
|
|
8705
|
+
});
|
|
8706
|
+
};
|
|
8707
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
8708
|
+
/* @__PURE__ */ jsx("button", {
|
|
8709
|
+
type: "button",
|
|
8710
|
+
style: repositoryConflictTrigger,
|
|
8711
|
+
title: t("conflictDescription", { operation: operationName }),
|
|
8712
|
+
onClick: openDialog,
|
|
8713
|
+
children: conflicts.length > 0 ? t("conflictBadge", {
|
|
8714
|
+
operation: operationName,
|
|
8715
|
+
count: conflicts.length
|
|
8716
|
+
}) : t("conflictBadgeReady", { operation: operationName })
|
|
8717
|
+
}),
|
|
8718
|
+
dialog === void 0 ? null : /* @__PURE__ */ jsx("style", {
|
|
8719
|
+
"data-dsh-claude-repository-modal-styles": true,
|
|
8720
|
+
children: diffModalCss
|
|
8721
|
+
}),
|
|
8722
|
+
/* @__PURE__ */ jsx(Modal, {
|
|
8723
|
+
className: "dshClaudeRepositoryActionModal",
|
|
8724
|
+
contentClassName: "dshClaudeRepositoryActionModalContent",
|
|
8725
|
+
open: dialog !== void 0,
|
|
8726
|
+
onClose: closeDialog,
|
|
8727
|
+
title: t("conflictTitle"),
|
|
8728
|
+
closeLabel: t("diffCancel"),
|
|
8729
|
+
description: t("conflictDescription", { operation: operationName }),
|
|
8730
|
+
footer: /* @__PURE__ */ jsxs("div", {
|
|
8731
|
+
style: diffModalFooter,
|
|
8732
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
8733
|
+
type: "button",
|
|
8734
|
+
style: {
|
|
8735
|
+
...button,
|
|
8736
|
+
...diffModalButton
|
|
8737
|
+
},
|
|
8738
|
+
disabled: dialog?.submitting === true,
|
|
8739
|
+
onClick: () => {
|
|
8740
|
+
if (dialog?.confirmAbort === true) run("resolve-abort");
|
|
8741
|
+
else setDialog((current) => current === void 0 ? current : {
|
|
8742
|
+
...current,
|
|
8743
|
+
confirmAbort: true
|
|
8744
|
+
});
|
|
8745
|
+
},
|
|
8746
|
+
children: t("conflictAbort", { operation: operationName })
|
|
8747
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
8748
|
+
type: "button",
|
|
8749
|
+
style: {
|
|
8750
|
+
...primaryButton,
|
|
8751
|
+
...diffModalButton
|
|
8752
|
+
},
|
|
8753
|
+
disabled: dialog?.submitting === true || conflicts.length > 0,
|
|
8754
|
+
onClick: () => run("resolve-continue"),
|
|
8755
|
+
children: dialog?.submitting === true ? t("diffSubmitting") : t("conflictContinue", { operation: operationName })
|
|
8756
|
+
})]
|
|
8757
|
+
}),
|
|
8758
|
+
children: dialog === void 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
8759
|
+
style: diffModalBody,
|
|
8760
|
+
children: [
|
|
8761
|
+
/* @__PURE__ */ jsxs("div", {
|
|
8762
|
+
style: diffModalMeta,
|
|
8763
|
+
children: [/* @__PURE__ */ jsxs("strong", {
|
|
8764
|
+
style: diffModalMetaText,
|
|
8765
|
+
children: [
|
|
8766
|
+
operationName,
|
|
8767
|
+
" · ",
|
|
8768
|
+
branchLabel(repository, t)
|
|
8769
|
+
]
|
|
8770
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
8771
|
+
style: diffModalFileState,
|
|
8772
|
+
children: conflicts.length > 0 ? t("conflictFiles") : t("conflictReady")
|
|
8773
|
+
})]
|
|
8774
|
+
}),
|
|
8775
|
+
conflicts.length === 0 ? null : /* @__PURE__ */ jsx("ul", {
|
|
8776
|
+
style: diffModalConflicts,
|
|
8777
|
+
children: conflicts.map((file) => /* @__PURE__ */ jsx("li", { children: file }, file))
|
|
8778
|
+
}),
|
|
8779
|
+
conflicts.length === 0 || submitPrompt === void 0 ? null : /* @__PURE__ */ jsx("button", {
|
|
8780
|
+
type: "button",
|
|
8781
|
+
style: diffModalConflictResolve,
|
|
8782
|
+
onClick: () => {
|
|
8783
|
+
submitPrompt(composeConflictsPrompt(conflicts, operation, repository.pullRequest?.baseBranch));
|
|
8784
|
+
closeDialog();
|
|
8785
|
+
},
|
|
8786
|
+
children: t("conflictResolve")
|
|
8787
|
+
}),
|
|
8788
|
+
repository.remote === void 0 ? null : /* @__PURE__ */ jsxs("label", {
|
|
8789
|
+
style: diffModalCheckbox,
|
|
8790
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
8791
|
+
type: "checkbox",
|
|
8792
|
+
checked: dialog.push,
|
|
8793
|
+
disabled: dialog.submitting,
|
|
8794
|
+
onChange: (event) => {
|
|
8795
|
+
const { checked } = event.currentTarget;
|
|
8796
|
+
setDialog((current) => current === void 0 ? current : {
|
|
8797
|
+
...current,
|
|
8798
|
+
push: checked
|
|
8799
|
+
});
|
|
8800
|
+
}
|
|
8801
|
+
}), t("conflictPush")]
|
|
8802
|
+
}),
|
|
8803
|
+
!dialog.confirmAbort ? null : /* @__PURE__ */ jsx("p", {
|
|
8804
|
+
role: "alert",
|
|
8805
|
+
style: diffModalStatus,
|
|
8806
|
+
children: t("conflictAbortConfirm", { operation: operationName })
|
|
8807
|
+
}),
|
|
8808
|
+
dialog.error === void 0 ? null : /* @__PURE__ */ jsx("p", {
|
|
8809
|
+
role: "alert",
|
|
8810
|
+
style: diffModalError,
|
|
8811
|
+
children: dialog.error
|
|
8812
|
+
})
|
|
8813
|
+
]
|
|
8814
|
+
})
|
|
8815
|
+
})
|
|
8816
|
+
] });
|
|
8817
|
+
}
|
|
8424
8818
|
/** The trigger only shows on a clean branch that is behind its base -- but a
|
|
8425
8819
|
* conflicted rebase leaves the tree dirty on a detached HEAD, so an open
|
|
8426
8820
|
* dialog (and its resolve button) has to outlive that. */
|
|
@@ -8592,7 +8986,7 @@ window.__ModuleLoader__.load({
|
|
|
8592
8986
|
type: "button",
|
|
8593
8987
|
style: diffModalConflictResolve,
|
|
8594
8988
|
onClick: () => {
|
|
8595
|
-
submitPrompt(composeConflictsPrompt(
|
|
8989
|
+
submitPrompt(composeConflictsPrompt(dialog.conflicts ?? [], method, base));
|
|
8596
8990
|
closeDialog();
|
|
8597
8991
|
},
|
|
8598
8992
|
children: t("diffUpdateBranchResolve")
|
|
@@ -8863,7 +9257,7 @@ window.__ModuleLoader__.load({
|
|
|
8863
9257
|
const repository = projection.repository;
|
|
8864
9258
|
const { toast, report } = useActionToast();
|
|
8865
9259
|
if (blank || !projection.owned || repository === void 0) return null;
|
|
8866
|
-
const branch =
|
|
9260
|
+
const branch = branchLabel(repository, t);
|
|
8867
9261
|
const pullRequest = repository.pullRequest;
|
|
8868
9262
|
const merged = pullRequest?.state === "merged";
|
|
8869
9263
|
const mergedAge = merged ? relativeAge(pullRequest.mergedAt) : void 0;
|
|
@@ -8935,79 +9329,89 @@ window.__ModuleLoader__.load({
|
|
|
8935
9329
|
}) : null,
|
|
8936
9330
|
/* @__PURE__ */ jsxs("span", {
|
|
8937
9331
|
style: repositoryStatusItems,
|
|
8938
|
-
children: [
|
|
8939
|
-
|
|
8940
|
-
style: {
|
|
8941
|
-
...diffTrigger,
|
|
8942
|
-
...merged ? diffTriggerMuted : {}
|
|
8943
|
-
},
|
|
8944
|
-
onClick: openDiff,
|
|
8945
|
-
"aria-label": t("diffOpen"),
|
|
8946
|
-
children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
8947
|
-
style: merged ? diffAddMuted : diffAdd,
|
|
8948
|
-
children: ["+", repository.diff.additions]
|
|
8949
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
8950
|
-
style: merged ? diffDeleteMuted : diffDelete,
|
|
8951
|
-
children: ["−", repository.diff.deletions]
|
|
8952
|
-
})] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
|
|
8953
|
-
style: merged ? diffAheadMuted : diffAhead,
|
|
8954
|
-
children: ["↑", aheadCount > 0 ? aheadCount : ""]
|
|
8955
|
-
}) : null]
|
|
8956
|
-
}) : null, pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
8957
|
-
style: repositoryMergedStatus,
|
|
8958
|
-
children: [
|
|
8959
|
-
/* @__PURE__ */ jsx("span", {
|
|
8960
|
-
style: repositoryMergedDot,
|
|
8961
|
-
"aria-hidden": "true"
|
|
8962
|
-
}),
|
|
8963
|
-
t("repositoryState_merged"),
|
|
8964
|
-
mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
|
|
8965
|
-
style: repositoryMergedAge,
|
|
8966
|
-
children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
|
|
8967
|
-
})
|
|
8968
|
-
]
|
|
8969
|
-
}), /* @__PURE__ */ jsx(CleanupControl, {
|
|
8970
|
-
repository,
|
|
8971
|
-
t,
|
|
8972
|
-
report,
|
|
8973
|
-
...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
|
|
8974
|
-
})] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
8975
|
-
pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
|
|
8976
|
-
sessionId,
|
|
8977
|
-
pullNumber: pullRequest.number,
|
|
8978
|
-
t,
|
|
8979
|
-
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
8980
|
-
}) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
|
|
8981
|
-
label: t(`repositoryChecks_${pullRequest.checks}`),
|
|
8982
|
-
tone: pullRequest.checks === "passing" ? "success" : "warning",
|
|
8983
|
-
children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
|
|
8984
|
-
}),
|
|
8985
|
-
pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
|
|
8986
|
-
label: t(`repositoryReview_${pullRequest.review}`),
|
|
8987
|
-
tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
|
|
8988
|
-
children: /* @__PURE__ */ jsx(ReviewGlyph, {})
|
|
8989
|
-
}),
|
|
8990
|
-
/* @__PURE__ */ jsx(AutoFixControl, {
|
|
8991
|
-
sessionId,
|
|
8992
|
-
repository,
|
|
8993
|
-
running,
|
|
8994
|
-
t,
|
|
8995
|
-
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
8996
|
-
}),
|
|
8997
|
-
/* @__PURE__ */ jsx(UpdateBranchControl, {
|
|
9332
|
+
children: [
|
|
9333
|
+
/* @__PURE__ */ jsx(ConflictControl, {
|
|
8998
9334
|
sessionId,
|
|
8999
9335
|
repository,
|
|
9000
9336
|
t,
|
|
9001
9337
|
report,
|
|
9002
9338
|
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
9003
9339
|
}),
|
|
9004
|
-
/* @__PURE__ */
|
|
9005
|
-
|
|
9340
|
+
hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
|
|
9341
|
+
type: "button",
|
|
9342
|
+
style: {
|
|
9343
|
+
...diffTrigger,
|
|
9344
|
+
...merged ? diffTriggerMuted : {}
|
|
9345
|
+
},
|
|
9346
|
+
onClick: openDiff,
|
|
9347
|
+
"aria-label": t("diffOpen"),
|
|
9348
|
+
children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
9349
|
+
style: merged ? diffAddMuted : diffAdd,
|
|
9350
|
+
children: ["+", repository.diff.additions]
|
|
9351
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
9352
|
+
style: merged ? diffDeleteMuted : diffDelete,
|
|
9353
|
+
children: ["−", repository.diff.deletions]
|
|
9354
|
+
})] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
|
|
9355
|
+
style: merged ? diffAheadMuted : diffAhead,
|
|
9356
|
+
children: ["↑", aheadCount > 0 ? aheadCount : ""]
|
|
9357
|
+
}) : null]
|
|
9358
|
+
}) : null,
|
|
9359
|
+
pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
9360
|
+
style: repositoryMergedStatus,
|
|
9361
|
+
children: [
|
|
9362
|
+
/* @__PURE__ */ jsx("span", {
|
|
9363
|
+
style: repositoryMergedDot,
|
|
9364
|
+
"aria-hidden": "true"
|
|
9365
|
+
}),
|
|
9366
|
+
t("repositoryState_merged"),
|
|
9367
|
+
mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
|
|
9368
|
+
style: repositoryMergedAge,
|
|
9369
|
+
children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
|
|
9370
|
+
})
|
|
9371
|
+
]
|
|
9372
|
+
}), /* @__PURE__ */ jsx(CleanupControl, {
|
|
9006
9373
|
repository,
|
|
9007
9374
|
t,
|
|
9008
|
-
report
|
|
9009
|
-
|
|
9010
|
-
|
|
9375
|
+
report,
|
|
9376
|
+
...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
|
|
9377
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
9378
|
+
pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
|
|
9379
|
+
sessionId,
|
|
9380
|
+
pullNumber: pullRequest.number,
|
|
9381
|
+
t,
|
|
9382
|
+
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
9383
|
+
}) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
|
|
9384
|
+
label: t(`repositoryChecks_${pullRequest.checks}`),
|
|
9385
|
+
tone: pullRequest.checks === "passing" ? "success" : "warning",
|
|
9386
|
+
children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
|
|
9387
|
+
}),
|
|
9388
|
+
pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
|
|
9389
|
+
label: t(`repositoryReview_${pullRequest.review}`),
|
|
9390
|
+
tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
|
|
9391
|
+
children: /* @__PURE__ */ jsx(ReviewGlyph, {})
|
|
9392
|
+
}),
|
|
9393
|
+
/* @__PURE__ */ jsx(AutoFixControl, {
|
|
9394
|
+
sessionId,
|
|
9395
|
+
repository,
|
|
9396
|
+
running,
|
|
9397
|
+
t,
|
|
9398
|
+
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
9399
|
+
}),
|
|
9400
|
+
/* @__PURE__ */ jsx(UpdateBranchControl, {
|
|
9401
|
+
sessionId,
|
|
9402
|
+
repository,
|
|
9403
|
+
t,
|
|
9404
|
+
report,
|
|
9405
|
+
...submitPrompt === void 0 ? {} : { submitPrompt }
|
|
9406
|
+
}),
|
|
9407
|
+
/* @__PURE__ */ jsx(MergePullRequestControl, {
|
|
9408
|
+
sessionId,
|
|
9409
|
+
repository,
|
|
9410
|
+
t,
|
|
9411
|
+
report
|
|
9412
|
+
})
|
|
9413
|
+
] })
|
|
9414
|
+
]
|
|
9011
9415
|
})
|
|
9012
9416
|
]
|
|
9013
9417
|
})]
|
|
@@ -14216,7 +14620,7 @@ window.__ModuleLoader__.load({
|
|
|
14216
14620
|
return (action === "commit" ? t("diffCommit") : action === "commit-push" ? t("diffCommitPush") : action === "push" ? t("diffPush") : action === "merge-pr" ? t("diffMergePr") : action === "update-branch" ? t("diffUpdateBranch") : t("diffCreatePr")).replace(/[….]+$/u, "");
|
|
14217
14621
|
}
|
|
14218
14622
|
function repositoryActionAvailability(repository) {
|
|
14219
|
-
const ready = repository?.status === "ready" && repository.detached !== true;
|
|
14623
|
+
const ready = repository?.status === "ready" && repository.detached !== true && (repository.conflicts ?? []).length === 0;
|
|
14220
14624
|
const committable = ready && repository.dirty === true;
|
|
14221
14625
|
const hasRemote = repository?.remote !== void 0;
|
|
14222
14626
|
const hasOpenPullRequest = repository?.pullRequest?.state === "open";
|
|
@@ -14536,7 +14940,7 @@ window.__ModuleLoader__.load({
|
|
|
14536
14940
|
repository?.status
|
|
14537
14941
|
]);
|
|
14538
14942
|
if (!projection.owned || repository?.status !== "ready" || diff === void 0) return null;
|
|
14539
|
-
const branch =
|
|
14943
|
+
const branch = branchLabel(repository, t);
|
|
14540
14944
|
const availability = repositoryActionAvailability(repository);
|
|
14541
14945
|
const anyActionAvailable = availability["commit"] || availability["commit-push"] || availability["push"] || availability["create-pr"];
|
|
14542
14946
|
const menuItems = [
|
|
@@ -17642,6 +18046,428 @@ window.__ModuleLoader__.load({
|
|
|
17642
18046
|
}
|
|
17643
18047
|
}
|
|
17644
18048
|
//#endregion
|
|
18049
|
+
//#region src/client/prompt-api.ts
|
|
18050
|
+
/** Prompt files live in one global directory, so one module-level cache serves
|
|
18051
|
+
* every session. The TTL is what lets a file added outside DSH appear without
|
|
18052
|
+
* a reload, while a burst of keystrokes through the menu still costs one
|
|
18053
|
+
* directory scan. */
|
|
18054
|
+
const TTL_MS = 5e3;
|
|
18055
|
+
let cached;
|
|
18056
|
+
function invalidateClaudePrompts() {
|
|
18057
|
+
cached = void 0;
|
|
18058
|
+
}
|
|
18059
|
+
/** The user's prompt snippets, at most one read per TTL window. */
|
|
18060
|
+
async function claudePrompts() {
|
|
18061
|
+
const at = Date.now();
|
|
18062
|
+
if (cached !== void 0 && at - cached.at < TTL_MS) return await cached.prompts;
|
|
18063
|
+
const prompts = pluginRead(CLAUDE_PROMPTS_PATH, "fast").then((payload) => payload.prompts).catch(() => {
|
|
18064
|
+
invalidateClaudePrompts();
|
|
18065
|
+
return [];
|
|
18066
|
+
});
|
|
18067
|
+
cached = {
|
|
18068
|
+
at,
|
|
18069
|
+
prompts
|
|
18070
|
+
};
|
|
18071
|
+
return await prompts;
|
|
18072
|
+
}
|
|
18073
|
+
/** Save one snippet, answering where it landed. Rejects with a
|
|
18074
|
+
* `PluginRequestError` whose `code` is `name-taken` when the file already
|
|
18075
|
+
* exists; nothing is overwritten. */
|
|
18076
|
+
async function saveClaudePrompt(name, body) {
|
|
18077
|
+
const saved = await pluginWrite(CLAUDE_PROMPTS_PATH, "fast", void 0, { json: {
|
|
18078
|
+
name,
|
|
18079
|
+
body
|
|
18080
|
+
} });
|
|
18081
|
+
invalidateClaudePrompts();
|
|
18082
|
+
return saved.prompt;
|
|
18083
|
+
}
|
|
18084
|
+
/** Ask the host to name a draft with Claude's cheapest model. Answers
|
|
18085
|
+
* undefined whenever no name could be had — the caller already holds the
|
|
18086
|
+
* locally derived one, so a failure here is never worth reporting. */
|
|
18087
|
+
async function suggestClaudePromptName(draft, cancel) {
|
|
18088
|
+
try {
|
|
18089
|
+
return (await pluginWrite(CLAUDE_PROMPT_NAME_PATH, "git", cancel, { json: { draft } })).name;
|
|
18090
|
+
} catch {
|
|
18091
|
+
return;
|
|
18092
|
+
}
|
|
18093
|
+
}
|
|
18094
|
+
/** Rewrite a draft into something an agent can act on. Unlike the name
|
|
18095
|
+
* suggestion this one throws: the user asked for it and is waiting, so a
|
|
18096
|
+
* failure is theirs to see rather than ours to swallow. */
|
|
18097
|
+
async function refineClaudePrompt(draft, cancel) {
|
|
18098
|
+
return (await pluginWrite(CLAUDE_PROMPT_REFINE_PATH, "git", cancel, { json: { draft } })).text;
|
|
18099
|
+
}
|
|
18100
|
+
/** Characters the host's `PROMPT_NAME` guard rejects; scrubbed rather than
|
|
18101
|
+
* re-implemented here, so a drift in the guard cannot let a bad name through
|
|
18102
|
+
* (the host still validates, and answers `invalid-name`). */
|
|
18103
|
+
const FOREIGN_NAME_CHARS = /[^\p{L}\p{M}\p{N} ._()\[\]-]/gu;
|
|
18104
|
+
const MAX_NAME_CHARS = 40;
|
|
18105
|
+
function stamp(now) {
|
|
18106
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
18107
|
+
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
18108
|
+
}
|
|
18109
|
+
/** The file name to offer for a draft: its opening line, scrubbed to what a
|
|
18110
|
+
* file name may hold. A draft that opens with punctuation or an emoji leaves
|
|
18111
|
+
* nothing usable, so those fall back to a timestamp the user can rename. */
|
|
18112
|
+
function defaultPromptName(draft, now = /* @__PURE__ */ new Date()) {
|
|
18113
|
+
const collapsed = (draft.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "").replace(FOREIGN_NAME_CHARS, " ").replace(/\s+/gu, " ").trim();
|
|
18114
|
+
const cut = collapsed.slice(0, MAX_NAME_CHARS);
|
|
18115
|
+
const boundary = collapsed.length > MAX_NAME_CHARS ? cut.search(/[\s\-_][^\s\-_]*$/u) : -1;
|
|
18116
|
+
const scrubbed = (boundary > 0 ? cut.slice(0, boundary) : cut).trim();
|
|
18117
|
+
return /^[\p{L}\p{N}]/u.test(scrubbed) ? scrubbed : `prompt-${stamp(now)}`;
|
|
18118
|
+
}
|
|
18119
|
+
//#endregion
|
|
18120
|
+
//#region src/client/ClaudePromptSaveAction.tsx
|
|
18121
|
+
const CARD_GAP = 8;
|
|
18122
|
+
const CARD_MARGIN = 12;
|
|
18123
|
+
/**
|
|
18124
|
+
* Place the card above its trigger, clamped into the viewport, and dismiss it
|
|
18125
|
+
* on a pointer that lands outside both.
|
|
18126
|
+
*
|
|
18127
|
+
* The primitives ship `useAnchoredPosition` and `useDismissOnOutsidePointer`
|
|
18128
|
+
* for exactly this, and both are used here through their published types —
|
|
18129
|
+
* but the Host's build of that package predates the `side` option of one and
|
|
18130
|
+
* the portal argument of the other. Neither omission is visible to `tsc`,
|
|
18131
|
+
* which reads this checkout's newer copy, and both are silent at runtime: the
|
|
18132
|
+
* card hangs below a composer pinned to the bottom of the window, and every
|
|
18133
|
+
* pointer landing in the naming field counts as outside and closes it. Owning
|
|
18134
|
+
* twenty lines beats behaviour that depends on which Desktop the plugin was
|
|
18135
|
+
* installed into.
|
|
18136
|
+
*/
|
|
18137
|
+
function useAnchoredCard(open, anchor, card, onDismiss) {
|
|
18138
|
+
const [position, setPosition] = useState();
|
|
18139
|
+
useLayoutEffect(() => {
|
|
18140
|
+
if (!open) {
|
|
18141
|
+
setPosition(void 0);
|
|
18142
|
+
return;
|
|
18143
|
+
}
|
|
18144
|
+
const place = () => {
|
|
18145
|
+
const trigger = anchor.current?.getBoundingClientRect();
|
|
18146
|
+
const panel = card.current?.getBoundingClientRect();
|
|
18147
|
+
if (trigger === void 0 || panel === void 0) return;
|
|
18148
|
+
const above = trigger.top - CARD_GAP - panel.height;
|
|
18149
|
+
setPosition({
|
|
18150
|
+
left: Math.max(CARD_MARGIN, Math.min(trigger.left, window.innerWidth - panel.width - CARD_MARGIN)),
|
|
18151
|
+
top: above >= CARD_MARGIN ? above : Math.min(trigger.bottom + CARD_GAP, window.innerHeight - panel.height - CARD_MARGIN)
|
|
18152
|
+
});
|
|
18153
|
+
};
|
|
18154
|
+
place();
|
|
18155
|
+
window.addEventListener("resize", place);
|
|
18156
|
+
window.addEventListener("scroll", place, true);
|
|
18157
|
+
return () => {
|
|
18158
|
+
window.removeEventListener("resize", place);
|
|
18159
|
+
window.removeEventListener("scroll", place, true);
|
|
18160
|
+
};
|
|
18161
|
+
}, [
|
|
18162
|
+
open,
|
|
18163
|
+
anchor,
|
|
18164
|
+
card
|
|
18165
|
+
]);
|
|
18166
|
+
useEffect(() => {
|
|
18167
|
+
if (!open) return void 0;
|
|
18168
|
+
const dismiss = (event) => {
|
|
18169
|
+
const target = event.target;
|
|
18170
|
+
if (!(target instanceof Node)) return;
|
|
18171
|
+
if (anchor.current?.contains(target) === true || card.current?.contains(target) === true) return;
|
|
18172
|
+
onDismiss();
|
|
18173
|
+
};
|
|
18174
|
+
document.addEventListener("pointerdown", dismiss);
|
|
18175
|
+
return () => {
|
|
18176
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
18177
|
+
};
|
|
18178
|
+
}, [
|
|
18179
|
+
open,
|
|
18180
|
+
anchor,
|
|
18181
|
+
card,
|
|
18182
|
+
onDismiss
|
|
18183
|
+
]);
|
|
18184
|
+
return position;
|
|
18185
|
+
}
|
|
18186
|
+
/**
|
|
18187
|
+
* Keep the draft you just wrote, from the composer's own tool row.
|
|
18188
|
+
*
|
|
18189
|
+
* It sits beside the attach and access controls rather than in a row of its
|
|
18190
|
+
* own: a band above the composer moves the repository bar and the composer
|
|
18191
|
+
* itself every time a draft appears, and this is a once-in-a-while action that
|
|
18192
|
+
* has not earned that. The naming card is portaled and anchored, so opening it
|
|
18193
|
+
* displaces nothing either.
|
|
18194
|
+
*
|
|
18195
|
+
* The field opens on a name derived from the draft's first line, which costs
|
|
18196
|
+
* nothing and is there instantly, and a Claude-written name replaces it when
|
|
18197
|
+
* one arrives. That ordering is the whole naming design: the suggestion is an
|
|
18198
|
+
* improvement on a working answer, never something the user waits for.
|
|
18199
|
+
*/
|
|
18200
|
+
function ClaudePromptSaveAction({ t, useClaudeProjection, input, savePrompt = saveClaudePrompt, suggestName = suggestClaudePromptName }) {
|
|
18201
|
+
const owned = useClaudeProjection((projection) => projection.owned);
|
|
18202
|
+
const anchor = useRef(null);
|
|
18203
|
+
const panelRef = useRef(null);
|
|
18204
|
+
const suggestion = useRef(void 0);
|
|
18205
|
+
const [panel, setPanel] = useState();
|
|
18206
|
+
const [saving, setSaving] = useState(false);
|
|
18207
|
+
const close = useCallback(() => {
|
|
18208
|
+
suggestion.current?.abort();
|
|
18209
|
+
suggestion.current = void 0;
|
|
18210
|
+
setPanel(void 0);
|
|
18211
|
+
}, []);
|
|
18212
|
+
const position = useAnchoredCard(panel !== void 0, anchor, panelRef, close);
|
|
18213
|
+
useEffect(() => () => {
|
|
18214
|
+
suggestion.current?.abort();
|
|
18215
|
+
}, []);
|
|
18216
|
+
if (!owned) return null;
|
|
18217
|
+
const draft = input?.draft ?? "";
|
|
18218
|
+
const label = t("promptSave");
|
|
18219
|
+
const open = () => {
|
|
18220
|
+
setPanel({
|
|
18221
|
+
kind: "naming",
|
|
18222
|
+
name: defaultPromptName(draft),
|
|
18223
|
+
touched: false,
|
|
18224
|
+
suggesting: true
|
|
18225
|
+
});
|
|
18226
|
+
const attempt = new AbortController();
|
|
18227
|
+
suggestion.current = attempt;
|
|
18228
|
+
suggestName(draft, attempt.signal).then((suggested) => {
|
|
18229
|
+
if (attempt.signal.aborted) return;
|
|
18230
|
+
setPanel((current) => current?.kind !== "naming" ? current : {
|
|
18231
|
+
...current,
|
|
18232
|
+
suggesting: false,
|
|
18233
|
+
...suggested === void 0 || current.touched ? {} : { name: suggested }
|
|
18234
|
+
});
|
|
18235
|
+
});
|
|
18236
|
+
};
|
|
18237
|
+
const save = () => {
|
|
18238
|
+
const name = panel?.kind === "naming" ? panel.name.trim() : "";
|
|
18239
|
+
if (saving || name === "" || draft.trim() === "") return;
|
|
18240
|
+
setSaving(true);
|
|
18241
|
+
savePrompt(name, draft).then((prompt) => {
|
|
18242
|
+
setPanel({
|
|
18243
|
+
kind: "saved",
|
|
18244
|
+
prompt
|
|
18245
|
+
});
|
|
18246
|
+
}, (error) => {
|
|
18247
|
+
setPanel({
|
|
18248
|
+
kind: "naming",
|
|
18249
|
+
name,
|
|
18250
|
+
touched: true,
|
|
18251
|
+
suggesting: false,
|
|
18252
|
+
failure: error instanceof PluginRequestError && error.code === "name-taken" ? t("promptSaveExists") : t("promptSaveFailed", { message: error instanceof Error ? error.message : String(error) })
|
|
18253
|
+
});
|
|
18254
|
+
}).finally(() => {
|
|
18255
|
+
setSaving(false);
|
|
18256
|
+
});
|
|
18257
|
+
};
|
|
18258
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
18259
|
+
ref: anchor,
|
|
18260
|
+
style: {
|
|
18261
|
+
position: "relative",
|
|
18262
|
+
display: "inline-flex"
|
|
18263
|
+
},
|
|
18264
|
+
children: [
|
|
18265
|
+
/* @__PURE__ */ jsx("style", {
|
|
18266
|
+
"data-dsh-claude-prompt-save-styles": true,
|
|
18267
|
+
children: promptSaveTriggerCss
|
|
18268
|
+
}),
|
|
18269
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
18270
|
+
label,
|
|
18271
|
+
side: "top",
|
|
18272
|
+
delayMs: 250,
|
|
18273
|
+
disabled: panel !== void 0,
|
|
18274
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
18275
|
+
type: "button",
|
|
18276
|
+
className: promptSaveTriggerClass,
|
|
18277
|
+
"aria-label": label,
|
|
18278
|
+
"aria-haspopup": "dialog",
|
|
18279
|
+
"aria-expanded": panel !== void 0,
|
|
18280
|
+
disabled: draft.trim() === "",
|
|
18281
|
+
onClick: () => {
|
|
18282
|
+
if (panel === void 0) open();
|
|
18283
|
+
else close();
|
|
18284
|
+
},
|
|
18285
|
+
children: /* @__PURE__ */ jsx(IconListPenOutline16, { size: 14 })
|
|
18286
|
+
})
|
|
18287
|
+
}),
|
|
18288
|
+
panel === void 0 || typeof document === "undefined" ? null : createPortal(/* @__PURE__ */ jsx("div", {
|
|
18289
|
+
ref: panelRef,
|
|
18290
|
+
style: {
|
|
18291
|
+
...promptSaveCard,
|
|
18292
|
+
...position
|
|
18293
|
+
},
|
|
18294
|
+
role: "dialog",
|
|
18295
|
+
"aria-label": label,
|
|
18296
|
+
children: panel.kind === "saved" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
18297
|
+
/* @__PURE__ */ jsx("span", { children: t("promptSaved", { name: panel.prompt.name }) }),
|
|
18298
|
+
/* @__PURE__ */ jsx("span", {
|
|
18299
|
+
style: promptSaveLocation,
|
|
18300
|
+
children: panel.prompt.location
|
|
18301
|
+
}),
|
|
18302
|
+
/* @__PURE__ */ jsx("span", {
|
|
18303
|
+
style: promptSaveActions,
|
|
18304
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
18305
|
+
variant: "primary",
|
|
18306
|
+
size: "sm",
|
|
18307
|
+
onClick: close,
|
|
18308
|
+
children: t("promptSaveDone")
|
|
18309
|
+
})
|
|
18310
|
+
})
|
|
18311
|
+
] }) : /* @__PURE__ */ jsxs("form", {
|
|
18312
|
+
style: { display: "contents" },
|
|
18313
|
+
onSubmit: (event) => {
|
|
18314
|
+
event.preventDefault();
|
|
18315
|
+
save();
|
|
18316
|
+
},
|
|
18317
|
+
children: [
|
|
18318
|
+
/* @__PURE__ */ jsx("span", {
|
|
18319
|
+
style: promptSaveHeading,
|
|
18320
|
+
children: panel.suggesting ? t("promptSaveNaming") : label
|
|
18321
|
+
}),
|
|
18322
|
+
/* @__PURE__ */ jsx("input", {
|
|
18323
|
+
className: promptSaveFieldClass,
|
|
18324
|
+
"aria-label": t("promptSaveName"),
|
|
18325
|
+
placeholder: t("promptSaveName"),
|
|
18326
|
+
value: panel.name,
|
|
18327
|
+
maxLength: 128,
|
|
18328
|
+
autoFocus: true,
|
|
18329
|
+
disabled: saving,
|
|
18330
|
+
onChange: (event) => setPanel({
|
|
18331
|
+
...panel,
|
|
18332
|
+
name: event.currentTarget.value,
|
|
18333
|
+
touched: true
|
|
18334
|
+
}),
|
|
18335
|
+
onKeyDown: (event) => {
|
|
18336
|
+
if (event.key === "Escape") close();
|
|
18337
|
+
}
|
|
18338
|
+
}),
|
|
18339
|
+
panel.failure === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
18340
|
+
style: promptSaveError,
|
|
18341
|
+
children: panel.failure
|
|
18342
|
+
}),
|
|
18343
|
+
/* @__PURE__ */ jsxs("span", {
|
|
18344
|
+
style: promptSaveActions,
|
|
18345
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
18346
|
+
variant: "ghost",
|
|
18347
|
+
size: "sm",
|
|
18348
|
+
type: "button",
|
|
18349
|
+
disabled: saving,
|
|
18350
|
+
onClick: close,
|
|
18351
|
+
children: t("promptSaveCancel")
|
|
18352
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
18353
|
+
variant: "primary",
|
|
18354
|
+
size: "sm",
|
|
18355
|
+
type: "submit",
|
|
18356
|
+
disabled: saving,
|
|
18357
|
+
children: t("promptSaveConfirm")
|
|
18358
|
+
})]
|
|
18359
|
+
})
|
|
18360
|
+
]
|
|
18361
|
+
})
|
|
18362
|
+
}), document.body)
|
|
18363
|
+
]
|
|
18364
|
+
});
|
|
18365
|
+
}
|
|
18366
|
+
//#endregion
|
|
18367
|
+
//#region src/client/ClaudePromptRefineAction.tsx
|
|
18368
|
+
/**
|
|
18369
|
+
* Rewrite the draft in place, from the composer's own tool row.
|
|
18370
|
+
*
|
|
18371
|
+
* The rewrite replaces the draft outright, which is what makes the undo state
|
|
18372
|
+
* load-bearing rather than a nicety: `SessionInput.setDraft` documents itself
|
|
18373
|
+
* as "merged into history so a seed is not an undoable step of its own", so
|
|
18374
|
+
* Ctrl/Cmd+Z does NOT bring the original back. Without somewhere to put it,
|
|
18375
|
+
* one press of this button would silently destroy a draft the user may have
|
|
18376
|
+
* spent minutes on. So the original is held for exactly as long as it is still
|
|
18377
|
+
* recoverable — until the rewrite is edited or sent — and the same button
|
|
18378
|
+
* offers it back over that window.
|
|
18379
|
+
*/
|
|
18380
|
+
function ClaudePromptRefineAction({ t, useClaudeProjection, input, replaceDraft, notify, refine = refineClaudePrompt }) {
|
|
18381
|
+
const owned = useClaudeProjection((projection) => projection.owned);
|
|
18382
|
+
const attempt = useRef(void 0);
|
|
18383
|
+
const [busy, setBusy] = useState(false);
|
|
18384
|
+
const [applied, setApplied] = useState();
|
|
18385
|
+
useEffect(() => () => {
|
|
18386
|
+
attempt.current?.abort();
|
|
18387
|
+
}, []);
|
|
18388
|
+
if (!owned || replaceDraft === void 0) return null;
|
|
18389
|
+
const draft = input?.draft ?? "";
|
|
18390
|
+
const undoable = applied !== void 0 && draft === applied.refined;
|
|
18391
|
+
const label = busy ? t("promptRefineBusy") : undoable ? t("promptRefineUndo") : t("promptRefine");
|
|
18392
|
+
const run = () => {
|
|
18393
|
+
if (busy || draft.trim() === "") return;
|
|
18394
|
+
setBusy(true);
|
|
18395
|
+
const running = new AbortController();
|
|
18396
|
+
attempt.current = running;
|
|
18397
|
+
refine(draft, running.signal).then((text) => {
|
|
18398
|
+
if (running.signal.aborted) return;
|
|
18399
|
+
setApplied({
|
|
18400
|
+
original: draft,
|
|
18401
|
+
refined: text
|
|
18402
|
+
});
|
|
18403
|
+
replaceDraft(text);
|
|
18404
|
+
}, (error) => {
|
|
18405
|
+
if (running.signal.aborted) return;
|
|
18406
|
+
notify?.("error", t("promptRefineFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
18407
|
+
}).finally(() => {
|
|
18408
|
+
setBusy(false);
|
|
18409
|
+
});
|
|
18410
|
+
};
|
|
18411
|
+
const undo = () => {
|
|
18412
|
+
if (applied === void 0) return;
|
|
18413
|
+
replaceDraft(applied.original);
|
|
18414
|
+
setApplied(void 0);
|
|
18415
|
+
};
|
|
18416
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
18417
|
+
label,
|
|
18418
|
+
side: "top",
|
|
18419
|
+
delayMs: 250,
|
|
18420
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
18421
|
+
type: "button",
|
|
18422
|
+
className: promptSaveTriggerClass,
|
|
18423
|
+
"aria-label": label,
|
|
18424
|
+
disabled: busy || !undoable && draft.trim() === "",
|
|
18425
|
+
onClick: undoable ? undo : run,
|
|
18426
|
+
children: [/* @__PURE__ */ jsx("style", {
|
|
18427
|
+
"data-dsh-claude-prompt-refine-styles": true,
|
|
18428
|
+
children: promptSaveTriggerCss
|
|
18429
|
+
}), busy ? /* @__PURE__ */ jsx(IconLoadingOutline16, {
|
|
18430
|
+
size: 14,
|
|
18431
|
+
className: promptSpinClass
|
|
18432
|
+
}) : undoable ? /* @__PURE__ */ jsx(IconRefreshOutline16, {
|
|
18433
|
+
size: 14,
|
|
18434
|
+
className: promptUndoClass
|
|
18435
|
+
}) : /* @__PURE__ */ jsx(IconSparkle16, { size: 14 })]
|
|
18436
|
+
})
|
|
18437
|
+
});
|
|
18438
|
+
}
|
|
18439
|
+
//#endregion
|
|
18440
|
+
//#region src/client/claude-prompt-source.ts
|
|
18441
|
+
/**
|
|
18442
|
+
* The user's own prompt snippets as a second `/` group.
|
|
18443
|
+
*
|
|
18444
|
+
* Unlike {@link createClaudeCommandSource}, a pick here settles as plain text:
|
|
18445
|
+
* the pipeline replaces the trigger token with the snippet body and leaves the
|
|
18446
|
+
* caret after it, so the draft stays editable and nothing is sent. A snippet is
|
|
18447
|
+
* a half-written message, not a command.
|
|
18448
|
+
*/
|
|
18449
|
+
function createClaudePromptSource(groupName, load = claudePrompts) {
|
|
18450
|
+
let known = [];
|
|
18451
|
+
return {
|
|
18452
|
+
trigger: "/",
|
|
18453
|
+
name: groupName,
|
|
18454
|
+
order: 20,
|
|
18455
|
+
async candidates(_session, request) {
|
|
18456
|
+
if (request.position !== "leading") return [];
|
|
18457
|
+
known = await load();
|
|
18458
|
+
const query = request.query.toLocaleLowerCase();
|
|
18459
|
+
return known.filter((prompt) => prompt.name.toLocaleLowerCase().includes(query)).map((prompt) => ({
|
|
18460
|
+
name: prompt.name,
|
|
18461
|
+
description: prompt.description
|
|
18462
|
+
}));
|
|
18463
|
+
},
|
|
18464
|
+
onPick(pick) {
|
|
18465
|
+
const prompt = known.find((item) => item.name === pick.candidate.name);
|
|
18466
|
+
return prompt === void 0 ? void 0 : { text: prompt.body };
|
|
18467
|
+
}
|
|
18468
|
+
};
|
|
18469
|
+
}
|
|
18470
|
+
//#endregion
|
|
17645
18471
|
//#region src/client/preset-seat-mark.ts
|
|
17646
18472
|
/** Flags the Host's agent-preset seat while it names the Claude preset.
|
|
17647
18473
|
*
|
|
@@ -17940,7 +18766,7 @@ window.__ModuleLoader__.load({
|
|
|
17940
18766
|
worktreeBranchPrefixEffect: "分支前缀修改会应用于之后自动创建的 Worktree 分支;显式指定的完整分支名不会被修改。",
|
|
17941
18767
|
maxProcessesSetting: "Claude 进程上限",
|
|
17942
18768
|
idleTimeoutSetting: "闲置回收时长(分钟)",
|
|
17943
|
-
maxProcessesEffect: "
|
|
18769
|
+
maxProcessesEffect: "保存后立即回收超额空闲进程;运行中的进程完成后再收敛。满载时,新会话会等待可用容量。留空或非法值会被拒绝。",
|
|
17944
18770
|
idleTimeoutEffect: "闲置回收时长在下一次回合结束后生效。留空或非法值会被拒绝。",
|
|
17945
18771
|
settingHint: "查看说明",
|
|
17946
18772
|
pluginUpdate: "插件更新",
|
|
@@ -18098,6 +18924,20 @@ window.__ModuleLoader__.load({
|
|
|
18098
18924
|
sessionMenu: "会话菜单",
|
|
18099
18925
|
sessionMenuOpenIn: "打开方式",
|
|
18100
18926
|
sessionMenuOpenFailed: "无法打开:{message}",
|
|
18927
|
+
promptSource: "常用提示词",
|
|
18928
|
+
promptSave: "存为常用提示词",
|
|
18929
|
+
promptSaveName: "名字",
|
|
18930
|
+
promptSaveNaming: "正在起名…",
|
|
18931
|
+
promptRefine: "AI 优化提示词",
|
|
18932
|
+
promptRefineBusy: "优化中…",
|
|
18933
|
+
promptRefineUndo: "还原成优化前的内容",
|
|
18934
|
+
promptRefineFailed: "优化失败:{message}",
|
|
18935
|
+
promptSaveConfirm: "保存",
|
|
18936
|
+
promptSaveCancel: "取消",
|
|
18937
|
+
promptSaveDone: "知道了",
|
|
18938
|
+
promptSaved: "已存为「{name}」",
|
|
18939
|
+
promptSaveExists: "已有同名提示词,换个名字。",
|
|
18940
|
+
promptSaveFailed: "保存失败:{message}",
|
|
18101
18941
|
presetHeaderHint: "当前会话运行的 Agent 预设",
|
|
18102
18942
|
diffWorkingTree: "分支改动",
|
|
18103
18943
|
diffFiles: "{count} 个已修改文件",
|
|
@@ -18160,6 +19000,24 @@ window.__ModuleLoader__.load({
|
|
|
18160
19000
|
diffUpdateBranchCompleted: "已更新并推送 commit {commit}",
|
|
18161
19001
|
diffUpdateBranchConflicts: "更新产生冲突,以下文件需要解决:",
|
|
18162
19002
|
diffUpdateBranchResolve: "让 Claude 解决冲突",
|
|
19003
|
+
conflictTitle: "解决冲突",
|
|
19004
|
+
conflictOperation_rebase: "Rebase",
|
|
19005
|
+
conflictOperation_merge: "合并",
|
|
19006
|
+
"conflictOperation_cherry-pick": "Cherry-pick",
|
|
19007
|
+
conflictOperation_revert: "Revert",
|
|
19008
|
+
conflictBadge: "{operation} 冲突 {count}",
|
|
19009
|
+
conflictBadgeReady: "继续 {operation}",
|
|
19010
|
+
conflictDescription: "{operation} 尚未完成。解决冲突后继续,或中止回到操作前的状态。",
|
|
19011
|
+
conflictFiles: "以下文件存在冲突,需要解决并 git add:",
|
|
19012
|
+
conflictReady: "冲突已全部解决,可以继续。",
|
|
19013
|
+
conflictResolve: "让 Claude 解决冲突",
|
|
19014
|
+
conflictContinue: "继续 {operation}",
|
|
19015
|
+
conflictAbort: "中止 {operation}",
|
|
19016
|
+
conflictAbortConfirm: "再次点击「中止 {operation}」确认:{operation} 期间的改动会被丢弃。",
|
|
19017
|
+
conflictPush: "完成后推送到远端",
|
|
19018
|
+
conflictContinued: "{operation} 已完成",
|
|
19019
|
+
conflictPushed: "{operation} 已完成并推送",
|
|
19020
|
+
conflictAborted: "已中止 {operation}",
|
|
18163
19021
|
repositoryChecksOpen: "查看失败的检查",
|
|
18164
19022
|
checksCardTitle: "失败的检查",
|
|
18165
19023
|
checksCardLoading: "正在读取检查详情…",
|
|
@@ -18350,7 +19208,7 @@ window.__ModuleLoader__.load({
|
|
|
18350
19208
|
worktreeBranchPrefixEffect: "Prefix changes apply to subsequently generated Worktree branches. Explicit full branch names remain unchanged.",
|
|
18351
19209
|
maxProcessesSetting: "Claude process limit",
|
|
18352
19210
|
idleTimeoutSetting: "Idle timeout (minutes)",
|
|
18353
|
-
maxProcessesEffect: "
|
|
19211
|
+
maxProcessesEffect: "Saving immediately reclaims excess idle processes; running processes converge after they finish. New sessions wait for capacity while every process is busy. Blank or invalid values are rejected.",
|
|
18354
19212
|
idleTimeoutEffect: "The idle timeout applies after the next completed turn. Blank or invalid values are rejected.",
|
|
18355
19213
|
settingHint: "Show details",
|
|
18356
19214
|
pluginUpdate: "Plugin updates",
|
|
@@ -18508,6 +19366,20 @@ window.__ModuleLoader__.load({
|
|
|
18508
19366
|
sessionMenu: "Session menu",
|
|
18509
19367
|
sessionMenuOpenIn: "Open in",
|
|
18510
19368
|
sessionMenuOpenFailed: "Could not open: {message}",
|
|
19369
|
+
promptSource: "Prompts",
|
|
19370
|
+
promptSave: "Save as a prompt",
|
|
19371
|
+
promptSaveName: "Name",
|
|
19372
|
+
promptSaveNaming: "Naming it…",
|
|
19373
|
+
promptRefine: "Rewrite with AI",
|
|
19374
|
+
promptRefineBusy: "Rewriting…",
|
|
19375
|
+
promptRefineUndo: "Put the original back",
|
|
19376
|
+
promptRefineFailed: "Could not rewrite: {message}",
|
|
19377
|
+
promptSaveConfirm: "Save",
|
|
19378
|
+
promptSaveCancel: "Cancel",
|
|
19379
|
+
promptSaveDone: "Got it",
|
|
19380
|
+
promptSaved: "Saved as \"{name}\"",
|
|
19381
|
+
promptSaveExists: "A prompt with that name already exists. Try another.",
|
|
19382
|
+
promptSaveFailed: "Could not save: {message}",
|
|
18511
19383
|
presetHeaderHint: "The agent preset this session runs",
|
|
18512
19384
|
diffWorkingTree: "Branch changes",
|
|
18513
19385
|
diffFiles: "{count} modified file(s)",
|
|
@@ -18570,6 +19442,24 @@ window.__ModuleLoader__.load({
|
|
|
18570
19442
|
diffUpdateBranchCompleted: "Updated and pushed commit {commit}",
|
|
18571
19443
|
diffUpdateBranchConflicts: "The update left conflicts in these files:",
|
|
18572
19444
|
diffUpdateBranchResolve: "Have Claude resolve the conflicts",
|
|
19445
|
+
conflictTitle: "Resolve conflicts",
|
|
19446
|
+
conflictOperation_rebase: "Rebase",
|
|
19447
|
+
conflictOperation_merge: "Merge",
|
|
19448
|
+
"conflictOperation_cherry-pick": "Cherry-pick",
|
|
19449
|
+
conflictOperation_revert: "Revert",
|
|
19450
|
+
conflictBadge: "{operation}: {count} conflict(s)",
|
|
19451
|
+
conflictBadgeReady: "Continue {operation}",
|
|
19452
|
+
conflictDescription: "The {operation} is unfinished. Resolve the conflicts and continue, or abort to return to the state before it.",
|
|
19453
|
+
conflictFiles: "These files conflict and must be resolved and staged:",
|
|
19454
|
+
conflictReady: "Every conflict is resolved. The operation can continue.",
|
|
19455
|
+
conflictResolve: "Have Claude resolve the conflicts",
|
|
19456
|
+
conflictContinue: "Continue {operation}",
|
|
19457
|
+
conflictAbort: "Abort {operation}",
|
|
19458
|
+
conflictAbortConfirm: "Press \"Abort {operation}\" again to confirm: work done during the {operation} is discarded.",
|
|
19459
|
+
conflictPush: "Push once it finishes",
|
|
19460
|
+
conflictContinued: "{operation} finished",
|
|
19461
|
+
conflictPushed: "{operation} finished and pushed",
|
|
19462
|
+
conflictAborted: "Aborted the {operation}",
|
|
18573
19463
|
repositoryChecksOpen: "Show failing checks",
|
|
18574
19464
|
checksCardTitle: "Failing checks",
|
|
18575
19465
|
checksCardLoading: "Loading check details…",
|
|
@@ -18785,6 +19675,7 @@ window.__ModuleLoader__.load({
|
|
|
18785
19675
|
diagnostics.report(kind, detail);
|
|
18786
19676
|
} });
|
|
18787
19677
|
ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(ctx, projections)), "dsh-claude: Claude slash source");
|
|
19678
|
+
ctx.effect(() => ctx.inputTriggers.registerSource(createClaudePromptSource(t("promptSource"))), "dsh-claude: Claude prompt source");
|
|
18788
19679
|
const sessions = ctx.get("sessions");
|
|
18789
19680
|
const workspaces = ctx.get("workspaces");
|
|
18790
19681
|
const uiWorkspace = ctx.get("uiWorkspace");
|
|
@@ -19131,6 +20022,34 @@ window.__ModuleLoader__.load({
|
|
|
19131
20022
|
locale: namespace,
|
|
19132
20023
|
inject: () => ({ t })
|
|
19133
20024
|
}, ClaudeSessionMenu));
|
|
20025
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
20026
|
+
name: "conversation.input.left",
|
|
20027
|
+
id: "claude-prompt-save",
|
|
20028
|
+
order: 40,
|
|
20029
|
+
locale: namespace,
|
|
20030
|
+
inject: () => ({ t })
|
|
20031
|
+
}, ClaudePromptSaveAction));
|
|
20032
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
20033
|
+
name: "conversation.input.left",
|
|
20034
|
+
id: "claude-prompt-refine",
|
|
20035
|
+
order: 41,
|
|
20036
|
+
locale: namespace,
|
|
20037
|
+
inject: (sessionId) => {
|
|
20038
|
+
if (sessions === void 0 || conversation === void 0) return { t };
|
|
20039
|
+
const scope = sessions.scope(sessionId);
|
|
20040
|
+
if (scope === void 0) return { t };
|
|
20041
|
+
const facade = sessionInput(conversation, scope);
|
|
20042
|
+
return {
|
|
20043
|
+
t,
|
|
20044
|
+
replaceDraft: (text) => {
|
|
20045
|
+
facade.setDraft(text);
|
|
20046
|
+
},
|
|
20047
|
+
notify: (level, text) => {
|
|
20048
|
+
facade.notify(level, text);
|
|
20049
|
+
}
|
|
20050
|
+
};
|
|
20051
|
+
}
|
|
20052
|
+
}, ClaudePromptRefineAction));
|
|
19134
20053
|
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
19135
20054
|
name: "conversation.input.dock",
|
|
19136
20055
|
id: "claude-review-comments",
|