@deepseek-harness-tui/dsh-tui 0.5.1 → 0.5.3

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.
Files changed (43) hide show
  1. package/README.md +6 -0
  2. package/cordis.patch.yml +5 -1
  3. package/lib/types/approvals.d.ts +80 -0
  4. package/lib/types/approvals.d.ts.map +1 -0
  5. package/lib/types/approvals.js +170 -0
  6. package/lib/types/commands.d.ts +14 -1
  7. package/lib/types/commands.d.ts.map +1 -1
  8. package/lib/types/commands.js +12 -0
  9. package/lib/types/compat/sessionLog.d.ts.map +1 -1
  10. package/lib/types/compat/sessionLog.js +52 -37
  11. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  12. package/lib/types/components/CommandSuggestions.js +5 -3
  13. package/lib/types/components/HelpMenu.d.ts +1 -0
  14. package/lib/types/components/HelpMenu.d.ts.map +1 -1
  15. package/lib/types/components/HelpMenu.js +4 -1
  16. package/lib/types/components/PromptInput.d.ts.map +1 -1
  17. package/lib/types/components/PromptInput.js +16 -15
  18. package/lib/types/components/approvals/ApprovalPanel.d.ts +21 -0
  19. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -0
  20. package/lib/types/components/approvals/ApprovalPanel.js +48 -0
  21. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +6 -0
  22. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  23. package/lib/types/components/questions/AskUserQuestionPanel.js +9 -2
  24. package/lib/types/components/questions/PlanReviewPanel.d.ts +39 -0
  25. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -0
  26. package/lib/types/components/questions/PlanReviewPanel.js +168 -0
  27. package/lib/types/i18n.d.ts +174 -2
  28. package/lib/types/i18n.d.ts.map +1 -1
  29. package/lib/types/i18n.js +82 -1
  30. package/lib/types/ink/stringWidth.d.ts.map +1 -1
  31. package/lib/types/ink/stringWidth.js +61 -0
  32. package/lib/types/plugin.d.ts.map +1 -1
  33. package/lib/types/plugin.js +15 -0
  34. package/lib/types/questions.d.ts +3 -0
  35. package/lib/types/questions.d.ts.map +1 -1
  36. package/lib/types/questions.js +13 -1
  37. package/lib/types/screens/Chat.d.ts +8 -13
  38. package/lib/types/screens/Chat.d.ts.map +1 -1
  39. package/lib/types/screens/Chat.js +33 -18
  40. package/lib/types/utils/modifiers.d.ts +19 -0
  41. package/lib/types/utils/modifiers.d.ts.map +1 -0
  42. package/lib/types/utils/modifiers.js +17 -0
  43. package/package.json +2 -1
@@ -8,6 +8,7 @@ import { formatClipboardInsert, readClipboard } from '../utils/clipboard.js';
8
8
  import { filterCommands, parseCommandName } from '../commands.js';
9
9
  import { appendHistory } from '../history.js';
10
10
  import { mentionAtCaret } from '../utils/mentions.js';
11
+ import { isMod } from '../utils/modifiers.js';
11
12
  import { CommandSuggestions } from './CommandSuggestions.js';
12
13
  import { FileSuggestions } from './FileSuggestions.js';
13
14
  import { HelpMenu } from './HelpMenu.js';
@@ -347,10 +348,10 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
347
348
  insertAtCaret(input.replace(/\r\n/g, '\n').replace(/\r/g, '\n'));
348
349
  return;
349
350
  }
