create-principles-disciple 1.89.0 → 1.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/console/dist/ui/api.d.ts +2 -1
  2. package/console/dist/ui/api.js +7 -1
  3. package/console/dist/ui/i18n/en.json +18 -1
  4. package/console/dist/ui/i18n/zh-CN.json +18 -1
  5. package/console/dist/ui/pages/focus/FocusPage.js +48 -4
  6. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +70 -2
  7. package/console/dist/web/assets/app.js +1541 -1223
  8. package/console/package.json +2 -2
  9. package/core/dist/prompt-builder/__tests__/routing-guidance.test.d.ts +1 -2
  10. package/core/dist/prompt-builder/__tests__/routing-guidance.test.d.ts.map +1 -1
  11. package/core/dist/prompt-builder/__tests__/routing-guidance.test.js +1 -2
  12. package/core/dist/prompt-builder/__tests__/routing-guidance.test.js.map +1 -1
  13. package/core/dist/prompt-builder/routing-guidance.d.ts +0 -1
  14. package/core/dist/prompt-builder/routing-guidance.d.ts.map +1 -1
  15. package/core/dist/prompt-builder/routing-guidance.js +0 -1
  16. package/core/dist/prompt-builder/routing-guidance.js.map +1 -1
  17. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +5 -43
  18. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  19. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +205 -2
  20. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
  21. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.js +17 -1
  22. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.js.map +1 -1
  23. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +4 -1
  24. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  25. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +6 -3
  26. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  27. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts +20 -7
  28. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts.map +1 -1
  29. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.js +19 -3
  30. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.js.map +1 -1
  31. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js +3 -3
  32. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js.map +1 -1
  33. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts.map +1 -1
  34. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +0 -18
  35. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -1
  36. package/core/package.json +1 -1
  37. package/package.json +2 -2
  38. package/pd-cli/package.json +1 -1
  39. package/plugin/dist/hooks/prompt.js +4 -5
  40. package/plugin/dist/index.js +0 -83
  41. package/plugin/package.json +2 -2
  42. package/plugin/dist/core/local-worker-routing.d.ts +0 -124
  43. package/plugin/dist/core/local-worker-routing.js +0 -216
  44. package/plugin/dist/core/model-deployment-registry.d.ts +0 -218
  45. package/plugin/dist/core/model-deployment-registry.js +0 -502
  46. package/plugin/dist/core/model-training-registry.d.ts +0 -295
  47. package/plugin/dist/core/model-training-registry.js +0 -474
  48. package/plugin/dist/core/promotion-gate.d.ts +0 -243
  49. package/plugin/dist/core/promotion-gate.js +0 -554
  50. package/plugin/dist/core/shadow-observation-registry.d.ts +0 -217
  51. package/plugin/dist/core/shadow-observation-registry.js +0 -280
  52. package/plugin/dist/utils/shadow-fingerprint.d.ts +0 -16
  53. package/plugin/dist/utils/shadow-fingerprint.js +0 -34
@@ -28,6 +28,7 @@ declare function fetchPrincipleTrajectory(principleId: string): Promise<ApiRespo
28
28
  declare function fetchApprovalDetail(approvalId: string): Promise<ApiResponse<ApprovalRecordData>>;
29
29
  declare function approveApproval(approvalId: string, note?: string): Promise<ApiResponse<ApprovalRecordData>>;
30
30
  declare function rejectApproval(approvalId: string, reason: string): Promise<ApiResponse<ApprovalRecordData>>;
31
+ declare function editApproval(approvalId: string, newArtifactId: string, editReason: string): Promise<ApiResponse<ApprovalRecordData>>;
31
32
  declare function createFeedbackReport(input: unknown, diagnostics: unknown): Promise<ApiResponse<FeedbackReportData>>;
32
33
  declare function listFeedbackReports(): Promise<ApiResponse<{
33
34
  drafts: FeedbackDraftSummaryData[];
@@ -50,7 +51,7 @@ declare function fetchUpdateHistory(): Promise<ApiResponse<UpdateHistoryData>>;
50
51
  declare function applyUpdate(): Promise<ApiResponse<ApplyUpdateResultData>>;
51
52
  declare function rollbackUpdate(backupDir: string): Promise<ApiResponse<RollbackResultData>>;
52
53
  declare function fetchEvidenceChain(): Promise<ApiResponse<EvidenceChainData>>;
53
- export { getToken, setToken, clearToken, checkAuth, request, fetchApprovalDetail, approveApproval, rejectApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleTrajectory, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, rollbackUpdate, fetchEvidenceChain, };
54
+ export { getToken, setToken, clearToken, checkAuth, request, fetchApprovalDetail, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleTrajectory, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, rollbackUpdate, fetchEvidenceChain, };
54
55
  export type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, OutputLanguageData, GovernanceQueueData, ActivationsData, DisableActivationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, } from "./utils/validators.js";
