@norman-else/dsh-claude 0.1.41 → 0.1.42
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 +15 -1
- package/lib/client.js +658 -8
- 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 +658 -40
- 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
|
}
|
|
@@ -1834,7 +1837,7 @@ window.__ModuleLoader__.load({
|
|
|
1834
1837
|
whiteSpace: "nowrap"
|
|
1835
1838
|
};
|
|
1836
1839
|
const repositoryBarFrame = {
|
|
1837
|
-
width: "calc(100% -
|
|
1840
|
+
width: "calc(100% - 2 * var(--dsh-composer-side-clearance, 16px))",
|
|
1838
1841
|
maxWidth: "var(--dsh-composer-card-max-width, 782px)",
|
|
1839
1842
|
minWidth: 0,
|
|
1840
1843
|
margin: "0 auto",
|
|
@@ -2994,7 +2997,7 @@ window.__ModuleLoader__.load({
|
|
|
2994
2997
|
fontSize: 11
|
|
2995
2998
|
};
|
|
2996
2999
|
const reviewCommentBarFrame = {
|
|
2997
|
-
width: "calc(100% -
|
|
3000
|
+
width: "calc(100% - 2 * var(--dsh-composer-side-clearance, 16px))",
|
|
2998
3001
|
maxWidth: "var(--dsh-composer-card-max-width, 782px)",
|
|
2999
3002
|
margin: "0 auto",
|
|
3000
3003
|
boxSizing: "border-box"
|
|
@@ -3085,6 +3088,113 @@ window.__ModuleLoader__.load({
|
|
|
3085
3088
|
whiteSpace: "pre-wrap",
|
|
3086
3089
|
overflowWrap: "anywhere"
|
|
3087
3090
|
};
|
|
3091
|
+
const promptSaveTriggerClass = "dshClaudePromptSaveTrigger";
|
|
3092
|
+
const promptSpinClass = "dshClaudePromptSpin";
|
|
3093
|
+
const promptUndoClass = "dshClaudePromptUndo";
|
|
3094
|
+
const promptSaveFieldClass = "dshClaudePromptSaveField";
|
|
3095
|
+
/** The composer's own attach button geometry (InputBar.module.css `.add`:
|
|
3096
|
+
* 28x28 circle on --dsw-specific-selector, 14px glyph). The primitives'
|
|
3097
|
+
* Button has no icon-only form — its `sm` is a 36px-wide capsule around a
|
|
3098
|
+
* 16px slot, which is why this reads oversized beside its neighbours. */
|
|
3099
|
+
const promptSaveTriggerCss = `
|
|
3100
|
+
.${promptSaveTriggerClass} {
|
|
3101
|
+
width: 28px;
|
|
3102
|
+
height: 28px;
|
|
3103
|
+
flex: none;
|
|
3104
|
+
display: grid;
|
|
3105
|
+
place-items: center;
|
|
3106
|
+
padding: 0;
|
|
3107
|
+
border: none;
|
|
3108
|
+
border-radius: 999px;
|
|
3109
|
+
background: var(--dsw-specific-selector);
|
|
3110
|
+
color: var(--dsw-alias-label-primary);
|
|
3111
|
+
cursor: pointer;
|
|
3112
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
3113
|
+
}
|
|
3114
|
+
/* With no draft there is nothing to keep, so the control drops its seat
|
|
3115
|
+
* entirely rather than sitting there as a filled-but-dead circle. */
|
|
3116
|
+
.${promptSaveTriggerClass}:disabled {
|
|
3117
|
+
cursor: default;
|
|
3118
|
+
background: transparent;
|
|
3119
|
+
color: var(--dsw-alias-label-tertiary);
|
|
3120
|
+
}
|
|
3121
|
+
.${promptSaveTriggerClass}:focus-visible { outline: none; }
|
|
3122
|
+
.${promptSaveFieldClass} {
|
|
3123
|
+
box-sizing: border-box;
|
|
3124
|
+
width: 100%;
|
|
3125
|
+
height: 30px;
|
|
3126
|
+
padding: 0 9px;
|
|
3127
|
+
border: 1px solid var(--dsw-alias-border-inverted);
|
|
3128
|
+
border-radius: 7px;
|
|
3129
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
3130
|
+
color: var(--dsw-alias-label-primary);
|
|
3131
|
+
font-family: var(--dsw-font-family);
|
|
3132
|
+
font-size: 13px;
|
|
3133
|
+
line-height: 20px;
|
|
3134
|
+
}
|
|
3135
|
+
/* Not --dsw-alias-brand-primary: in the dark theme that alias resolves to
|
|
3136
|
+
* neutral-bluish-50, so a focused field draws a near-white slab around
|
|
3137
|
+
* itself. The focus ring is a step up in the same greys the card is made of. */
|
|
3138
|
+
.${promptSaveFieldClass}:focus-visible {
|
|
3139
|
+
outline: none;
|
|
3140
|
+
border-color: var(--dsw-alias-label-tertiary);
|
|
3141
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
3142
|
+
}
|
|
3143
|
+
.${promptSaveFieldClass}:disabled { opacity: 0.6; }
|
|
3144
|
+
/* ic_ds_loading_outline_16 is a bare open ring and nothing in the primitives
|
|
3145
|
+
* turns it, so a four-second rewrite sat behind a frozen glyph that reads as
|
|
3146
|
+
* the letter C. The spin is what makes it a spinner. */
|
|
3147
|
+
.${promptSpinClass} { animation: dshClaudePromptSpin 900ms linear infinite; }
|
|
3148
|
+
@keyframes dshClaudePromptSpin { to { transform: rotate(360deg); } }
|
|
3149
|
+
/* The icon set has no undo arrow, and ic_ds_refresh_outline_16 turns
|
|
3150
|
+
* clockwise — the redo direction. Mirrored, it is the ordinary undo glyph. */
|
|
3151
|
+
.${promptUndoClass} { transform: scaleX(-1); }
|
|
3152
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3153
|
+
.${promptSpinClass} { animation: none; }
|
|
3154
|
+
}
|
|
3155
|
+
`;
|
|
3156
|
+
/** The naming card hangs off the composer tool row, portaled so the composer
|
|
3157
|
+
* card cannot clip it and positioned by useAnchoredPosition. */
|
|
3158
|
+
const promptSaveCard = {
|
|
3159
|
+
position: "fixed",
|
|
3160
|
+
zIndex: 120,
|
|
3161
|
+
boxSizing: "border-box",
|
|
3162
|
+
display: "flex",
|
|
3163
|
+
flexDirection: "column",
|
|
3164
|
+
gap: 8,
|
|
3165
|
+
width: 340,
|
|
3166
|
+
maxWidth: "calc(100vw - 32px)",
|
|
3167
|
+
padding: "10px 12px",
|
|
3168
|
+
border: "1px solid var(--dsw-alias-border-inverted)",
|
|
3169
|
+
borderRadius: 11,
|
|
3170
|
+
background: "var(--dsw-specific-menu)",
|
|
3171
|
+
boxShadow: "var(--dsw-shadow-lv3)",
|
|
3172
|
+
color: "var(--dsw-alias-label-primary)",
|
|
3173
|
+
fontSize: 12,
|
|
3174
|
+
lineHeight: "20px"
|
|
3175
|
+
};
|
|
3176
|
+
const promptSaveHeading = {
|
|
3177
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3178
|
+
fontSize: 11,
|
|
3179
|
+
lineHeight: "16px"
|
|
3180
|
+
};
|
|
3181
|
+
const promptSaveActions = {
|
|
3182
|
+
display: "flex",
|
|
3183
|
+
justifyContent: "flex-end",
|
|
3184
|
+
gap: 6
|
|
3185
|
+
};
|
|
3186
|
+
/** The saved file, wrapped rather than truncated: a path the user cannot read
|
|
3187
|
+
* in full does not tell them where to go and edit it. */
|
|
3188
|
+
const promptSaveLocation = {
|
|
3189
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3190
|
+
fontSize: 11,
|
|
3191
|
+
lineHeight: "16px",
|
|
3192
|
+
overflowWrap: "anywhere"
|
|
3193
|
+
};
|
|
3194
|
+
const promptSaveError = {
|
|
3195
|
+
color: "var(--dsw-static-red-450, #d64545)",
|
|
3196
|
+
overflowWrap: "anywhere"
|
|
3197
|
+
};
|
|
3088
3198
|
const diffAhead = { color: "var(--dsw-static-blue-450)" };
|
|
3089
3199
|
const diffAheadMuted = { color: "color-mix(in srgb, var(--dsw-static-blue-450) 62%, var(--dsw-alias-label-tertiary))" };
|
|
3090
3200
|
const diffAddMuted = { color: "color-mix(in srgb, var(--dsw-alias-state-success-primary) 62%, var(--dsw-alias-label-tertiary))" };
|
|
@@ -4008,6 +4118,16 @@ window.__ModuleLoader__.load({
|
|
|
4008
4118
|
laneHeld[lane] -= 1;
|
|
4009
4119
|
pump();
|
|
4010
4120
|
}
|
|
4121
|
+
/** Runs at most once, so a release can be wired to every ending of a stream
|
|
4122
|
+
* without any of them having to know about the others. */
|
|
4123
|
+
function once(action) {
|
|
4124
|
+
let done = false;
|
|
4125
|
+
return () => {
|
|
4126
|
+
if (done) return;
|
|
4127
|
+
done = true;
|
|
4128
|
+
action();
|
|
4129
|
+
};
|
|
4130
|
+
}
|
|
4011
4131
|
function acquire(lane) {
|
|
4012
4132
|
let released = false;
|
|
4013
4133
|
const releaseOnce = () => {
|
|
@@ -4111,11 +4231,53 @@ window.__ModuleLoader__.load({
|
|
|
4111
4231
|
writeId += 1;
|
|
4112
4232
|
return writeId;
|
|
4113
4233
|
}
|
|
4234
|
+
/**
|
|
4235
|
+
* A reader that gives its permit back however the stream ends.
|
|
4236
|
+
*
|
|
4237
|
+
* The permit used to be released by one event only -- the caller's abort --
|
|
4238
|
+
* and a stream has three endings, two of which the caller does not cause: the
|
|
4239
|
+
* server closing the response, and the body failing mid-read. Either one left
|
|
4240
|
+
* the permit held. For the reserved carrier that permit is a single boolean,
|
|
4241
|
+
* so one such ending closed the transcript stream for the life of the page and
|
|
4242
|
+
* every reopen answered `starved`, silently, forever.
|
|
4243
|
+
*
|
|
4244
|
+
* Binding the release to the reader rather than to the signal is what makes
|
|
4245
|
+
* that unrepresentable: a caller cannot hold the permit past the stream it is
|
|
4246
|
+
* reading, because every way of leaving the read passes through here. `free`
|
|
4247
|
+
* is idempotent, so the abort listener below stays as the belt to this brace.
|
|
4248
|
+
*/
|
|
4249
|
+
function releasingReader(reader, free) {
|
|
4250
|
+
return {
|
|
4251
|
+
get closed() {
|
|
4252
|
+
return reader.closed;
|
|
4253
|
+
},
|
|
4254
|
+
read: async () => {
|
|
4255
|
+
try {
|
|
4256
|
+
const chunk = await reader.read();
|
|
4257
|
+
if (chunk.done) free();
|
|
4258
|
+
return chunk;
|
|
4259
|
+
} catch (error) {
|
|
4260
|
+
free();
|
|
4261
|
+
throw error;
|
|
4262
|
+
}
|
|
4263
|
+
},
|
|
4264
|
+
cancel: async (reason) => {
|
|
4265
|
+
try {
|
|
4266
|
+
await reader.cancel(reason);
|
|
4267
|
+
} finally {
|
|
4268
|
+
free();
|
|
4269
|
+
}
|
|
4270
|
+
},
|
|
4271
|
+
releaseLock: () => {
|
|
4272
|
+
reader.releaseLock();
|
|
4273
|
+
}
|
|
4274
|
+
};
|
|
4275
|
+
}
|
|
4114
4276
|
async function openStream(lane, path, cancel, options, reserved) {
|
|
4115
4277
|
const url = withQuery(path, options?.query);
|
|
4116
|
-
const free = reserved ? () => {
|
|
4278
|
+
const free = reserved ? once(() => {
|
|
4117
4279
|
projectionHeld = false;
|
|
4118
|
-
} : await acquire(lane);
|
|
4280
|
+
}) : await acquire(lane);
|
|
4119
4281
|
try {
|
|
4120
4282
|
const response = await send(url, {
|
|
4121
4283
|
method: options?.method ?? "GET",
|
|
@@ -4133,7 +4295,7 @@ window.__ModuleLoader__.load({
|
|
|
4133
4295
|
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
4134
4296
|
}
|
|
4135
4297
|
cancel.addEventListener("abort", free, { once: true });
|
|
4136
|
-
return response.body.getReader();
|
|
4298
|
+
return releasingReader(response.body.getReader(), free);
|
|
4137
4299
|
} catch (error) {
|
|
4138
4300
|
free();
|
|
4139
4301
|
throw failureOf(error, cancel);
|
|
@@ -4598,6 +4760,10 @@ window.__ModuleLoader__.load({
|
|
|
4598
4760
|
#report;
|
|
4599
4761
|
#resyncCooldownMs;
|
|
4600
4762
|
#resyncedAt = 0;
|
|
4763
|
+
/** Whether the carrier is in a run of failures. One report per outage: the
|
|
4764
|
+
* retry loop runs every couple of seconds and a beacon per attempt would
|
|
4765
|
+
* be the plugin reporting its own noise. */
|
|
4766
|
+
#carrierFailing = false;
|
|
4601
4767
|
#controller;
|
|
4602
4768
|
#settle;
|
|
4603
4769
|
#running = false;
|
|
@@ -4683,6 +4849,7 @@ window.__ModuleLoader__.load({
|
|
|
4683
4849
|
let superseded = false;
|
|
4684
4850
|
try {
|
|
4685
4851
|
const reader = await this.#open(`${CLAUDE_PROJECTION_PATH}/multi?sessions=${lanes.map(encodeURIComponent).join(",")}`, controller.signal);
|
|
4852
|
+
this.#carrierFailing = false;
|
|
4686
4853
|
const stop = () => {
|
|
4687
4854
|
superseded = true;
|
|
4688
4855
|
reader.cancel().catch(() => void 0);
|
|
@@ -4705,6 +4872,10 @@ window.__ModuleLoader__.load({
|
|
|
4705
4872
|
if (this.#controller !== controller) continue;
|
|
4706
4873
|
return;
|
|
4707
4874
|
}
|
|
4875
|
+
if (!this.#carrierFailing) {
|
|
4876
|
+
this.#carrierFailing = true;
|
|
4877
|
+
this.#report("projection-carrier-unavailable", error instanceof Error ? error.message : String(error));
|
|
4878
|
+
}
|
|
4708
4879
|
} finally {
|
|
4709
4880
|
if (this.#controller === controller) this.#controller = void 0;
|
|
4710
4881
|
}
|
|
@@ -17642,6 +17813,428 @@ window.__ModuleLoader__.load({
|
|
|
17642
17813
|
}
|
|
17643
17814
|
}
|
|
17644
17815
|
//#endregion
|
|
17816
|
+
//#region src/client/prompt-api.ts
|
|
17817
|
+
/** Prompt files live in one global directory, so one module-level cache serves
|
|
17818
|
+
* every session. The TTL is what lets a file added outside DSH appear without
|
|
17819
|
+
* a reload, while a burst of keystrokes through the menu still costs one
|
|
17820
|
+
* directory scan. */
|
|
17821
|
+
const TTL_MS = 5e3;
|
|
17822
|
+
let cached;
|
|
17823
|
+
function invalidateClaudePrompts() {
|
|
17824
|
+
cached = void 0;
|
|
17825
|
+
}
|
|
17826
|
+
/** The user's prompt snippets, at most one read per TTL window. */
|
|
17827
|
+
async function claudePrompts() {
|
|
17828
|
+
const at = Date.now();
|
|
17829
|
+
if (cached !== void 0 && at - cached.at < TTL_MS) return await cached.prompts;
|
|
17830
|
+
const prompts = pluginRead(CLAUDE_PROMPTS_PATH, "fast").then((payload) => payload.prompts).catch(() => {
|
|
17831
|
+
invalidateClaudePrompts();
|
|
17832
|
+
return [];
|
|
17833
|
+
});
|
|
17834
|
+
cached = {
|
|
17835
|
+
at,
|
|
17836
|
+
prompts
|
|
17837
|
+
};
|
|
17838
|
+
return await prompts;
|
|
17839
|
+
}
|
|
17840
|
+
/** Save one snippet, answering where it landed. Rejects with a
|
|
17841
|
+
* `PluginRequestError` whose `code` is `name-taken` when the file already
|
|
17842
|
+
* exists; nothing is overwritten. */
|
|
17843
|
+
async function saveClaudePrompt(name, body) {
|
|
17844
|
+
const saved = await pluginWrite(CLAUDE_PROMPTS_PATH, "fast", void 0, { json: {
|
|
17845
|
+
name,
|
|
17846
|
+
body
|
|
17847
|
+
} });
|
|
17848
|
+
invalidateClaudePrompts();
|
|
17849
|
+
return saved.prompt;
|
|
17850
|
+
}
|
|
17851
|
+
/** Ask the host to name a draft with Claude's cheapest model. Answers
|
|
17852
|
+
* undefined whenever no name could be had — the caller already holds the
|
|
17853
|
+
* locally derived one, so a failure here is never worth reporting. */
|
|
17854
|
+
async function suggestClaudePromptName(draft, cancel) {
|
|
17855
|
+
try {
|
|
17856
|
+
return (await pluginWrite(CLAUDE_PROMPT_NAME_PATH, "git", cancel, { json: { draft } })).name;
|
|
17857
|
+
} catch {
|
|
17858
|
+
return;
|
|
17859
|
+
}
|
|
17860
|
+
}
|
|
17861
|
+
/** Rewrite a draft into something an agent can act on. Unlike the name
|
|
17862
|
+
* suggestion this one throws: the user asked for it and is waiting, so a
|
|
17863
|
+
* failure is theirs to see rather than ours to swallow. */
|
|
17864
|
+
async function refineClaudePrompt(draft, cancel) {
|
|
17865
|
+
return (await pluginWrite(CLAUDE_PROMPT_REFINE_PATH, "git", cancel, { json: { draft } })).text;
|
|
17866
|
+
}
|
|
17867
|
+
/** Characters the host's `PROMPT_NAME` guard rejects; scrubbed rather than
|
|
17868
|
+
* re-implemented here, so a drift in the guard cannot let a bad name through
|
|
17869
|
+
* (the host still validates, and answers `invalid-name`). */
|
|
17870
|
+
const FOREIGN_NAME_CHARS = /[^\p{L}\p{M}\p{N} ._()\[\]-]/gu;
|
|
17871
|
+
const MAX_NAME_CHARS = 40;
|
|
17872
|
+
function stamp(now) {
|
|
17873
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
17874
|
+
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
17875
|
+
}
|
|
17876
|
+
/** The file name to offer for a draft: its opening line, scrubbed to what a
|
|
17877
|
+
* file name may hold. A draft that opens with punctuation or an emoji leaves
|
|
17878
|
+
* nothing usable, so those fall back to a timestamp the user can rename. */
|
|
17879
|
+
function defaultPromptName(draft, now = /* @__PURE__ */ new Date()) {
|
|
17880
|
+
const collapsed = (draft.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "").replace(FOREIGN_NAME_CHARS, " ").replace(/\s+/gu, " ").trim();
|
|
17881
|
+
const cut = collapsed.slice(0, MAX_NAME_CHARS);
|
|
17882
|
+
const boundary = collapsed.length > MAX_NAME_CHARS ? cut.search(/[\s\-_][^\s\-_]*$/u) : -1;
|
|
17883
|
+
const scrubbed = (boundary > 0 ? cut.slice(0, boundary) : cut).trim();
|
|
17884
|
+
return /^[\p{L}\p{N}]/u.test(scrubbed) ? scrubbed : `prompt-${stamp(now)}`;
|
|
17885
|
+
}
|
|
17886
|
+
//#endregion
|
|
17887
|
+
//#region src/client/ClaudePromptSaveAction.tsx
|
|
17888
|
+
const CARD_GAP = 8;
|
|
17889
|
+
const CARD_MARGIN = 12;
|
|
17890
|
+
/**
|
|
17891
|
+
* Place the card above its trigger, clamped into the viewport, and dismiss it
|
|
17892
|
+
* on a pointer that lands outside both.
|
|
17893
|
+
*
|
|
17894
|
+
* The primitives ship `useAnchoredPosition` and `useDismissOnOutsidePointer`
|
|
17895
|
+
* for exactly this, and both are used here through their published types —
|
|
17896
|
+
* but the Host's build of that package predates the `side` option of one and
|
|
17897
|
+
* the portal argument of the other. Neither omission is visible to `tsc`,
|
|
17898
|
+
* which reads this checkout's newer copy, and both are silent at runtime: the
|
|
17899
|
+
* card hangs below a composer pinned to the bottom of the window, and every
|
|
17900
|
+
* pointer landing in the naming field counts as outside and closes it. Owning
|
|
17901
|
+
* twenty lines beats behaviour that depends on which Desktop the plugin was
|
|
17902
|
+
* installed into.
|
|
17903
|
+
*/
|
|
17904
|
+
function useAnchoredCard(open, anchor, card, onDismiss) {
|
|
17905
|
+
const [position, setPosition] = useState();
|
|
17906
|
+
useLayoutEffect(() => {
|
|
17907
|
+
if (!open) {
|
|
17908
|
+
setPosition(void 0);
|
|
17909
|
+
return;
|
|
17910
|
+
}
|
|
17911
|
+
const place = () => {
|
|
17912
|
+
const trigger = anchor.current?.getBoundingClientRect();
|
|
17913
|
+
const panel = card.current?.getBoundingClientRect();
|
|
17914
|
+
if (trigger === void 0 || panel === void 0) return;
|
|
17915
|
+
const above = trigger.top - CARD_GAP - panel.height;
|
|
17916
|
+
setPosition({
|
|
17917
|
+
left: Math.max(CARD_MARGIN, Math.min(trigger.left, window.innerWidth - panel.width - CARD_MARGIN)),
|
|
17918
|
+
top: above >= CARD_MARGIN ? above : Math.min(trigger.bottom + CARD_GAP, window.innerHeight - panel.height - CARD_MARGIN)
|
|
17919
|
+
});
|
|
17920
|
+
};
|
|
17921
|
+
place();
|
|
17922
|
+
window.addEventListener("resize", place);
|
|
17923
|
+
window.addEventListener("scroll", place, true);
|
|
17924
|
+
return () => {
|
|
17925
|
+
window.removeEventListener("resize", place);
|
|
17926
|
+
window.removeEventListener("scroll", place, true);
|
|
17927
|
+
};
|
|
17928
|
+
}, [
|
|
17929
|
+
open,
|
|
17930
|
+
anchor,
|
|
17931
|
+
card
|
|
17932
|
+
]);
|
|
17933
|
+
useEffect(() => {
|
|
17934
|
+
if (!open) return void 0;
|
|
17935
|
+
const dismiss = (event) => {
|
|
17936
|
+
const target = event.target;
|
|
17937
|
+
if (!(target instanceof Node)) return;
|
|
17938
|
+
if (anchor.current?.contains(target) === true || card.current?.contains(target) === true) return;
|
|
17939
|
+
onDismiss();
|
|
17940
|
+
};
|
|
17941
|
+
document.addEventListener("pointerdown", dismiss);
|
|
17942
|
+
return () => {
|
|
17943
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
17944
|
+
};
|
|
17945
|
+
}, [
|
|
17946
|
+
open,
|
|
17947
|
+
anchor,
|
|
17948
|
+
card,
|
|
17949
|
+
onDismiss
|
|
17950
|
+
]);
|
|
17951
|
+
return position;
|
|
17952
|
+
}
|
|
17953
|
+
/**
|
|
17954
|
+
* Keep the draft you just wrote, from the composer's own tool row.
|
|
17955
|
+
*
|
|
17956
|
+
* It sits beside the attach and access controls rather than in a row of its
|
|
17957
|
+
* own: a band above the composer moves the repository bar and the composer
|
|
17958
|
+
* itself every time a draft appears, and this is a once-in-a-while action that
|
|
17959
|
+
* has not earned that. The naming card is portaled and anchored, so opening it
|
|
17960
|
+
* displaces nothing either.
|
|
17961
|
+
*
|
|
17962
|
+
* The field opens on a name derived from the draft's first line, which costs
|
|
17963
|
+
* nothing and is there instantly, and a Claude-written name replaces it when
|
|
17964
|
+
* one arrives. That ordering is the whole naming design: the suggestion is an
|
|
17965
|
+
* improvement on a working answer, never something the user waits for.
|
|
17966
|
+
*/
|
|
17967
|
+
function ClaudePromptSaveAction({ t, useClaudeProjection, input, savePrompt = saveClaudePrompt, suggestName = suggestClaudePromptName }) {
|
|
17968
|
+
const owned = useClaudeProjection((projection) => projection.owned);
|
|
17969
|
+
const anchor = useRef(null);
|
|
17970
|
+
const panelRef = useRef(null);
|
|
17971
|
+
const suggestion = useRef(void 0);
|
|
17972
|
+
const [panel, setPanel] = useState();
|
|
17973
|
+
const [saving, setSaving] = useState(false);
|
|
17974
|
+
const close = useCallback(() => {
|
|
17975
|
+
suggestion.current?.abort();
|
|
17976
|
+
suggestion.current = void 0;
|
|
17977
|
+
setPanel(void 0);
|
|
17978
|
+
}, []);
|
|
17979
|
+
const position = useAnchoredCard(panel !== void 0, anchor, panelRef, close);
|
|
17980
|
+
useEffect(() => () => {
|
|
17981
|
+
suggestion.current?.abort();
|
|
17982
|
+
}, []);
|
|
17983
|
+
if (!owned) return null;
|
|
17984
|
+
const draft = input?.draft ?? "";
|
|
17985
|
+
const label = t("promptSave");
|
|
17986
|
+
const open = () => {
|
|
17987
|
+
setPanel({
|
|
17988
|
+
kind: "naming",
|
|
17989
|
+
name: defaultPromptName(draft),
|
|
17990
|
+
touched: false,
|
|
17991
|
+
suggesting: true
|
|
17992
|
+
});
|
|
17993
|
+
const attempt = new AbortController();
|
|
17994
|
+
suggestion.current = attempt;
|
|
17995
|
+
suggestName(draft, attempt.signal).then((suggested) => {
|
|
17996
|
+
if (attempt.signal.aborted) return;
|
|
17997
|
+
setPanel((current) => current?.kind !== "naming" ? current : {
|
|
17998
|
+
...current,
|
|
17999
|
+
suggesting: false,
|
|
18000
|
+
...suggested === void 0 || current.touched ? {} : { name: suggested }
|
|
18001
|
+
});
|
|
18002
|
+
});
|
|
18003
|
+
};
|
|
18004
|
+
const save = () => {
|
|
18005
|
+
const name = panel?.kind === "naming" ? panel.name.trim() : "";
|
|
18006
|
+
if (saving || name === "" || draft.trim() === "") return;
|
|
18007
|
+
setSaving(true);
|
|
18008
|
+
savePrompt(name, draft).then((prompt) => {
|
|
18009
|
+
setPanel({
|
|
18010
|
+
kind: "saved",
|
|
18011
|
+
prompt
|
|
18012
|
+
});
|
|
18013
|
+
}, (error) => {
|
|
18014
|
+
setPanel({
|
|
18015
|
+
kind: "naming",
|
|
18016
|
+
name,
|
|
18017
|
+
touched: true,
|
|
18018
|
+
suggesting: false,
|
|
18019
|
+
failure: error instanceof PluginRequestError && error.code === "name-taken" ? t("promptSaveExists") : t("promptSaveFailed", { message: error instanceof Error ? error.message : String(error) })
|
|
18020
|
+
});
|
|
18021
|
+
}).finally(() => {
|
|
18022
|
+
setSaving(false);
|
|
18023
|
+
});
|
|
18024
|
+
};
|
|
18025
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
18026
|
+
ref: anchor,
|
|
18027
|
+
style: {
|
|
18028
|
+
position: "relative",
|
|
18029
|
+
display: "inline-flex"
|
|
18030
|
+
},
|
|
18031
|
+
children: [
|
|
18032
|
+
/* @__PURE__ */ jsx("style", {
|
|
18033
|
+
"data-dsh-claude-prompt-save-styles": true,
|
|
18034
|
+
children: promptSaveTriggerCss
|
|
18035
|
+
}),
|
|
18036
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
18037
|
+
label,
|
|
18038
|
+
side: "top",
|
|
18039
|
+
delayMs: 250,
|
|
18040
|
+
disabled: panel !== void 0,
|
|
18041
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
18042
|
+
type: "button",
|
|
18043
|
+
className: promptSaveTriggerClass,
|
|
18044
|
+
"aria-label": label,
|
|
18045
|
+
"aria-haspopup": "dialog",
|
|
18046
|
+
"aria-expanded": panel !== void 0,
|
|
18047
|
+
disabled: draft.trim() === "",
|
|
18048
|
+
onClick: () => {
|
|
18049
|
+
if (panel === void 0) open();
|
|
18050
|
+
else close();
|
|
18051
|
+
},
|
|
18052
|
+
children: /* @__PURE__ */ jsx(IconListPenOutline16, { size: 14 })
|
|
18053
|
+
})
|
|
18054
|
+
}),
|
|
18055
|
+
panel === void 0 || typeof document === "undefined" ? null : createPortal(/* @__PURE__ */ jsx("div", {
|
|
18056
|
+
ref: panelRef,
|
|
18057
|
+
style: {
|
|
18058
|
+
...promptSaveCard,
|
|
18059
|
+
...position
|
|
18060
|
+
},
|
|
18061
|
+
role: "dialog",
|
|
18062
|
+
"aria-label": label,
|
|
18063
|
+
children: panel.kind === "saved" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
18064
|
+
/* @__PURE__ */ jsx("span", { children: t("promptSaved", { name: panel.prompt.name }) }),
|
|
18065
|
+
/* @__PURE__ */ jsx("span", {
|
|
18066
|
+
style: promptSaveLocation,
|
|
18067
|
+
children: panel.prompt.location
|
|
18068
|
+
}),
|
|
18069
|
+
/* @__PURE__ */ jsx("span", {
|
|
18070
|
+
style: promptSaveActions,
|
|
18071
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
18072
|
+
variant: "primary",
|
|
18073
|
+
size: "sm",
|
|
18074
|
+
onClick: close,
|
|
18075
|
+
children: t("promptSaveDone")
|
|
18076
|
+
})
|
|
18077
|
+
})
|
|
18078
|
+
] }) : /* @__PURE__ */ jsxs("form", {
|
|
18079
|
+
style: { display: "contents" },
|
|
18080
|
+
onSubmit: (event) => {
|
|
18081
|
+
event.preventDefault();
|
|
18082
|
+
save();
|
|
18083
|
+
},
|
|
18084
|
+
children: [
|
|
18085
|
+
/* @__PURE__ */ jsx("span", {
|
|
18086
|
+
style: promptSaveHeading,
|
|
18087
|
+
children: panel.suggesting ? t("promptSaveNaming") : label
|
|
18088
|
+
}),
|
|
18089
|
+
/* @__PURE__ */ jsx("input", {
|
|
18090
|
+
className: promptSaveFieldClass,
|
|
18091
|
+
"aria-label": t("promptSaveName"),
|
|
18092
|
+
placeholder: t("promptSaveName"),
|
|
18093
|
+
value: panel.name,
|
|
18094
|
+
maxLength: 128,
|
|
18095
|
+
autoFocus: true,
|
|
18096
|
+
disabled: saving,
|
|
18097
|
+
onChange: (event) => setPanel({
|
|
18098
|
+
...panel,
|
|
18099
|
+
name: event.currentTarget.value,
|
|
18100
|
+
touched: true
|
|
18101
|
+
}),
|
|
18102
|
+
onKeyDown: (event) => {
|
|
18103
|
+
if (event.key === "Escape") close();
|
|
18104
|
+
}
|
|
18105
|
+
}),
|
|
18106
|
+
panel.failure === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
18107
|
+
style: promptSaveError,
|
|
18108
|
+
children: panel.failure
|
|
18109
|
+
}),
|
|
18110
|
+
/* @__PURE__ */ jsxs("span", {
|
|
18111
|
+
style: promptSaveActions,
|
|
18112
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
18113
|
+
variant: "ghost",
|
|
18114
|
+
size: "sm",
|
|
18115
|
+
type: "button",
|
|
18116
|
+
disabled: saving,
|
|
18117
|
+
onClick: close,
|
|
18118
|
+
children: t("promptSaveCancel")
|
|
18119
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
18120
|
+
variant: "primary",
|
|
18121
|
+
size: "sm",
|
|
18122
|
+
type: "submit",
|
|
18123
|
+
disabled: saving,
|
|
18124
|
+
children: t("promptSaveConfirm")
|
|
18125
|
+
})]
|
|
18126
|
+
})
|
|
18127
|
+
]
|
|
18128
|
+
})
|
|
18129
|
+
}), document.body)
|
|
18130
|
+
]
|
|
18131
|
+
});
|
|
18132
|
+
}
|
|
18133
|
+
//#endregion
|
|
18134
|
+
//#region src/client/ClaudePromptRefineAction.tsx
|
|
18135
|
+
/**
|
|
18136
|
+
* Rewrite the draft in place, from the composer's own tool row.
|
|
18137
|
+
*
|
|
18138
|
+
* The rewrite replaces the draft outright, which is what makes the undo state
|
|
18139
|
+
* load-bearing rather than a nicety: `SessionInput.setDraft` documents itself
|
|
18140
|
+
* as "merged into history so a seed is not an undoable step of its own", so
|
|
18141
|
+
* Ctrl/Cmd+Z does NOT bring the original back. Without somewhere to put it,
|
|
18142
|
+
* one press of this button would silently destroy a draft the user may have
|
|
18143
|
+
* spent minutes on. So the original is held for exactly as long as it is still
|
|
18144
|
+
* recoverable — until the rewrite is edited or sent — and the same button
|
|
18145
|
+
* offers it back over that window.
|
|
18146
|
+
*/
|
|
18147
|
+
function ClaudePromptRefineAction({ t, useClaudeProjection, input, replaceDraft, notify, refine = refineClaudePrompt }) {
|
|
18148
|
+
const owned = useClaudeProjection((projection) => projection.owned);
|
|
18149
|
+
const attempt = useRef(void 0);
|
|
18150
|
+
const [busy, setBusy] = useState(false);
|
|
18151
|
+
const [applied, setApplied] = useState();
|
|
18152
|
+
useEffect(() => () => {
|
|
18153
|
+
attempt.current?.abort();
|
|
18154
|
+
}, []);
|
|
18155
|
+
if (!owned || replaceDraft === void 0) return null;
|
|
18156
|
+
const draft = input?.draft ?? "";
|
|
18157
|
+
const undoable = applied !== void 0 && draft === applied.refined;
|
|
18158
|
+
const label = busy ? t("promptRefineBusy") : undoable ? t("promptRefineUndo") : t("promptRefine");
|
|
18159
|
+
const run = () => {
|
|
18160
|
+
if (busy || draft.trim() === "") return;
|
|
18161
|
+
setBusy(true);
|
|
18162
|
+
const running = new AbortController();
|
|
18163
|
+
attempt.current = running;
|
|
18164
|
+
refine(draft, running.signal).then((text) => {
|
|
18165
|
+
if (running.signal.aborted) return;
|
|
18166
|
+
setApplied({
|
|
18167
|
+
original: draft,
|
|
18168
|
+
refined: text
|
|
18169
|
+
});
|
|
18170
|
+
replaceDraft(text);
|
|
18171
|
+
}, (error) => {
|
|
18172
|
+
if (running.signal.aborted) return;
|
|
18173
|
+
notify?.("error", t("promptRefineFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
18174
|
+
}).finally(() => {
|
|
18175
|
+
setBusy(false);
|
|
18176
|
+
});
|
|
18177
|
+
};
|
|
18178
|
+
const undo = () => {
|
|
18179
|
+
if (applied === void 0) return;
|
|
18180
|
+
replaceDraft(applied.original);
|
|
18181
|
+
setApplied(void 0);
|
|
18182
|
+
};
|
|
18183
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
18184
|
+
label,
|
|
18185
|
+
side: "top",
|
|
18186
|
+
delayMs: 250,
|
|
18187
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
18188
|
+
type: "button",
|
|
18189
|
+
className: promptSaveTriggerClass,
|
|
18190
|
+
"aria-label": label,
|
|
18191
|
+
disabled: busy || !undoable && draft.trim() === "",
|
|
18192
|
+
onClick: undoable ? undo : run,
|
|
18193
|
+
children: [/* @__PURE__ */ jsx("style", {
|
|
18194
|
+
"data-dsh-claude-prompt-refine-styles": true,
|
|
18195
|
+
children: promptSaveTriggerCss
|
|
18196
|
+
}), busy ? /* @__PURE__ */ jsx(IconLoadingOutline16, {
|
|
18197
|
+
size: 14,
|
|
18198
|
+
className: promptSpinClass
|
|
18199
|
+
}) : undoable ? /* @__PURE__ */ jsx(IconRefreshOutline16, {
|
|
18200
|
+
size: 14,
|
|
18201
|
+
className: promptUndoClass
|
|
18202
|
+
}) : /* @__PURE__ */ jsx(IconSparkle16, { size: 14 })]
|
|
18203
|
+
})
|
|
18204
|
+
});
|
|
18205
|
+
}
|
|
18206
|
+
//#endregion
|
|
18207
|
+
//#region src/client/claude-prompt-source.ts
|
|
18208
|
+
/**
|
|
18209
|
+
* The user's own prompt snippets as a second `/` group.
|
|
18210
|
+
*
|
|
18211
|
+
* Unlike {@link createClaudeCommandSource}, a pick here settles as plain text:
|
|
18212
|
+
* the pipeline replaces the trigger token with the snippet body and leaves the
|
|
18213
|
+
* caret after it, so the draft stays editable and nothing is sent. A snippet is
|
|
18214
|
+
* a half-written message, not a command.
|
|
18215
|
+
*/
|
|
18216
|
+
function createClaudePromptSource(groupName, load = claudePrompts) {
|
|
18217
|
+
let known = [];
|
|
18218
|
+
return {
|
|
18219
|
+
trigger: "/",
|
|
18220
|
+
name: groupName,
|
|
18221
|
+
order: 20,
|
|
18222
|
+
async candidates(_session, request) {
|
|
18223
|
+
if (request.position !== "leading") return [];
|
|
18224
|
+
known = await load();
|
|
18225
|
+
const query = request.query.toLocaleLowerCase();
|
|
18226
|
+
return known.filter((prompt) => prompt.name.toLocaleLowerCase().includes(query)).map((prompt) => ({
|
|
18227
|
+
name: prompt.name,
|
|
18228
|
+
description: prompt.description
|
|
18229
|
+
}));
|
|
18230
|
+
},
|
|
18231
|
+
onPick(pick) {
|
|
18232
|
+
const prompt = known.find((item) => item.name === pick.candidate.name);
|
|
18233
|
+
return prompt === void 0 ? void 0 : { text: prompt.body };
|
|
18234
|
+
}
|
|
18235
|
+
};
|
|
18236
|
+
}
|
|
18237
|
+
//#endregion
|
|
17645
18238
|
//#region src/client/preset-seat-mark.ts
|
|
17646
18239
|
/** Flags the Host's agent-preset seat while it names the Claude preset.
|
|
17647
18240
|
*
|
|
@@ -17940,7 +18533,7 @@ window.__ModuleLoader__.load({
|
|
|
17940
18533
|
worktreeBranchPrefixEffect: "分支前缀修改会应用于之后自动创建的 Worktree 分支;显式指定的完整分支名不会被修改。",
|
|
17941
18534
|
maxProcessesSetting: "Claude 进程上限",
|
|
17942
18535
|
idleTimeoutSetting: "闲置回收时长(分钟)",
|
|
17943
|
-
maxProcessesEffect: "
|
|
18536
|
+
maxProcessesEffect: "保存后立即回收超额空闲进程;运行中的进程完成后再收敛。满载时,新会话会等待可用容量。留空或非法值会被拒绝。",
|
|
17944
18537
|
idleTimeoutEffect: "闲置回收时长在下一次回合结束后生效。留空或非法值会被拒绝。",
|
|
17945
18538
|
settingHint: "查看说明",
|
|
17946
18539
|
pluginUpdate: "插件更新",
|
|
@@ -18098,6 +18691,20 @@ window.__ModuleLoader__.load({
|
|
|
18098
18691
|
sessionMenu: "会话菜单",
|
|
18099
18692
|
sessionMenuOpenIn: "打开方式",
|
|
18100
18693
|
sessionMenuOpenFailed: "无法打开:{message}",
|
|
18694
|
+
promptSource: "常用提示词",
|
|
18695
|
+
promptSave: "存为常用提示词",
|
|
18696
|
+
promptSaveName: "名字",
|
|
18697
|
+
promptSaveNaming: "正在起名…",
|
|
18698
|
+
promptRefine: "AI 优化提示词",
|
|
18699
|
+
promptRefineBusy: "优化中…",
|
|
18700
|
+
promptRefineUndo: "还原成优化前的内容",
|
|
18701
|
+
promptRefineFailed: "优化失败:{message}",
|
|
18702
|
+
promptSaveConfirm: "保存",
|
|
18703
|
+
promptSaveCancel: "取消",
|
|
18704
|
+
promptSaveDone: "知道了",
|
|
18705
|
+
promptSaved: "已存为「{name}」",
|
|
18706
|
+
promptSaveExists: "已有同名提示词,换个名字。",
|
|
18707
|
+
promptSaveFailed: "保存失败:{message}",
|
|
18101
18708
|
presetHeaderHint: "当前会话运行的 Agent 预设",
|
|
18102
18709
|
diffWorkingTree: "分支改动",
|
|
18103
18710
|
diffFiles: "{count} 个已修改文件",
|
|
@@ -18350,7 +18957,7 @@ window.__ModuleLoader__.load({
|
|
|
18350
18957
|
worktreeBranchPrefixEffect: "Prefix changes apply to subsequently generated Worktree branches. Explicit full branch names remain unchanged.",
|
|
18351
18958
|
maxProcessesSetting: "Claude process limit",
|
|
18352
18959
|
idleTimeoutSetting: "Idle timeout (minutes)",
|
|
18353
|
-
maxProcessesEffect: "
|
|
18960
|
+
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
18961
|
idleTimeoutEffect: "The idle timeout applies after the next completed turn. Blank or invalid values are rejected.",
|
|
18355
18962
|
settingHint: "Show details",
|
|
18356
18963
|
pluginUpdate: "Plugin updates",
|
|
@@ -18508,6 +19115,20 @@ window.__ModuleLoader__.load({
|
|
|
18508
19115
|
sessionMenu: "Session menu",
|
|
18509
19116
|
sessionMenuOpenIn: "Open in",
|
|
18510
19117
|
sessionMenuOpenFailed: "Could not open: {message}",
|
|
19118
|
+
promptSource: "Prompts",
|
|
19119
|
+
promptSave: "Save as a prompt",
|
|
19120
|
+
promptSaveName: "Name",
|
|
19121
|
+
promptSaveNaming: "Naming it…",
|
|
19122
|
+
promptRefine: "Rewrite with AI",
|
|
19123
|
+
promptRefineBusy: "Rewriting…",
|
|
19124
|
+
promptRefineUndo: "Put the original back",
|
|
19125
|
+
promptRefineFailed: "Could not rewrite: {message}",
|
|
19126
|
+
promptSaveConfirm: "Save",
|
|
19127
|
+
promptSaveCancel: "Cancel",
|
|
19128
|
+
promptSaveDone: "Got it",
|
|
19129
|
+
promptSaved: "Saved as \"{name}\"",
|
|
19130
|
+
promptSaveExists: "A prompt with that name already exists. Try another.",
|
|
19131
|
+
promptSaveFailed: "Could not save: {message}",
|
|
18511
19132
|
presetHeaderHint: "The agent preset this session runs",
|
|
18512
19133
|
diffWorkingTree: "Branch changes",
|
|
18513
19134
|
diffFiles: "{count} modified file(s)",
|
|
@@ -18785,6 +19406,7 @@ window.__ModuleLoader__.load({
|
|
|
18785
19406
|
diagnostics.report(kind, detail);
|
|
18786
19407
|
} });
|
|
18787
19408
|
ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(ctx, projections)), "dsh-claude: Claude slash source");
|
|
19409
|
+
ctx.effect(() => ctx.inputTriggers.registerSource(createClaudePromptSource(t("promptSource"))), "dsh-claude: Claude prompt source");
|
|
18788
19410
|
const sessions = ctx.get("sessions");
|
|
18789
19411
|
const workspaces = ctx.get("workspaces");
|
|
18790
19412
|
const uiWorkspace = ctx.get("uiWorkspace");
|
|
@@ -19131,6 +19753,34 @@ window.__ModuleLoader__.load({
|
|
|
19131
19753
|
locale: namespace,
|
|
19132
19754
|
inject: () => ({ t })
|
|
19133
19755
|
}, ClaudeSessionMenu));
|
|
19756
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
19757
|
+
name: "conversation.input.left",
|
|
19758
|
+
id: "claude-prompt-save",
|
|
19759
|
+
order: 40,
|
|
19760
|
+
locale: namespace,
|
|
19761
|
+
inject: () => ({ t })
|
|
19762
|
+
}, ClaudePromptSaveAction));
|
|
19763
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
19764
|
+
name: "conversation.input.left",
|
|
19765
|
+
id: "claude-prompt-refine",
|
|
19766
|
+
order: 41,
|
|
19767
|
+
locale: namespace,
|
|
19768
|
+
inject: (sessionId) => {
|
|
19769
|
+
if (sessions === void 0 || conversation === void 0) return { t };
|
|
19770
|
+
const scope = sessions.scope(sessionId);
|
|
19771
|
+
if (scope === void 0) return { t };
|
|
19772
|
+
const facade = sessionInput(conversation, scope);
|
|
19773
|
+
return {
|
|
19774
|
+
t,
|
|
19775
|
+
replaceDraft: (text) => {
|
|
19776
|
+
facade.setDraft(text);
|
|
19777
|
+
},
|
|
19778
|
+
notify: (level, text) => {
|
|
19779
|
+
facade.notify(level, text);
|
|
19780
|
+
}
|
|
19781
|
+
};
|
|
19782
|
+
}
|
|
19783
|
+
}, ClaudePromptRefineAction));
|
|
19134
19784
|
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
19135
19785
|
name: "conversation.input.dock",
|
|
19136
19786
|
id: "claude-review-comments",
|