350
- // Ctrl+V: raw mode hands the key to the app, so the clipboard is read
351
- // here — text, or file paths when Explorer copied files (pasted files
352
- // insert their paths).
353
- if (key.ctrl && input === 'v') {
351
+ // Ctrl+V / Cmd+V: raw mode hands the key to the app, so the clipboard is
352
+ // read here — text, or file paths when Explorer copied files (pasted
353
+ // files insert their paths).
354
+ if (isMod(key) && input === 'v') {
354
355
  if (clipboardBusyRef.current)
355
356
  return;
356
357
  clipboardBusyRef.current = true;
@@ -425,9 +426,9 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
425
426
  submitText(line);
426
427
  return;
427
428
  }
428
- if (key.return && key.ctrl) {
429
- // Ctrl+Enter: interrupt the running turn and process this message
430
- // immediately (Windows Terminal sends CSI 13;5u / 13;1;5u).
429
+ if (key.return && isMod(key)) {
430
+ // Ctrl+Enter / Cmd+Enter: interrupt the running turn and process this
431
+ // message immediately (Windows Terminal sends CSI 13;5u / 13;1;5u).
431
432
  interruptSend();
432
433
  return;
433
434
  }
@@ -537,12 +538,12 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
537
538
  setCursor(previous => Math.min(value.length, previous + 1));
538
539
  return;
539
540
  }
540
- if (key.ctrl && key.leftArrow) {
541
+ if (isMod(key) && key.leftArrow) {
541
542
  // Jump to the previous word boundary (readline alt+b).
542
543
  setCursor(previous => wordBoundaryLeft(value, previous));
543
544
  return;
544
545
  }
545
- if (key.ctrl && key.rightArrow) {
546
+ if (isMod(key) && key.rightArrow) {
546
547
  // Jump to the next word boundary (readline alt+f).
547
548
  setCursor(previous => wordBoundaryRight(value, previous));
548
549
  return;
@@ -572,31 +573,31 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
572
573
  setCursor(nextLine === -1 ? value.length : nextLine);
573
574
  return;
574
575
  }
575
- if (key.ctrl && input === 'a') {
576
+ if (isMod(key) && input === 'a') {
576
577
  const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
577
578
  setCursor(lineStart);
578
579
  return;
579
580
  }
580
- if (key.ctrl && input === 'e') {
581
+ if (isMod(key) && input === 'e') {
581
582
  const nextLine = value.indexOf('\n', cursor);
582
583
  setCursor(nextLine === -1 ? value.length : nextLine);
583
584
  return;
584
585
  }
585
- if (key.ctrl && input === 'u') {
586
+ if (isMod(key) && input === 'u') {
586
587
  // Delete to start of line.
587
588
  const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
588
589
  setValue(value.slice(0, lineStart) + value.slice(cursor));
589
590
  setCursor(lineStart);
590
591
  return;
591
592
  }
592
- if (key.ctrl && input === 'k') {
593
+ if (isMod(key) && input === 'k') {
593
594
  // Delete to end of line.
594
595
  const nextLine = value.indexOf('\n', cursor);
595
596
  const end = nextLine === -1 ? value.length : nextLine;
596
597
  setValue(value.slice(0, cursor) + value.slice(end));
597
598
  return;
598
599
  }
599
- if (key.ctrl && input === 'w') {
600
+ if (isMod(key) && input === 'w') {
600
601
  // Delete the word before the cursor (CC/readline behavior): skip
601
602
  // trailing whitespace, then the whitespace-delimited word.
602
603
  const before = value.slice(0, cursor);
@@ -678,7 +679,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
678
679
  onToggleHelp();
679
680
  return;
680
681
  }
681
- if (input && !key.ctrl && !key.meta && !key.tab && !key.escape) {
682
+ if (input && !key.ctrl && !key.meta && !key.super && !key.tab && !key.escape) {
682
683
  // Typing anything else dismisses the help menu (CC behavior).
683
684
  if (helpOpen)
684
685
  onToggleHelp();
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The approval panel — Claude Code style permission prompt for the DSH
3
+ * approval seam (`ctx.approval`). One ask per panel: a permission-colored
4
+ * divider header naming the tool, the gated command recovered from the
5
+ * paired tool call (CC verbose full-command semantics), the asker's reason,
6
+ * "Do you want to proceed?", and a numbered Yes/No list.
7
+ *
8
+ * The protocol's outcome set is closed (allowed-once / rejected /
9
+ * cancelled / unavailable) with no allow-always or feedback channel, so
10
+ * the panel deliberately offers exactly two rows; Esc and Ctrl+C reject
11
+ * (fail closed, CC's "Esc to cancel" semantics).
12
+ */
13
+ import React from 'react';
14
+ import type { ApprovalSnapshot } from '../../approvals.js';
15
+ export type ApprovalPanelProps = {
16
+ /** The approval to render (from the ApprovalStore snapshot). */
17
+ readonly approval: ApprovalSnapshot;
18
+ readonly onDecide: (outcome: 'allowed-once' | 'rejected') => void;
19
+ };
20
+ export declare function ApprovalPanel({ approval, onDecide }: ApprovalPanelProps): React.ReactNode;
21
+ //# sourceMappingURL=ApprovalPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApprovalPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/approvals/ApprovalPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,KAAK,IAAI,CAAA;CAClE,CAAA;AAID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAmEzF"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The approval panel — Claude Code style permission prompt for the DSH
4
+ * approval seam (`ctx.approval`). One ask per panel: a permission-colored
5
+ * divider header naming the tool, the gated command recovered from the
6
+ * paired tool call (CC verbose full-command semantics), the asker's reason,
7
+ * "Do you want to proceed?", and a numbered Yes/No list.
8
+ *
9
+ * The protocol's outcome set is closed (allowed-once / rejected /
10
+ * cancelled / unavailable) with no allow-always or feedback channel, so
11
+ * the panel deliberately offers exactly two rows; Esc and Ctrl+C reject
12
+ * (fail closed, CC's "Esc to cancel" semantics).
13
+ */
14
+ import React from 'react';
15
+ import { t } from '../../i18n.js';
16
+ import { Box, Text, useInput } from '../../ui.js';
17
+ import { Divider } from '../design-system/Divider.js';
18
+ import { POINTER } from '../../cc/figures.js';
19
+ const OUTCOMES = ['allowed-once', 'rejected'];
20
+ export function ApprovalPanel({ approval, onDecide }) {
21
+ const [focusIndex, setFocusIndex] = React.useState(0);
22
+ useInput((input, key) => {
23
+ if (key.escape || (key.ctrl && input === 'c')) {
24
+ onDecide('rejected');
25
+ return;
26
+ }
27
+ if (key.upArrow) {
28
+ setFocusIndex(index => (index + OUTCOMES.length - 1) % OUTCOMES.length);
29
+ return;
30
+ }
31
+ if (key.downArrow) {
32
+ setFocusIndex(index => (index + 1) % OUTCOMES.length);
33
+ return;
34
+ }
35
+ if (input === '1' || input === '2') {
36
+ onDecide(OUTCOMES[Number(input) - 1]);
37
+ return;
38
+ }
39
+ if (key.return) {
40
+ onDecide(OUTCOMES[focusIndex]);
41
+ }
42
+ }, { isActive: true });
43
+ const optionLabels = [t('approval-yes'), t('approval-no')];
44
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: t('approval-waiting', { tool: approval.toolName }), padding: 4 }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [approval.command !== undefined && (_jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: approval.command }) })), approval.reason !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: approval.reason })), _jsx(Text, { dimColor: true, children: t('approval-proceed') })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: optionLabels.map((label, index) => {
45
+ const focused = index === focusIndex;
46
+ return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Text, { bold: focused, color: focused ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", label] })] }, label));
47
+ }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('approval-hint') }) })] }));
48
+ }
@@ -24,6 +24,12 @@ export type AskUserQuestionPanelProps = {
24
24
  readonly description?: string;
25
25
  }>;