55
56
  export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
56
57
  export type { ApprovalRecordData as ApprovalRecord } from "./utils/validators.js";
@@ -134,6 +134,12 @@ async function rejectApproval(approvalId, reason) {
134
134
  body: JSON.stringify({ reason }),
135
135
  }, validateApprovalRecordDirect);
136
136
  }
137
+ async function editApproval(approvalId, newArtifactId, editReason) {
138
+ return request('/api/v1/approvals/' + encodeURIComponent(approvalId) + '/edit', {
139
+ method: 'POST',
140
+ body: JSON.stringify({ newArtifactId, editReason }),
141
+ }, validateApprovalRecordDirect);
142
+ }
137
143
  // ── MVP seed feedback report drafts (PRI-285) ────────────────────────────────
138
144
  async function createFeedbackReport(input, diagnostics) {
139
145
  return request('/api/feedback/reports', {
@@ -230,4 +236,4 @@ async function fetchEvidenceChain() {
230
236
  return request('/api/v1/evidence-chain', undefined, validateEvidenceChain);
231
237
  }
232
238
  // ── Exports ───────────────────────────────────────────────────────────────────
233
- export { getToken, setToken, clearToken, checkAuth, request, fetchApprovalDetail, approveApproval, rejectApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleTrajectory, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, rollbackUpdate, fetchEvidenceChain, };
239
+ export { getToken, setToken, clearToken, checkAuth, request, fetchApprovalDetail, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleTrajectory, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, rollbackUpdate, fetchEvidenceChain, };
@@ -98,14 +98,22 @@
98
98
  "untitledCandidate": "Untitled candidate principle",
99
99
  "approveAction": "Approve",
100
100
  "rejectAction": "Reject",
101
+ "editAction": "Edit",
101
102
  "confirmReject": "Confirm reject",
103
+ "confirmEdit": "Confirm edit",
102
104
  "cancelAction": "Cancel",
103
105
  "rejectReasonLabel": "Reject reason (required)",
104
106
  "rejectReasonPlaceholder": "Explain why this is rejected",
107
+ "editReasonLabel": "Edit reason (required)",
108
+ "editReasonPlaceholder": "Explain why the wording or scope needs to change",
109
+ "editNewArtifactPlaceholder": "Enter validated revision artifact ID",
110
+ "currentArtifactLabel": "Current artifact",
105
111
  "approveSucceeded": "Approved",
106
112
  "rejectSucceeded": "Rejected",
113
+ "editSucceeded": "Revision saved",
107
114
  "approveFailed": "Approval failed, please retry.",
108
115
  "rejectFailed": "Rejection failed, please retry.",
116
+ "editFailed": "Edit failed, please retry.",
109
117
  "partialFailure": "Action completed, but {{failedCount}}/{{totalCount}} record(s) failed: {{reason}}",
110
118
  "unknownFailure": "Check the service logs.",
111
119
  "onboardingTitle": "How PD Works",
@@ -368,18 +376,27 @@
368
376
  "approve": "Approve Principle",
369
377
  "park": "Park",
370
378
  "reject": "Reject",
379
+ "editAction": "Edit",
371
380
  "confirmApprove": "After approval, the principle will activate via prompt channel. Confirm approval?",
372
381
  "confirm": "Confirm",
373
382
  "cancel": "Cancel",
374
383
  "rejectReasonPlaceholder": "Please explain why you reject (required)",
384
+ "editReasonLabel": "Edit reason (required)",
385
+ "editReasonPlaceholder": "Explain why the wording or scope needs to change",
386
+ "editNewArtifactPlaceholder": "Enter validated revision artifact ID",
387
+ "currentArtifactLabel": "Current artifact",
375
388
  "confirmReject": "Confirm Rejection",
389
+ "confirmEdit": "Confirm Edit",
376
390
  "approved": "Approved / Will activate via prompt channel",
377
391
  "rejected": "Rejected",
392
+ "editSucceeded": "Revision saved",
378
393
  "parked": "Parked",
379
394
  "viewActivation": "View activation status",
380
395
  "approveFailed": "Approval failed. Please try again later.",
381
- "partialFailure": "Decision applied, but {{failedCount}}/{{totalCount}} records failed. Please verify and retry.",
396
+ "partialFailure": "Decision applied, but {{failedCount}}/{{totalCount}} records failed: {{reason}}",
382
397
  "rejectFailed": "Rejection failed. Please try again later.",
398
+ "editFailed": "Edit failed. Please try again later.",
399
+ "unknownFailure": "Check the service logs.",
383
400
  "decisionTitle": "Decision Actions",
384
401
  "parkUnavailable": "Park is not yet available",
385
402
  "reasonDataUnavailable": "Data temporarily unavailable",
@@ -98,14 +98,22 @@
98
98
  "untitledCandidate": "待命名候选原则",
99
99
  "approveAction": "批准",
100
100
  "rejectAction": "拒绝",
101
+ "editAction": "编辑",
101
102
  "confirmReject": "确认拒绝",
103
+ "confirmEdit": "确认编辑",
102
104
  "cancelAction": "取消",
103
105
  "rejectReasonLabel": "拒绝原因(必填)",
104
106
  "rejectReasonPlaceholder": "请说明拒绝原因",
107
+ "editReasonLabel": "编辑原因(必填)",
108
+ "editReasonPlaceholder": "请说明为什么需要调整措辞或范围",
109
+ "editNewArtifactPlaceholder": "输入新的已验证工件 ID",
110
+ "currentArtifactLabel": "当前工件",
105
111
  "approveSucceeded": "已批准",
106
112
  "rejectSucceeded": "已拒绝",
113
+ "editSucceeded": "已保存修订",
107
114
  "approveFailed": "批准失败,请稍后重试。",
108
115
  "rejectFailed": "拒绝失败,请稍后重试。",
116
+ "editFailed": "编辑失败,请稍后重试。",
109
117
  "partialFailure": "操作完成,但 {{failedCount}}/{{totalCount}} 条记录失败:{{reason}}",
110
118
  "unknownFailure": "请检查服务日志。",
111
119
  "onboardingTitle": "PD 的工作方式",
@@ -368,18 +376,27 @@
368
376
  "approve": "批准原则",
369
377
  "park": "暂存",
370
378
  "reject": "拒绝",
379
+ "editAction": "编辑",
371
380
  "confirmApprove": "批准后原则将通过提示词通道激活。确认批准?",
372
381
  "confirm": "确认",
373
382
  "cancel": "取消",
374
383
  "rejectReasonPlaceholder": "请说明拒绝原因(必填)",
384
+ "editReasonLabel": "编辑原因(必填)",
385
+ "editReasonPlaceholder": "请说明为什么需要调整措辞或范围",
386
+ "editNewArtifactPlaceholder": "输入新的已验证工件 ID",
387
+ "currentArtifactLabel": "当前工件",
375
388
  "confirmReject": "确认拒绝",
389
+ "confirmEdit": "确认编辑",
376
390
  "approved": "已批准 / 将通过提示词通道激活",
377
391
  "rejected": "已拒绝",
392
+ "editSucceeded": "已保存修订",
378
393
  "parked": "已暂存",
379
394
  "viewActivation": "查看生效情况",
380
395
  "approveFailed": "批准失败,请稍后重试。",
381
- "partialFailure": "决策已执行,但 {{failedCount}}/{{totalCount}} 条记录失败。请检查后重试。",
396
+ "partialFailure": "决策已执行,但 {{failedCount}}/{{totalCount}} 条记录失败:{{reason}}",
382
397
  "rejectFailed": "拒绝失败,请稍后重试。",
398
+ "editFailed": "编辑失败,请稍后重试。",
399
+ "unknownFailure": "请检查服务日志。",
383
400
  "decisionTitle": "决策操作",
384
401
  "parkUnavailable": "暂存尚未可用",
385
402
  "reasonDataUnavailable": "数据暂不可用",
@@ -7,7 +7,7 @@ import { PageShell } from "../../components/layout/page-shell.js";
7
7
  import { PageLoading } from "../../components/layout/page-loading.js";
8
8
  import { SectionTitle } from "../../components/layout/section-title.js";
9
9
  import { DailyThoughtCard } from "../../components/focus/daily-thought-card.js";
10
- import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApproval, } from "../../api.js";
10
+ import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApproval, editApproval, } from "../../api.js";
11
11
  export function summarizeDecisionResults(results) {
12
12
  const failures = results.filter((result) => !result.success);
13
13
  const first = failures[0];
@@ -175,6 +175,10 @@ function PendingReviewCard({ group, onDecisionApplied, }) {
175
175
  const [actionLoading, setActionLoading] = useState(false);
176
176
  const [showRejectInput, setShowRejectInput] = useState(false);
177
177
  const [rejectReason, setRejectReason] = useState("");
178
+ const [showEditInput, setShowEditInput] = useState(false);
179
+ const [editReason, setEditReason] = useState("");
180
+ const [newArtifactId, setNewArtifactId] = useState("");
181
+ const isActionable = group.status === "pending" && group.records.some((r) => r.status === "pending");
178
182
  // Display title: prefer candidateDescription (human-readable) over principleTitle
179
183
  // (which falls back to a fabricated principleId when the principle isn't in ledger).
180
184
  const displayTitle = group.candidateDescription
@@ -194,7 +198,7 @@ function PendingReviewCard({ group, onDecisionApplied, }) {
194
198
  return { ...summarizeDecisionResults(results), totalCount: pendingRecords.length };
195
199
  }
196
200
  const handleApprove = async () => {
197
- if (actionLoading)
201
+ if (!isActionable || actionLoading)
198
202
  return;
199
203
  setActionLoading(true);
200
204
  try {
@@ -221,7 +225,7 @@ function PendingReviewCard({ group, onDecisionApplied, }) {
221
225
  }
222
226
  };
223
227
  const handleReject = async () => {
224
- if (actionLoading || !rejectReason.trim())
228
+ if (!isActionable || actionLoading || !rejectReason.trim())
225
229
  return;
226
230
  setActionLoading(true);
227
231
  try {
@@ -249,7 +253,47 @@ function PendingReviewCard({ group, onDecisionApplied, }) {
249
253
  setActionLoading(false);
250
254
  }
251
255
  };
252
- return (_jsxs("article", { className: "relative pl-[22px] py-[18px] pr-[18px] bg-panel border border-line rounded-[6px] transition-colors", children: [_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-[3px] rounded-l-[6px] bg-gov" }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: channelLabel }), isReversible && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: primaryChannel === "prompt" ? t("pages.focus.tagReversible") : t("pages.focus.tagLowRisk") }))] }), _jsx("div", { className: "mt-[14px] mb-2 font-semibold text-ink leading-snug", children: displayTitle }), _jsx("div", { className: "mt-2 px-3 py-2 bg-surface/60 border-l-2 border-gov text-ink-3 text-[13px] leading-snug", children: t("pages.focus.evidenceSummary", { count: group.records.length }) }), _jsxs("div", { className: "flex gap-2 mt-4 flex-wrap items-center", children: [_jsx("button", { type: "button", onClick: handleApprove, disabled: actionLoading, className: "inline-flex items-center border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: actionLoading ? t("common.loading") + "…" : t("pages.focus.approveAction", { defaultValue: "批准" }) }), _jsx("button", { type: "button", onClick: () => setShowRejectInput((v) => !v), disabled: actionLoading, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.rejectAction", { defaultValue: "拒绝" }) }), _jsx(Link, { to: "/evidence", className: "inline-flex items-center text-gov text-[12.5px] hover:underline focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.viewFullChain") })] }), showRejectInput && (_jsxs("div", { className: "mt-3 p-3 border border-danger/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.rejectReasonLabel", { defaultValue: "拒绝原因(必填)" }) }), _jsx("textarea", { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), placeholder: t("pages.focus.rejectReasonPlaceholder", { defaultValue: "请说明拒绝原因" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleReject, disabled: !rejectReason.trim() || actionLoading, className: "inline-flex items-center border border-danger text-danger bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-danger/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.focus.confirmReject", { defaultValue: "确认拒绝" }) }), _jsx("button", { type: "button", onClick: () => { setShowRejectInput(false); setRejectReason(""); }, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.cancelAction", { defaultValue: "取消" }) })] })] }))] }));
256
+ const currentArtifactId = group.records.find((r) => r.status === "pending")?.artifactId ?? "";
257
+ const handleEdit = async () => {
258
+ if (!isActionable || actionLoading || !editReason.trim() || !newArtifactId.trim())
259
+ return;
260
+ setActionLoading(true);
261
+ try {
262
+ const pendingRecords = group.records.filter((r) => r.status === "pending");
263
+ const results = [];
264
+ for (const record of pendingRecords) {
265
+ const result = await editApproval(record.id, newArtifactId.trim(), editReason.trim());
266
+ results.push(result.success
267
+ ? { success: true }
268
+ : { success: false, error: result.error, nextAction: result.nextAction });
269
+ }
270
+ const { allSucceeded, failedCount, failureReason } = summarizeDecisionResults(results);
271
+ const totalCount = results.length;
272
+ if (allSucceeded) {
273
+ toast.success(t("pages.focus.editSucceeded", { defaultValue: "已保存修订" }));
274
+ setShowEditInput(false);
275
+ setEditReason("");
276
+ setNewArtifactId("");
277
+ onDecisionApplied();
278
+ }
279
+ else {
280
+ toast.error(t("pages.focus.partialFailure", {
281
+ defaultValue: `编辑完成,但 ${failedCount}/${totalCount} 条记录失败。`,
282
+ failedCount,
283
+ totalCount,
284
+ reason: failureReason ?? t("pages.focus.unknownFailure", { defaultValue: "请检查服务日志。" }),
285
+ }));
286
+ onDecisionApplied();
287
+ }
288
+ }
289
+ catch {
290
+ toast.error(t("pages.focus.editFailed", { defaultValue: "编辑失败,请稍后重试。" }));
291
+ }
292
+ finally {
293
+ setActionLoading(false);
294
+ }
295
+ };
296
+ return (_jsxs("article", { className: "relative pl-[22px] py-[18px] pr-[18px] bg-panel border border-line rounded-[6px] transition-colors", children: [_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-[3px] rounded-l-[6px] bg-gov" }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: channelLabel }), isReversible && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: primaryChannel === "prompt" ? t("pages.focus.tagReversible") : t("pages.focus.tagLowRisk") }))] }), _jsx("div", { className: "mt-[14px] mb-2 font-semibold text-ink leading-snug", children: displayTitle }), _jsx("div", { className: "mt-2 px-3 py-2 bg-surface/60 border-l-2 border-gov text-ink-3 text-[13px] leading-snug", children: t("pages.focus.evidenceSummary", { count: group.records.length }) }), _jsxs("div", { className: "flex gap-2 mt-4 flex-wrap items-center", children: [_jsx("button", { type: "button", onClick: handleApprove, disabled: !isActionable || actionLoading, className: "inline-flex items-center border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: actionLoading ? t("common.loading") + "…" : t("pages.focus.approveAction", { defaultValue: "批准" }) }), _jsx("button", { type: "button", onClick: () => { setShowEditInput((v) => !v); setShowRejectInput(false); }, disabled: !isActionable || actionLoading, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.editAction", { defaultValue: "编辑" }) }), _jsx("button", { type: "button", onClick: () => { setShowRejectInput((v) => !v); setShowEditInput(false); }, disabled: !isActionable || actionLoading, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.rejectAction", { defaultValue: "拒绝" }) }), _jsx(Link, { to: "/evidence", className: "inline-flex items-center text-gov text-[12.5px] hover:underline focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.viewFullChain") })] }), showRejectInput && (_jsxs("div", { className: "mt-3 p-3 border border-danger/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.rejectReasonLabel", { defaultValue: "拒绝原因(必填)" }) }), _jsx("textarea", { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), placeholder: t("pages.focus.rejectReasonPlaceholder", { defaultValue: "请说明拒绝原因" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleReject, disabled: !rejectReason.trim() || actionLoading, className: "inline-flex items-center border border-danger text-danger bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-danger/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.focus.confirmReject", { defaultValue: "确认拒绝" }) }), _jsx("button", { type: "button", onClick: () => { setShowRejectInput(false); setRejectReason(""); }, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.cancelAction", { defaultValue: "取消" }) })] })] })), showEditInput && (_jsxs("div", { className: "mt-3 p-3 border border-gov/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.editReasonLabel", { defaultValue: "编辑原因(必填)" }) }), _jsxs("div", { className: "mb-2 text-ink-4 text-[12px] font-mono", children: [t("pages.focus.currentArtifactLabel", { defaultValue: "当前工件" }), ": ", currentArtifactId] }), _jsx("input", { type: "text", value: newArtifactId, onChange: (e) => setNewArtifactId(e.target.value), placeholder: t("pages.focus.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] mb-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", "aria-label": t("pages.focus.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }) }), _jsx("textarea", { value: editReason, onChange: (e) => setEditReason(e.target.value), placeholder: t("pages.focus.editReasonPlaceholder", { defaultValue: "请说明编辑原因" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleEdit, disabled: !editReason.trim() || !newArtifactId.trim() || actionLoading, className: "inline-flex items-center border border-gov text-gov bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.focus.confirmEdit", { defaultValue: "确认编辑" }) }), _jsx("button", { type: "button", onClick: () => { setShowEditInput(false); setEditReason(""); setNewArtifactId(""); }, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.cancelAction", { defaultValue: "取消" }) })] })] }))] }));
253
297
  }
254
298
  function StagnationSignalCard({ signal }) {
255
299
  const { t } = useTranslation();
@@ -7,7 +7,7 @@ import { PageShell } from "../../components/layout/page-shell.js";
7
7
  import { PageLoading } from "../../components/layout/page-loading.js";
8
8
  import { SectionTitle } from "../../components/layout/section-title.js";
9
9
  import { Button } from "../../components/ui/button.js";
10
- import { fetchPrincipleDetail, fetchApprovalsGrouped, fetchLifecycleMetrics, fetchPrincipleTrajectory, approveApproval, rejectApproval, } from "../../api.js";
10
+ import { fetchPrincipleDetail, fetchApprovalsGrouped, fetchLifecycleMetrics, fetchPrincipleTrajectory, approveApproval, rejectApproval, editApproval, } from "../../api.js";
11
11
  // ── Runtime validation (H section) ──────────────────────────────────────────
12
12
  function isString(v) {
13
13
  return typeof v === "string";
@@ -121,6 +121,9 @@ export function PrincipleDetailPage() {
121
121
  const [showConfirm, setShowConfirm] = useState(false);
122
122
  const [showRejectInput, setShowRejectInput] = useState(false);
123
123
  const [rejectReason, setRejectReason] = useState("");
124
+ const [showEditInput, setShowEditInput] = useState(false);
125
+ const [editReason, setEditReason] = useState("");
126
+ const [newArtifactId, setNewArtifactId] = useState("");
124
127
  const [actionLoading, setActionLoading] = useState(false);
125
128
  // Evidence ID copy state (Wave 6: stop showing raw UUIDs as primary content)
126
129
  const [evidenceCopied, setEvidenceCopied] = useState(false);
@@ -303,6 +306,71 @@ export function PrincipleDetailPage() {
303
306
  const handlePark = () => {
304
307
  // No-op: Park is disabled and not available in this version.
305
308
  };
309
+ const handleEdit = () => {
310
+ if (!isActionable)
311
+ return;
312
+ setShowEditInput(true);
313
+ setShowRejectInput(false);
314
+ setShowConfirm(false);
315
+ };
316
+ const cancelEdit = () => {
317
+ setShowEditInput(false);
318
+ setEditReason("");
319
+ setNewArtifactId("");
320
+ };
321
+ const currentArtifactId = approvalGroup?.records.find((r) => r.status === "pending")?.artifactId ?? "";
322
+ const confirmEdit = async () => {
323
+ if (!isActionable || !approvalGroup || !editReason.trim() || !newArtifactId.trim() || actionLoading)
324
+ return;
325
+ const pendingRecords = approvalGroup.records.filter((r) => r.status === "pending");
326
+ // P1-5: guard against an empty pending set — without this the loop is a
327
+ // no-op yet allSucceeded evaluates true, reporting a misleading success.
328
+ if (pendingRecords.length === 0) {
329
+ toast.error(t("principles.detail.editFailed", { defaultValue: "没有可编辑的待处理记录。" }));
330
+ setShowEditInput(false);
331
+ return;
332
+ }
333
+ setActionLoading(true);
334
+ try {
335
+ let failedCount = 0;
336
+ let failureReason;
337
+ for (const record of pendingRecords) {
338
+ const result = await editApproval(record.id, newArtifactId.trim(), editReason.trim());
339
+ if (!result.success) {
340
+ failedCount++;
341
+ if (!failureReason) {
342
+ failureReason = result.nextAction ? `${result.error} ${result.nextAction}` : result.error;
343
+ }
344
+ }
345
+ }
346
+ const allSucceeded = failedCount === 0;
347
+ if (allSucceeded) {
348
+ toast.success(t("principles.detail.editSucceeded", { defaultValue: "已保存修订" }));
349
+ setShowEditInput(false);
350
+ setEditReason("");
351
+ setNewArtifactId("");
352
+ loadData();
353
+ }
354
+ else {
355
+ // P1-4: thread the backend failureReason into the toast so the owner
356
+ // sees WHY records failed (matches FocusPage behaviour). Previously
357
+ // failureReason was computed but never used (dead code).
358
+ toast.error(t("principles.detail.partialFailure", {
359
+ defaultValue: `编辑完成,但 ${failedCount}/${pendingRecords.length} 条记录失败:${failureReason ?? t("principles.detail.unknownFailure", { defaultValue: "请检查服务日志。" })}`,
360
+ failedCount,
361
+ totalCount: pendingRecords.length,
362
+ reason: failureReason ?? t("principles.detail.unknownFailure", { defaultValue: "请检查服务日志。" }),
363
+ }));
364
+ loadData();
365
+ }
366
+ }
367
+ catch {
368
+ toast.error(t("principles.detail.editFailed", { defaultValue: "编辑失败,请稍后重试。" }));
369
+ }
370
+ finally {
371
+ setActionLoading(false);
372
+ }
373
+ };
306
374
  // ── Render ──────────────────────────────────────────────────────────────
307
375
  if (loading) {
308
376
  return (_jsx(PageShell, { children: _jsx(PageLoading, { cardCount: 1, label: t("principles.loading", { defaultValue: "Loading…" }) }) }));
@@ -339,5 +407,5 @@ export function PrincipleDetailPage() {
339
407
  // Fallback: show loading state or no-data message
340
408
  ['evidence', 'diagnosis', 'proposal', 'review', 'deploy', 'behavior'].map((key) => (_jsxs("div", { className: "flex gap-4", children: [_jsx("span", { className: "font-mono text-[12px] text-ink-3 min-w-[80px]", children: t(STAGE_LABEL_KEYS[key] ?? `principles.detail.stage${key}`, { defaultValue: key }) }), _jsx("div", { className: "flex-1", children: _jsx("p", { className: "text-ink-4 text-[13px]", children: trajectory === null
341
409
  ? t("principles.detail.trajectoryLoadFailed", { defaultValue: "无法加载轨迹数据" })
342
- : "—" }) })] }, key)))) }), hasRules && lifecycle && (_jsxs("div", { className: "mt-6 pt-4 border-t border-line", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.lifecycleMetrics", { defaultValue: "生命周期指标" }) }), _jsx("p", { className: "text-amber text-[12px] font-mono mt-1 mb-3", children: t("principles.detail.lifecycleNote") }), lifecycle.adherence.insufficientData ? (_jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed", children: t("principles.detail.insufficientData") })) : (_jsxs("div", { children: [_jsxs("p", { className: "text-ink-2 text-[14px]", children: [t("principles.detail.adherenceRate", { defaultValue: "遵守率" }), ": ", lifecycle.adherence.rate !== null ? `${(lifecycle.adherence.rate * 100).toFixed(1)}%` : "—"] }), _jsx("p", { className: "text-ink-3 text-[12px] mt-1", children: lifecycle.adherence.note })] })), lifecycle.ruleMetrics.length > 0 && (_jsx("div", { className: "mt-3", children: lifecycle.ruleMetrics.map((rm) => (_jsxs("div", { className: "flex gap-3 text-[12px] font-mono text-ink-3", children: [_jsx("span", { children: rm.ruleId }), _jsxs("span", { children: [t("principles.detail.triggered", { defaultValue: "触发" }), ": ", rm.triggered] })] }, rm.ruleId))) }))] })), hasRules && !lifecycle && (_jsx("div", { className: "mt-6 pt-4 border-t border-line", children: _jsx("p", { className: "text-ink-3 text-[13px]", children: t("principles.detail.lifecycleLoadFailed", { defaultValue: "无法加载生命周期指标。你可以稍后重试。" }) }) }))] })] }), _jsxs("div", { className: "border-t border-line pt-6 mt-6", children: [_jsx(SectionTitle, { children: t("principles.detail.decisionTitle", { defaultValue: "决策操作" }) }), _jsxs("div", { className: "flex gap-3 flex-wrap items-center", children: [_jsx(Button, { variant: "default", onClick: handleApprove, disabled: !isActionable || actionLoading, children: t("principles.detail.approve") }), _jsx(Button, { variant: "outline", onClick: handlePark, disabled: true, children: t("principles.detail.park") }), _jsx(Button, { variant: "destructive", onClick: handleReject, disabled: !isActionable || actionLoading, children: t("principles.detail.reject") }), _jsxs("span", { className: "text-ink-4 text-[13px]", children: ["(", t("principles.detail.parkUnavailable", { defaultValue: "暂存尚未可用" }), ")"] })] }), !isActionable && (_jsxs("p", { className: "text-danger text-[13px] mt-3 font-mono", children: [t("principles.detail.unactionableReasonPrefix", { defaultValue: "不可操作原因: " }), t(reasonKey, { defaultValue: defaultReason })] })), isActionable && showConfirm && (_jsxs("div", { className: "mt-4 p-3 bg-gov/5 border border-gov/20 rounded-[var(--radius-md)]", children: [_jsx("p", { className: "text-ink-2 text-[13px] mb-3", children: t("principles.detail.confirmApprove") }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: confirmApprove, disabled: actionLoading, children: t("principles.detail.confirm") }), _jsx(Button, { variant: "outline", size: "sm", onClick: cancelConfirm, children: t("principles.detail.cancel") })] })] })), isActionable && showRejectInput && (_jsxs("div", { className: "mt-4 p-3 border border-danger/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("principles.detail.rejectReasonLabel", { defaultValue: "拒绝原因" }) }), _jsx("textarea", { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), placeholder: t("principles.detail.rejectReasonPlaceholder"), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", "aria-label": t("principles.detail.rejectReasonPlaceholder") }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx(Button, { variant: "destructive", size: "sm", onClick: confirmReject, disabled: !rejectReason.trim() || actionLoading, children: t("principles.detail.confirmReject") }), _jsx(Button, { variant: "outline", size: "sm", onClick: cancelReject, children: t("principles.detail.cancel") })] })] }))] })] }) }));
410
+ : "—" }) })] }, key)))) }), hasRules && lifecycle && (_jsxs("div", { className: "mt-6 pt-4 border-t border-line", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.lifecycleMetrics", { defaultValue: "生命周期指标" }) }), _jsx("p", { className: "text-amber text-[12px] font-mono mt-1 mb-3", children: t("principles.detail.lifecycleNote") }), lifecycle.adherence.insufficientData ? (_jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed", children: t("principles.detail.insufficientData") })) : (_jsxs("div", { children: [_jsxs("p", { className: "text-ink-2 text-[14px]", children: [t("principles.detail.adherenceRate", { defaultValue: "遵守率" }), ": ", lifecycle.adherence.rate !== null ? `${(lifecycle.adherence.rate * 100).toFixed(1)}%` : "—"] }), _jsx("p", { className: "text-ink-3 text-[12px] mt-1", children: lifecycle.adherence.note })] })), lifecycle.ruleMetrics.length > 0 && (_jsx("div", { className: "mt-3", children: lifecycle.ruleMetrics.map((rm) => (_jsxs("div", { className: "flex gap-3 text-[12px] font-mono text-ink-3", children: [_jsx("span", { children: rm.ruleId }), _jsxs("span", { children: [t("principles.detail.triggered", { defaultValue: "触发" }), ": ", rm.triggered] })] }, rm.ruleId))) }))] })), hasRules && !lifecycle && (_jsx("div", { className: "mt-6 pt-4 border-t border-line", children: _jsx("p", { className: "text-ink-3 text-[13px]", children: t("principles.detail.lifecycleLoadFailed", { defaultValue: "无法加载生命周期指标。你可以稍后重试。" }) }) }))] })] }), _jsxs("div", { className: "border-t border-line pt-6 mt-6", children: [_jsx(SectionTitle, { children: t("principles.detail.decisionTitle", { defaultValue: "决策操作" }) }), _jsxs("div", { className: "flex gap-3 flex-wrap items-center", children: [_jsx(Button, { variant: "default", onClick: handleApprove, disabled: !isActionable || actionLoading, children: t("principles.detail.approve") }), _jsx(Button, { variant: "outline", onClick: handleEdit, disabled: !isActionable || actionLoading, children: t("principles.detail.editAction", { defaultValue: "编辑" }) }), _jsx(Button, { variant: "outline", onClick: handlePark, disabled: true, children: t("principles.detail.park") }), _jsx(Button, { variant: "destructive", onClick: handleReject, disabled: !isActionable || actionLoading, children: t("principles.detail.reject") }), _jsxs("span", { className: "text-ink-4 text-[13px]", children: ["(", t("principles.detail.parkUnavailable", { defaultValue: "暂存尚未可用" }), ")"] })] }), !isActionable && (_jsxs("p", { className: "text-danger text-[13px] mt-3 font-mono", children: [t("principles.detail.unactionableReasonPrefix", { defaultValue: "不可操作原因: " }), t(reasonKey, { defaultValue: defaultReason })] })), isActionable && showConfirm && (_jsxs("div", { className: "mt-4 p-3 bg-gov/5 border border-gov/20 rounded-[var(--radius-md)]", children: [_jsx("p", { className: "text-ink-2 text-[13px] mb-3", children: t("principles.detail.confirmApprove") }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: confirmApprove, disabled: actionLoading, children: t("principles.detail.confirm") }), _jsx(Button, { variant: "outline", size: "sm", onClick: cancelConfirm, children: t("principles.detail.cancel") })] })] })), isActionable && showRejectInput && (_jsxs("div", { className: "mt-4 p-3 border border-danger/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("principles.detail.rejectReasonLabel", { defaultValue: "拒绝原因" }) }), _jsx("textarea", { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), placeholder: t("principles.detail.rejectReasonPlaceholder"), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", "aria-label": t("principles.detail.rejectReasonPlaceholder") }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx(Button, { variant: "destructive", size: "sm", onClick: confirmReject, disabled: !rejectReason.trim() || actionLoading, children: t("principles.detail.confirmReject") }), _jsx(Button, { variant: "outline", size: "sm", onClick: cancelReject, children: t("principles.detail.cancel") })] })] })), isActionable && showEditInput && (_jsxs("div", { className: "mt-4 p-3 border border-gov/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("principles.detail.editReasonLabel", { defaultValue: "编辑原因" }) }), _jsxs("div", { className: "mb-2 text-ink-4 text-[12px] font-mono", children: [t("principles.detail.currentArtifactLabel", { defaultValue: "当前工件" }), ": ", currentArtifactId] }), _jsx("input", { type: "text", value: newArtifactId, onChange: (e) => setNewArtifactId(e.target.value), placeholder: t("principles.detail.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] mb-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", "aria-label": t("principles.detail.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }) }), _jsx("textarea", { value: editReason, onChange: (e) => setEditReason(e.target.value), placeholder: t("principles.detail.editReasonPlaceholder", { defaultValue: "请说明编辑原因" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: confirmEdit, disabled: !editReason.trim() || !newArtifactId.trim() || actionLoading, children: t("principles.detail.confirmEdit", { defaultValue: "确认编辑" }) }), _jsx(Button, { variant: "outline", size: "sm", onClick: cancelEdit, children: t("principles.detail.cancel") })] })] }))] })] }) }));
343
411
  }