26
26
  readonly multiSelect?: boolean;
27
+ /** Presentation intent tag (rc.6): 'plan-review' switches to the
28
+ * decision-card layout; an intent never changes the protocol. */
29
+ readonly intent?: {
30
+ readonly kind: 'plan-review';
31
+ readonly approve: string;
32
+ };
27
33
  };
28
34
  /** 1-based position within the batch (progress header). */
29
35
  readonly position: number;
@@ -1 +1 @@
1
- {"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAC/B,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CA8S7C"}
1
+ {"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAO3D,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;QAC9B;0EACkE;QAClE,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAoT7C"}
@@ -17,10 +17,17 @@ import { t } from '../../i18n.js';
17
17
  import { Box, Text, useInput } from '../../ui.js';
18
18
  import { Divider } from '../design-system/Divider.js';
19
19
  import { POINTER } from '../../cc/figures.js';
20
+ import { PlanReviewPanel } from './PlanReviewPanel.js';
20
21
  const CHECKED = '◉';
21
22
  const UNCHECKED = '○';
22
23
  const PENCIL = '✎';
23
24
  export function AskUserQuestionPanel({ question, position, total, answered, onAnswer, onCancel, }) {
25
+ // Plan-mode's exit_plan_mode ask carries a presentation intent: render
26
+ // the CC-style decision card instead of the generic questionnaire. The
27
+ // branch precedes every hook so hook order stays stable per remount key.
28
+ if (question.intent?.kind === 'plan-review') {
29
+ return _jsx(PlanReviewPanel, { question: question, onAnswer: onAnswer, onCancel: onCancel });
30
+ }
24
31
  const options = question.options ?? [];
25
32
  const multiSelect = question.multiSelect === true;
26
33
  /** Rows: the real options plus the inline input row at the tail. */
@@ -147,7 +154,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, onAn
147
154
  setCustomCursor(customText.length);
148
155
  return;
149
156
  }
150
- if (!key.ctrl && !key.meta && input) {
157
+ if (!key.ctrl && !key.meta && !key.super && input) {
151
158
  setCustomText(text => text.slice(0, customCursor) + input + text.slice(customCursor));
152
159
  setCustomCursor(cursor => cursor + input.length);
153
160
  setError(null);
@@ -191,7 +198,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, onAn
191
198
  }
192
199
  // Typing on an option appends into the input row; single-select also
193
200
  // attaches this option's label so Enter carries label + text (#9).
194
- if (!key.ctrl && !key.meta && input) {
201
+ if (!key.ctrl && !key.meta && !key.super && input) {
195
202
  appendText(input);
196
203
  if (!multiSelect)
197
204
  setAttached(options[focusIndex]?.label ?? null);
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The plan-review panel — Claude Code style exit-plan-mode decision card
3
+ * for the DSH user-interaction seam. plan-mode's `exit_plan_mode` tool asks
4
+ * through `ctx.userQuestions` with `intent: { kind: 'plan-review',
5
+ * approve }`: the plan markdown arrives in `detail`, the approve/decline
6
+ * choices in `options` (labels verbatim — the protocol answers with the
7
+ * asker's own labels).
8
+ *
9
+ * Protocol-exact answer mapping (dsh-plan-mode):
10
+ * - Approve: `{ selected: [intent.approve] }` — custom MUST be absent, or
11
+ * plan-mode treats it as keep-planning-with-feedback.
12
+ * - Keep planning / feedback: `{ selected: [declineLabel], custom? }` where
13
+ * declineLabel is the first option that is not the approve label.
14
+ * - Esc / Ctrl+C: the store rejects with ASK_CANCELLED, which plan-mode
15
+ * reads as "the user dismissed the review to speak instead".
16
+ */
17
+ import React from 'react';
18
+ import type { QuestionSelection } from '../../questions.js';
19
+ export type PlanReviewPanelProps = {
20
+ /** The plan-review question (intent.kind === 'plan-review'). */
21
+ readonly question: {
22
+ readonly question: string;
23
+ readonly header?: string;
24
+ readonly detail?: string;
25
+ readonly options?: ReadonlyArray<{
26
+ readonly label: string;
27
+ readonly description?: string;
28
+ }>;
29
+ readonly intent?: {
30
+ readonly kind: 'plan-review';
31
+ readonly approve: string;
32
+ };
33
+ };
34
+ readonly onAnswer: (selection: QuestionSelection) => void;
35
+ /** Esc / Ctrl+C — dismissed to speak instead (ASK_CANCELLED). */
36
+ readonly onCancel: () => void;
37
+ };
38
+ export declare function PlanReviewPanel({ question, onAnswer, onCancel, }: PlanReviewPanelProps): React.ReactNode;
39
+ //# sourceMappingURL=PlanReviewPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanReviewPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/PlanReviewPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAI3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,GAAG,KAAK,CAAC,SAAS,CAgOxC"}
@@ -0,0 +1,168 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The plan-review panel — Claude Code style exit-plan-mode decision card
4
+ * for the DSH user-interaction seam. plan-mode's `exit_plan_mode` tool asks
5
+ * through `ctx.userQuestions` with `intent: { kind: 'plan-review',
6
+ * approve }`: the plan markdown arrives in `detail`, the approve/decline
7
+ * choices in `options` (labels verbatim — the protocol answers with the
8
+ * asker's own labels).
9
+ *
10
+ * Protocol-exact answer mapping (dsh-plan-mode):
11
+ * - Approve: `{ selected: [intent.approve] }` — custom MUST be absent, or
12
+ * plan-mode treats it as keep-planning-with-feedback.
13
+ * - Keep planning / feedback: `{ selected: [declineLabel], custom? }` where
14
+ * declineLabel is the first option that is not the approve label.
15
+ * - Esc / Ctrl+C: the store rejects with ASK_CANCELLED, which plan-mode
16
+ * reads as "the user dismissed the review to speak instead".
17
+ */
18
+ import React from 'react';
19
+ import { t } from '../../i18n.js';
20
+ import { Box, Text, useInput } from '../../ui.js';
21
+ import { Divider } from '../design-system/Divider.js';
22
+ import { Markdown } from '../Markdown.js';
23
+ import { POINTER } from '../../cc/figures.js';
24
+ const PENCIL = '✎';
25
+ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
26
+ const options = question.options ?? [];
27
+ const approveLabel = question.intent?.approve ?? options[0]?.label;
28
+ const declineLabel = options.find(option => option.label !== approveLabel)?.label;
29
+ /** Rows: the asker's options plus the feedback input row at the tail. */
30
+ const rowCount = options.length + 1;
31
+ const [focusIndex, setFocusIndex] = React.useState(0);
32
+ const [feedback, setFeedback] = React.useState('');
33
+ const [cursor, setCursor] = React.useState(0);
34
+ const [error, setError] = React.useState(null);
35
+ const inputFocused = focusIndex === options.length;
36
+ const moveFocus = (delta) => {
37
+ setFocusIndex(index => (index + delta + rowCount) % rowCount);
38
+ setError(null);
39
+ };
40
+ /** Typing anywhere appends to the feedback buffer and focuses the input
41
+ * row — plan review has no "attach" semantics: approve must be clean. */
42
+ const appendFeedback = (text) => {
43
+ setFeedback(previous => previous + text);
44
+ setCursor(previous => previous + text.length);
45
+ setFocusIndex(options.length);
46
+ setError(null);
47
+ };
48
+ const backspaceFeedback = () => {
49
+ if (cursor <= 0)
50
+ return;
51
+ setFeedback(previous => previous.slice(0, cursor - 1) + previous.slice(cursor));
52
+ setCursor(previous => previous - 1);
53
+ };
54
+ /** The decline answer: the other option's label when the asker named one,
55
+ * else an empty selection (plan-mode reads any non-approve as decline). */
56
+ const declineSelected = () => declineLabel !== undefined ? [declineLabel] : [];
57
+ /** Enter on an option row. Approve with feedback in the buffer is an
58
+ * error — the protocol would silently read it as keep-planning. */
59
+ const submitOption = (index) => {
60
+ const label = options[index]?.label;
61
+ if (label === undefined)
62
+ return;
63
+ const text = feedback.trim();
64
+ if (label === approveLabel && text !== '') {
65
+ setError(t('plan-review-approve-needs-empty'));
66
+ return;
67
+ }
68
+ if (label === approveLabel) {
69
+ onAnswer({ selected: [label] });
70
+ return;
71
+ }
72
+ onAnswer({ selected: [label], ...(text !== '' ? { custom: text } : {}) });
73
+ };
74
+ /** Enter on the feedback row: text routes to keep-planning-with-feedback;
75
+ * empty is a plain keep-planning. */
76
+ const submitFeedback = () => {
77
+ const text = feedback.trim();
78
+ onAnswer({ selected: declineSelected(), ...(text !== '' ? { custom: text } : {}) });
79
+ };
80
+ useInput((input, key) => {
81
+ if (key.escape || (key.ctrl && input === 'c')) {
82
+ onCancel();
83
+ return;
84
+ }
85
+ if (inputFocused) {
86
+ if (key.upArrow) {
87
+ moveFocus(-1);
88
+ return;
89
+ }
90
+ if (key.downArrow) {
91
+ moveFocus(1);
92
+ return;
93
+ }
94
+ if (key.return) {
95
+ submitFeedback();
96
+ return;
97
+ }
98
+ if (key.backspace) {
99
+ backspaceFeedback();
100
+ return;
101
+ }
102
+ if (key.delete) {
103
+ if (cursor < feedback.length) {
104
+ setFeedback(text => text.slice(0, cursor) + text.slice(cursor + 1));
105
+ }
106
+ return;
107
+ }
108
+ if (key.leftArrow) {
109
+ setCursor(value => Math.max(0, value - 1));
110
+ return;
111
+ }
112
+ if (key.rightArrow) {
113
+ setCursor(value => Math.min(feedback.length, value + 1));
114
+ return;
115
+ }
116
+ if (key.home) {
117
+ setCursor(0);
118
+ return;
119
+ }
120
+ if (key.end) {
121
+ setCursor(feedback.length);
122
+ return;
123
+ }
124
+ if (!key.ctrl && !key.meta && input) {
125
+ setFeedback(text => text.slice(0, cursor) + input + text.slice(cursor));
126
+ setCursor(value => value + input.length);
127
+ setError(null);
128
+ }
129
+ return;
130
+ }
131
+ // An option row.
132
+ if (key.upArrow) {
133
+ moveFocus(-1);
134
+ return;
135
+ }
136
+ if (key.downArrow) {
137
+ moveFocus(1);
138
+ return;
139
+ }
140
+ if (key.return) {
141
+ submitOption(focusIndex);
142
+ return;
143
+ }
144
+ if (key.backspace) {
145
+ if (feedback !== '')
146
+ backspaceFeedback();
147
+ return;
148
+ }
149
+ if (!key.ctrl && !key.meta && input) {
150
+ // Number quick-pick submits the option outright — but only with an
151
+ // empty buffer; with feedback pending, digits are feedback chars.
152
+ const digit = /^[1-9]$/.test(input) ? Number(input) : 0;
153
+ if (feedback === '' && digit >= 1 && digit <= options.length) {
154
+ submitOption(digit - 1);
155
+ return;
156
+ }
157
+ appendFeedback(input);
158
+ }
159
+ }, { isActive: true });
160
+ const cursorChar = cursor < feedback.length ? feedback[cursor] : ' ';
161
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: ` ${question.header ?? t('plan-review-fallback-header')} `, padding: 4 }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, wrap: "wrap", children: question.question }), question.detail !== undefined && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Markdown, { children: question.detail }) }))] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.map((option, index) => {
162
+ const focused = index === focusIndex;
163
+ const isApprove = option.label === approveLabel;
164
+ return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { bold: focused, color: focused || isApprove ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", option.label] }), option.description !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: option.description }))] })] }, option.label));
165
+ }), _jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : 'suggestion', children: PENCIL }) }), _jsx(Box, { flexDirection: "row", marginLeft: 1, children: feedback === '' && !inputFocused ? (_jsx(Text, { dimColor: true, children: t('plan-review-feedback-placeholder') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: feedback.slice(0, cursor) }), inputFocused
166
+ ? _jsx(Text, { inverse: true, children: cursorChar })
167
+ : _jsx(Text, { color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: feedback.slice(inputFocused ? cursor + 1 : cursor) })] })) })] })] }), error !== null && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "error", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('plan-review-hint') }) })] }));
168
+ }
@@ -584,8 +584,8 @@ declare const dict: {
584
584
  readonly en: "Recommended: Windows Terminal (≥110 columns, monospace, TrueColor).";
585
585
  };
586
586
  readonly 'terminal-paste-hint': {
587
- readonly zh: "Ctrl+V 粘贴文本/文件路径;Ctrl+Shift+V 终端原生粘贴;右键粘贴同样可用。";
588
- readonly en: "Ctrl+V pastes text/file paths; Ctrl+Shift+V is native terminal paste; right-click paste also works.";
587
+ readonly zh: "{{mod}}V 粘贴文本/文件路径;Ctrl+Shift+V 终端原生粘贴;右键粘贴同样可用。";
588
+ readonly en: "{{mod}}V pastes text/file paths; Ctrl+Shift+V is native terminal paste; right-click paste also works.";
589
589
  };
590
590
  readonly 'connect-none': {
591
591
  readonly zh: "DSH 暂无远程连接机制(CC 的 /connect 对应能力未适配)。";
@@ -855,6 +855,168 @@ declare const dict: {
855
855
  readonly zh: "直接输入…";
856
856
  readonly en: "Type directly…";
857
857
  };
858
+ readonly 'approval-waiting': {
859
+ readonly zh: " ⏳ 等待审批 · {{tool}} ";
860
+ readonly en: " Awaiting approval · {{tool}} ";
861
+ };
862
+ readonly 'approval-proceed': {
863
+ readonly zh: "要允许这次操作吗?";
864
+ readonly en: "Do you want to proceed?";
865
+ };
866
+ readonly 'approval-yes': {
867
+ readonly zh: "允许(仅本次)";
868
+ readonly en: "Yes, allow once";
869
+ };
870
+ readonly 'approval-no': {
871
+ readonly zh: "拒绝";
872
+ readonly en: "No";
873
+ };
874
+ readonly 'approval-hint': {
875
+ readonly zh: "↑/↓ 选择 · Enter 确认 · Esc 拒绝";
876
+ readonly en: "↑/↓ select · Enter confirm · Esc reject";
877
+ };
878
+ readonly 'plan-review-fallback-header': {
879
+ readonly zh: "计划评审";
880
+ readonly en: "Plan review";
881
+ };
882
+ readonly 'plan-review-feedback-placeholder': {
883
+ readonly zh: "输入反馈,告诉模型要改什么…";
884
+ readonly en: "Tell the model what to change…";
885
+ };
886
+ readonly 'plan-review-approve-needs-empty': {
887
+ readonly zh: "请先清空反馈再批准(或在输入行回车提交反馈)";
888
+ readonly en: "Clear the feedback to approve (or press Enter on the input row to send it)";
889
+ };
890
+ readonly 'plan-review-hint': {
891
+ readonly zh: "↑/↓ 选择 · 1/2 快选 · 打字输入反馈 · Enter 提交 · Esc 打断评审";
892
+ readonly en: "↑/↓ select · 1/2 quick-pick · type feedback · Enter submit · Esc dismiss";
893
+ };
894
+ readonly 'cmd-desc-new': {
895
+ readonly zh: "新开会话";
896
+ };
897
+ readonly 'cmd-desc-clear': {
898
+ readonly zh: "清空当前会话";
899
+ };
900
+ readonly 'cmd-desc-compact': {
901
+ readonly zh: "压缩会话历史";
902
+ };
903
+ readonly 'cmd-desc-resume': {
904
+ readonly zh: "恢复历史会话";
905
+ };
906
+ readonly 'cmd-desc-rewind': {
907
+ readonly zh: "回退会话到历史消息";
908
+ };
909
+ readonly 'cmd-desc-export': {
910
+ readonly zh: "导出会话为 Markdown 文件";
911
+ };
912
+ readonly 'cmd-desc-status': {
913
+ readonly zh: "查看会话状态";
914
+ };
915
+ readonly 'cmd-desc-cost': {
916
+ readonly zh: "查看会话 token 用量";
917
+ };
918
+ readonly 'cmd-desc-config': {
919
+ readonly zh: "查看 dsh-cc 配置来源";
920
+ };
921
+ readonly 'cmd-desc-doctor': {
922
+ readonly zh: "运行环境检查";
923
+ };
924
+ readonly 'cmd-desc-init': {
925
+ readonly zh: "在工作目录创建 AGENTS.md";
926
+ };
927
+ readonly 'cmd-desc-agents': {
928
+ readonly zh: "查看本会话的子代理";
929
+ };
930
+ readonly 'cmd-desc-activity': {
931
+ readonly zh: "切换工作状态指示器预设";
932
+ };
933
+ readonly 'cmd-desc-preset': {
934
+ readonly zh: "切换 Agent 预设(standard/code/minimal/cordis)";
935
+ };
936
+ readonly 'cmd-desc-theme': {
937
+ readonly zh: "切换配色主题(内置或自定义)";
938
+ };
939
+ readonly 'cmd-desc-lang': {
940
+ readonly zh: "切换界面语言(en / zh)";
941
+ };
942
+ readonly 'cmd-desc-model': {
943
+ readonly zh: "查看当前模型";
944
+ };
945
+ readonly 'cmd-desc-thinking': {
946
+ readonly zh: "切换扩展思考显示";
947
+ };
948
+ readonly 'cmd-desc-tokens': {
949
+ readonly zh: "查看会话 token 用量";
950
+ };
951
+ readonly 'cmd-desc-login': {
952
+ readonly zh: "查看 API 凭证状态";
953
+ };
954
+ readonly 'cmd-desc-logout': {
955
+ readonly zh: "清除 API 凭证";
956
+ };
957
+ readonly 'cmd-desc-permissions': {
958
+ readonly zh: "查看权限策略状态";
959
+ };
960
+ readonly 'cmd-desc-add-dir': {
961
+ readonly zh: "查看文件系统策略范围";
962
+ };
963
+ readonly 'cmd-desc-hooks': {
964
+ readonly zh: "查看 hooks 状态";
965
+ };
966
+ readonly 'cmd-desc-mcp': {
967
+ readonly zh: "查看 MCP 状态";
968
+ };
969
+ readonly 'cmd-desc-memory': {
970
+ readonly zh: "查看记忆状态";
971
+ };
972
+ readonly 'cmd-desc-update': {
973
+ readonly zh: "更新 dsh-cc-tui 并重启";
974
+ };
975
+ readonly 'cmd-desc-audit': {
976
+ readonly zh: "对当前项目做全面代码审计";
977
+ };
978
+ readonly 'cmd-desc-bug': {
979
+ readonly zh: "记录一份 bug 报告";
980
+ };
981
+ readonly 'cmd-desc-practice': {
982
+ readonly zh: "与 dsh-cc 进行编程练习";
983
+ };
984
+ readonly 'cmd-desc-review': {
985
+ readonly zh: "对当前项目做全面代码评审";
986
+ };
987
+ readonly 'cmd-desc-pr_comments': {
988
+ readonly zh: "审查拉取请求评论";
989
+ };
990
+ readonly 'cmd-desc-release-notes': {
991
+ readonly zh: "生成发布说明";
992
+ };
993
+ readonly 'cmd-desc-vuln-check': {
994
+ readonly zh: "运行安全漏洞检查";
995
+ };
996
+ readonly 'cmd-desc-vim': {
997
+ readonly zh: "切换 vim 模式";
998
+ };
999
+ readonly 'cmd-desc-terminal-setup': {
1000
+ readonly zh: "查看终端配置建议";
1001
+ };
1002
+ readonly 'cmd-desc-connect': {
1003
+ readonly zh: "连接远程机器";
1004
+ };
1005
+ readonly 'cmd-desc-help': {
1006
+ readonly zh: "查看快捷键与命令";
1007
+ };
1008
+ readonly 'cmd-desc-exit': {
1009
+ readonly zh: "退出 dsh-tui";
1010
+ };
1011
+ readonly 'cmd-desc-plan': {
1012
+ readonly zh: "切换计划模式(/plan off 退出)";
1013
+ };
1014
+ readonly 'cmd-desc-goal': {
1015
+ readonly zh: "设置或查看会话目标";
1016
+ };
1017
+ readonly 'cmd-desc-feedback': {
1018
+ readonly zh: "提交使用反馈";
1019
+ };
858
1020
  readonly 'lang-current': {
859
1021
  readonly zh: "当前语言 {{lang}}";
860
1022
  readonly en: "Current language {{lang}}";
@@ -900,6 +1062,16 @@ export declare function isLang(value: unknown): value is Lang;
900
1062
  * @param params - Placeholder values.
901
1063
  */
902
1064
  export declare function t(key: I18nKey, params?: I18nParams): string;
1065
+ /**
1066
+ * Translate a runtime-computed key (e.g. `cmd-desc-${name}`), falling back
1067
+ * to the given text when the key is missing or has no entry in the active
1068
+ * language — unlike {@link t}, which renders the key itself. Used where the
1069
+ * fallback holds the authoritative text (command descriptions: the en copy
1070
+ * lives in `LOCAL_COMMANDS` / the DSH registry, the dict carries zh only).
1071
+ * @param key - Dictionary key, computed at runtime so it is not type-checked.
1072
+ * @param fallback - Text used when no translation exists.
1073
+ */
1074
+ export declare function tOr(key: string, fallback: string): string;
903
1075
  /**
904
1076
  * Parse a persisted `{ lang }` value; anything else yields undefined.
905
1077
  * @param text - Raw file contents.
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyPA,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAOxD,uEAAuE;AACvE,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAG1B,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAG5D;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAGxC;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAS5D;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAkB,GAAG,IAAI,GAAG,SAAS,CAMtE;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,GAAE,MAAkB,GAAG,OAAO,CAQ1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAUvC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}