create-principles-disciple 1.95.0 → 1.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console/dist/server/config/pd-config-store.js +49 -12
- package/console/dist/server/index.js +4 -3
- package/console/dist/server/models/IntentPageModel.d.ts +36 -2
- package/console/dist/server/models/IntentPageModel.js +213 -11
- package/console/dist/server/routes/intent.d.ts +5 -1
- package/console/dist/server/routes/intent.js +200 -12
- package/console/dist/types.d.ts +1 -0
- package/console/dist/ui/App.js +0 -53
- package/console/dist/ui/api.d.ts +18 -4
- package/console/dist/ui/api.js +38 -5
- package/console/dist/ui/components/layout/app-sidebar.js +12 -12
- package/console/dist/ui/components/ui/badge.d.ts +1 -1
- package/console/dist/ui/i18n/en.json +142 -3
- package/console/dist/ui/i18n/zh-CN.json +142 -3
- package/console/dist/ui/pages/control-center/AgentCard.d.ts +31 -0
- package/console/dist/ui/pages/control-center/AgentCard.js +156 -0
- package/console/dist/ui/pages/control-center/AgentGroup.d.ts +26 -0
- package/console/dist/ui/pages/control-center/AgentGroup.js +38 -0
- package/console/dist/ui/pages/control-center/ControlCenterPage.js +77 -41
- package/console/dist/ui/pages/control-center/WorkflowDiagram.d.ts +17 -0
- package/console/dist/ui/pages/control-center/WorkflowDiagram.js +55 -0
- package/console/dist/ui/pages/intent/IntentOnboarding.d.ts +67 -0
- package/console/dist/ui/pages/intent/IntentOnboarding.js +289 -0
- package/console/dist/ui/pages/intent/IntentPage.js +117 -6
- package/console/dist/ui/utils/agent-metadata.d.ts +58 -0
- package/console/dist/ui/utils/agent-metadata.js +275 -0
- package/console/dist/ui/utils/control-center-helpers.d.ts +19 -0
- package/console/dist/ui/utils/control-center-helpers.js +37 -0
- package/console/dist/ui/utils/validators.d.ts +37 -0
- package/console/dist/ui/utils/validators.js +130 -0
- package/console/dist/web/assets/app.css +360 -3
- package/console/dist/web/assets/app.js +2952 -1143
- package/console/package.json +1 -1
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +27 -35
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice-v2.test.js +85 -7
- package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice-v2.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/golden-trace-replay-validator.test.js +70 -0
- package/core/dist/runtime-v2/__tests__/golden-trace-replay-validator.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/golden-trace.test.js +136 -1
- package/core/dist/runtime-v2/__tests__/golden-trace.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/v2-adversarial-cases.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/v2-adversarial-cases.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/v2-adversarial-cases.test.js +211 -0
- package/core/dist/runtime-v2/__tests__/v2-adversarial-cases.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js +93 -0
- package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts +11 -0
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +22 -0
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/rulecode-context-v2-flag.test.d.ts +2 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/rulecode-context-v2-flag.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/rulecode-context-v2-flag.test.js +74 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/rulecode-context-v2-flag.test.js.map +1 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/surface-guard-policy.test.d.ts +2 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/surface-guard-policy.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/surface-guard-policy.test.js +246 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/surface-guard-policy.test.js.map +1 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +7 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/index.d.ts +2 -0
- package/core/dist/runtime-v2/feature-flags/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/index.js +2 -0
- package/core/dist/runtime-v2/feature-flags/index.js.map +1 -1
- package/{plugin/dist/core/surface-guard.d.ts → core/dist/runtime-v2/feature-flags/surface-guard-policy.d.ts} +15 -5
- package/core/dist/runtime-v2/feature-flags/surface-guard-policy.d.ts.map +1 -0
- package/{plugin/dist/core/surface-guard.js → core/dist/runtime-v2/feature-flags/surface-guard-policy.js} +14 -2
- package/core/dist/runtime-v2/feature-flags/surface-guard-policy.js.map +1 -0
- package/core/dist/runtime-v2/golden-trace-replay-validator.js +1 -1
- package/core/dist/runtime-v2/golden-trace-replay-validator.js.map +1 -1
- package/core/dist/runtime-v2/golden-trace.d.ts +19 -3
- package/core/dist/runtime-v2/golden-trace.d.ts.map +1 -1
- package/core/dist/runtime-v2/golden-trace.js +2 -0
- package/core/dist/runtime-v2/golden-trace.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +15 -3
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +11 -2
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/intent/__tests__/intent-doc-version.test.d.ts +2 -0
- package/core/dist/runtime-v2/intent/__tests__/intent-doc-version.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/intent/__tests__/intent-doc-version.test.js +88 -0
- package/core/dist/runtime-v2/intent/__tests__/intent-doc-version.test.js.map +1 -0
- package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.js +91 -1
- package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.js.map +1 -1
- package/core/dist/runtime-v2/intent/index.d.ts +2 -0
- package/core/dist/runtime-v2/intent/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/intent/index.js +2 -0
- package/core/dist/runtime-v2/intent/index.js.map +1 -1
- package/core/dist/runtime-v2/intent/intent-browser.d.ts +23 -0
- package/core/dist/runtime-v2/intent/intent-browser.d.ts.map +1 -0
- package/core/dist/runtime-v2/intent/intent-browser.js +21 -0
- package/core/dist/runtime-v2/intent/intent-browser.js.map +1 -0
- package/core/dist/runtime-v2/intent/intent-doc-version.d.ts +28 -0
- package/core/dist/runtime-v2/intent/intent-doc-version.d.ts.map +1 -0
- package/core/dist/runtime-v2/intent/intent-doc-version.js +21 -0
- package/core/dist/runtime-v2/intent/intent-doc-version.js.map +1 -0
- package/core/dist/runtime-v2/intent/intent-doc.d.ts +17 -1
- package/core/dist/runtime-v2/intent/intent-doc.d.ts.map +1 -1
- package/core/dist/runtime-v2/intent/intent-doc.js +95 -7
- package/core/dist/runtime-v2/intent/intent-doc.js.map +1 -1
- package/core/dist/runtime-v2/intent/intent-hash.d.ts +2 -0
- package/core/dist/runtime-v2/intent/intent-hash.d.ts.map +1 -0
- package/core/dist/runtime-v2/intent/intent-hash.js +10 -0
- package/core/dist/runtime-v2/intent/intent-hash.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js +41 -1
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/artificer-rule-output.test.js +130 -2
- package/core/dist/runtime-v2/internalization/__tests__/artificer-rule-output.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/behavior-example-pack.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/behavior-example-pack.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/behavior-example-pack.test.js +228 -0
- package/core/dist/runtime-v2/internalization/__tests__/behavior-example-pack.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/diag-chain-e2e.test.js +41 -11
- package/core/dist/runtime-v2/internalization/__tests__/diag-chain-e2e.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/refiner-sandbox-wrapper.test.js +135 -0
- package/core/dist/runtime-v2/internalization/__tests__/refiner-sandbox-wrapper.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/rule-context-v2.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-context-v2.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-context-v2.test.js +449 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-context-v2.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/adversarial-case.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/adversarial-case.js +24 -0
- package/core/dist/runtime-v2/internalization/adversarial-case.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-output.d.ts +10 -0
- package/core/dist/runtime-v2/internalization/artificer-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-output.js +45 -0
- package/core/dist/runtime-v2/internalization/artificer-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +2 -2
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +7 -2
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/behavior-example-pack.d.ts +66 -0
- package/core/dist/runtime-v2/internalization/behavior-example-pack.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/behavior-example-pack.js +183 -0
- package/core/dist/runtime-v2/internalization/behavior-example-pack.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +8 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.js +10 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +17 -0
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +118 -24
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js +1 -1
- package/core/dist/runtime-v2/internalization/refiner-sandbox-wrapper.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-context-v2.d.ts +61 -0
- package/core/dist/runtime-v2/internalization/rule-context-v2.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/rule-context-v2.js +301 -0
- package/core/dist/runtime-v2/internalization/rule-context-v2.js.map +1 -0
- package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts +9 -0
- package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/v2-adversarial-cases.d.ts +55 -0
- package/core/dist/runtime-v2/internalization/v2-adversarial-cases.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/v2-adversarial-cases.js +131 -0
- package/core/dist/runtime-v2/internalization/v2-adversarial-cases.js.map +1 -0
- package/core/dist/runtime-v2/pain-signal-observability.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-observability.js +24 -1
- package/core/dist/runtime-v2/pain-signal-observability.js.map +1 -1
- package/core/dist/runtime-v2/risk/__tests__/risk-calculator-policy.test.d.ts +2 -0
- package/core/dist/runtime-v2/risk/__tests__/risk-calculator-policy.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/risk/__tests__/risk-calculator-policy.test.js +95 -0
- package/core/dist/runtime-v2/risk/__tests__/risk-calculator-policy.test.js.map +1 -0
- package/core/dist/runtime-v2/risk/index.d.ts +7 -0
- package/core/dist/runtime-v2/risk/index.d.ts.map +1 -0
- package/core/dist/runtime-v2/risk/index.js +6 -0
- package/core/dist/runtime-v2/risk/index.js.map +1 -0
- package/core/dist/runtime-v2/risk/risk-calculator-policy.d.ts +18 -0
- package/core/dist/runtime-v2/risk/risk-calculator-policy.d.ts.map +1 -0
- package/core/dist/runtime-v2/risk/risk-calculator-policy.js +47 -0
- package/core/dist/runtime-v2/risk/risk-calculator-policy.js.map +1 -0
- package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-doc-version-store.test.d.ts +2 -0
- package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-doc-version-store.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-doc-version-store.test.js +83 -0
- package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-doc-version-store.test.js.map +1 -0
- package/core/dist/runtime-v2/store/intent/index.d.ts +2 -1
- package/core/dist/runtime-v2/store/intent/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/intent/index.js +2 -1
- package/core/dist/runtime-v2/store/intent/index.js.map +1 -1
- package/core/dist/runtime-v2/store/intent/sqlite-intent-doc-version-store.d.ts +28 -0
- package/core/dist/runtime-v2/store/intent/sqlite-intent-doc-version-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/intent/sqlite-intent-doc-version-store.js +88 -0
- package/core/dist/runtime-v2/store/intent/sqlite-intent-doc-version-store.js.map +1 -0
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +14 -0
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/core/dist/runtime-v2/thinking-models/__tests__/thinking-models-policy.test.d.ts +2 -0
- package/core/dist/runtime-v2/thinking-models/__tests__/thinking-models-policy.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/thinking-models/__tests__/thinking-models-policy.test.js +149 -0
- package/core/dist/runtime-v2/thinking-models/__tests__/thinking-models-policy.test.js.map +1 -0
- package/core/dist/runtime-v2/thinking-models/index.d.ts +3 -0
- package/core/dist/runtime-v2/thinking-models/index.d.ts.map +1 -0
- package/core/dist/runtime-v2/thinking-models/index.js +3 -0
- package/core/dist/runtime-v2/thinking-models/index.js.map +1 -0
- package/core/dist/runtime-v2/thinking-models/thinking-models-policy.d.ts +73 -0
- package/core/dist/runtime-v2/thinking-models/thinking-models-policy.d.ts.map +1 -0
- package/core/dist/runtime-v2/thinking-models/thinking-models-policy.js +227 -0
- package/core/dist/runtime-v2/thinking-models/thinking-models-policy.js.map +1 -0
- package/core/dist/runtime-v2/tools/__tests__/artificer-l2-tool-contract.test.js +40 -1
- package/core/dist/runtime-v2/tools/__tests__/artificer-l2-tool-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js +28 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-output-typebox.d.ts +3 -0
- package/core/dist/runtime-v2/tools/artificer-output-typebox.d.ts.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-output-typebox.js +3 -0
- package/core/dist/runtime-v2/tools/artificer-output-typebox.js.map +1 -1
- package/core/package.json +4 -0
- package/package.json +1 -1
- package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.js +74 -3
- package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.js.map +1 -1
- package/pd-cli/dist/commands/intent.d.ts +21 -4
- package/pd-cli/dist/commands/intent.d.ts.map +1 -1
- package/pd-cli/dist/commands/intent.js +88 -27
- package/pd-cli/dist/commands/intent.js.map +1 -1
- package/plugin/dist/commands/pain.js +2 -2
- package/plugin/dist/core/behavior-example-pack-assembler.d.ts +43 -0
- package/plugin/dist/core/behavior-example-pack-assembler.js +247 -0
- package/plugin/dist/core/intent-doc-reader-adapter.d.ts +13 -5
- package/plugin/dist/core/intent-doc-reader-adapter.js +25 -5
- package/plugin/dist/core/intent-doc-reader.d.ts +13 -4
- package/plugin/dist/core/intent-doc-reader.js +45 -24
- package/plugin/dist/core/rule-context-assembler.d.ts +45 -0
- package/plugin/dist/core/rule-context-assembler.js +148 -0
- package/plugin/dist/core/thinking-models.d.ts +12 -57
- package/plugin/dist/core/thinking-models.js +18 -224
- package/plugin/dist/core/trajectory-types.d.ts +18 -0
- package/plugin/dist/core/trajectory.d.ts +34 -2
- package/plugin/dist/core/trajectory.js +103 -0
- package/plugin/dist/hooks/gate.js +59 -2
- package/plugin/dist/hooks/pain.js +2 -2
- package/plugin/dist/hooks/prompt.js +2 -1
- package/plugin/dist/index.js +1 -1
- package/plugin/package.json +1 -1
- package/plugin/dist/core/evolution-migration.d.ts +0 -5
- package/plugin/dist/core/evolution-migration.js +0 -65
- package/plugin/dist/core/risk-calculator.d.ts +0 -22
- package/plugin/dist/core/risk-calculator.js +0 -88
- package/plugin/dist/core/rule-host-helpers.d.ts +0 -8
- package/plugin/dist/core/rule-host-helpers.js +0 -7
- package/plugin/dist/core/rule-host-types.d.ts +0 -7
- package/plugin/dist/core/rule-host-types.js +0 -7
- package/plugin/dist/core/schema/db-types.d.ts +0 -11
- package/plugin/dist/core/schema/db-types.js +0 -5
- package/plugin/dist/core/schema/index.d.ts +0 -17
- package/plugin/dist/core/schema/index.js +0 -15
- package/plugin/dist/core/schema/migration-runner.d.ts +0 -53
- package/plugin/dist/core/schema/migration-runner.js +0 -170
- package/plugin/dist/core/schema/migrations/001-init-trajectory.d.ts +0 -6
- package/plugin/dist/core/schema/migrations/001-init-trajectory.js +0 -190
- package/plugin/dist/core/schema/migrations/002-init-central.d.ts +0 -5
- package/plugin/dist/core/schema/migrations/002-init-central.js +0 -108
- package/plugin/dist/core/schema/migrations/003-init-workflow.d.ts +0 -5
- package/plugin/dist/core/schema/migrations/003-init-workflow.js +0 -50
- package/plugin/dist/core/schema/migrations/004-add-thinking-and-gfi.d.ts +0 -8
- package/plugin/dist/core/schema/migrations/004-add-thinking-and-gfi.js +0 -66
- package/plugin/dist/core/schema/migrations/index.d.ts +0 -16
- package/plugin/dist/core/schema/migrations/index.js +0 -27
- package/plugin/dist/core/schema/schema-definitions.d.ts +0 -46
- package/plugin/dist/core/schema/schema-definitions.js +0 -599
- package/plugin/scripts/db-migrate.mjs +0 -170
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* PRI-477 + spec 2026-06-27: Intent Engineering onboarding components.
|
|
4
|
+
*
|
|
5
|
+
* Three components:
|
|
6
|
+
* 1. OnboardingModal — 5-step wizard (intro → Why → Desired Outcome →
|
|
7
|
+
* Non-negotiables → Stop/Escalation → Current Strategic Focus → finish)
|
|
8
|
+
* 2. IntentEditor — section editor with 5 independent textareas (one per
|
|
9
|
+
* section), replacing the single monolithic textarea
|
|
10
|
+
* 3. CreateIntentButton — creates INTENT.md template, optionally shows wizard
|
|
11
|
+
*
|
|
12
|
+
* SPEC §22.1.1 update: Intent Page supports inline editing. This breaks the
|
|
13
|
+
* original "read-only governance view" constraint, but preserves the
|
|
14
|
+
* Owner-owned boundary: all edits are triggered by Owner clicking buttons,
|
|
15
|
+
* never by Agent auto-modification (SPEC §3.9 preserved).
|
|
16
|
+
*
|
|
17
|
+
* Architecture note: parseIntentDocSections / assembleIntentDoc are imported
|
|
18
|
+
* from @principles/core/runtime-v2. These are pure functions (no I/O, no
|
|
19
|
+
* node:crypto) and safe for the browser bundle. The crypto-dependent
|
|
20
|
+
* computeIntentContentHash lives in intent-hash.ts to keep intent-doc.ts
|
|
21
|
+
* browser-bundleable.
|
|
22
|
+
*/
|
|
23
|
+
import { useState, useEffect, useRef, useMemo } from "react";
|
|
24
|
+
import { useTranslation } from "react-i18next";
|
|
25
|
+
import { toast } from "sonner";
|
|
26
|
+
import { createIntentTemplate, saveIntentContent } from "../../api.js";
|
|
27
|
+
import { parseIntentDocSections, assembleIntentDoc, } from "@principles/core/runtime-v2/intent-browser";
|
|
28
|
+
// ── Section step helpers ─────────────────────────────────────────────────────
|
|
29
|
+
const SECTION_STEPS = [1, 2, 3, 4, 5];
|
|
30
|
+
const STEP_TO_KEY = {
|
|
31
|
+
1: "why",
|
|
32
|
+
2: "desiredOutcome",
|
|
33
|
+
3: "nonNegotiables",
|
|
34
|
+
4: "stopEscalation",
|
|
35
|
+
5: "currentStrategicFocus",
|
|
36
|
+
};
|
|
37
|
+
// ── localStorage key for onboarding dismissal ─────────────────────────────────
|
|
38
|
+
const ONBOARDING_DISMISSED_KEY = "pd_intent_onboarding_dismissed";
|
|
39
|
+
function isOnboardingDismissed() {
|
|
40
|
+
try {
|
|
41
|
+
return localStorage.getItem(ONBOARDING_DISMISSED_KEY) === "true";
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function setOnboardingDismissed() {
|
|
48
|
+
try {
|
|
49
|
+
localStorage.setItem(ONBOARDING_DISMISSED_KEY, "true");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// ignore localStorage errors
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function OnboardingModal({ onComplete, onSkip, onClose }) {
|
|
56
|
+
const { t } = useTranslation();
|
|
57
|
+
const [step, setStep] = useState("intro");
|
|
58
|
+
const [sections, setSections] = useState({});
|
|
59
|
+
const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);
|
|
60
|
+
const textareaRef = useRef(null);
|
|
61
|
+
// Focus textarea when entering a section step
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (typeof step === "number") {
|
|
64
|
+
textareaRef.current?.focus();
|
|
65
|
+
}
|
|
66
|
+
}, [step]);
|
|
67
|
+
const hasContent = useMemo(() => Object.values(sections).some((v) => typeof v === "string" && v.trim().length > 0), [sections]);
|
|
68
|
+
function handleRequestClose() {
|
|
69
|
+
if (hasContent) {
|
|
70
|
+
setShowDiscardConfirm(true);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
onClose();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function handleNext() {
|
|
77
|
+
if (step === "intro") {
|
|
78
|
+
setStep(1);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (typeof step === "number" && step < 5) {
|
|
82
|
+
setStep((step + 1));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (step === 5) {
|
|
86
|
+
setStep("finish");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function handlePrev() {
|
|
90
|
+
if (typeof step === "number" && step > 1) {
|
|
91
|
+
setStep((step - 1));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (step === 1) {
|
|
95
|
+
setStep("intro");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function handleSectionChange(value) {
|
|
99
|
+
if (typeof step !== "number")
|
|
100
|
+
return;
|
|
101
|
+
const key = STEP_TO_KEY[step];
|
|
102
|
+
setSections((prev) => ({ ...prev, [key]: value }));
|
|
103
|
+
}
|
|
104
|
+
const currentSectionValue = typeof step === "number" ? (sections[STEP_TO_KEY[step]] ?? "") : "";
|
|
105
|
+
const isCurrentSectionEmpty = typeof step === "number" && currentSectionValue.trim().length === 0;
|
|
106
|
+
return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-ink/40 backdrop-blur-sm animate-[pdFadeIn_200ms_ease-out]", role: "dialog", "aria-modal": "true", "aria-labelledby": "onboarding-title", onClick: handleRequestClose, children: _jsxs("div", { className: "bg-surface border border-line rounded-[8px] p-6 max-w-[620px] w-full mx-4 max-h-[85vh] overflow-y-auto", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "flex items-start justify-between mb-4", children: [_jsxs("div", { children: [_jsx("h2", { id: "onboarding-title", className: "text-[20px] font-semibold tracking-tight text-ink mb-1", children: t("pages.intent.onboarding.title") }), step === "intro" && (_jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed", children: t("pages.intent.onboarding.subtitle") })), typeof step === "number" && (_jsx("p", { className: "text-ink-3 text-[12px] font-mono", children: t("pages.intent.wizard.progressLabel", { current: step }) }))] }), _jsx("button", { type: "button", onClick: handleRequestClose, className: "shrink-0 ml-3 text-ink-3 hover:text-ink transition-colors", "aria-label": t("pages.intent.onboarding.close"), children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M4 4L14 14M14 4L4 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })] }), step === "intro" && (_jsxs("div", { className: "mb-5", children: [_jsxs("div", { className: "mb-5", children: [_jsx("h3", { className: "text-[14px] font-semibold text-ink mb-1.5", children: t("pages.intent.onboarding.whatIsIt.title") }), _jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed", children: t("pages.intent.onboarding.whatIsIt.description") })] }), _jsxs("div", { className: "mb-5", children: [_jsx("h3", { className: "text-[14px] font-semibold text-ink mb-1.5", children: t("pages.intent.onboarding.whyYouFill.title") }), _jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed", children: t("pages.intent.onboarding.whyYouFill.description") })] }), _jsxs("div", { className: "mb-5", children: [_jsx("h3", { className: "text-[14px] font-semibold text-ink mb-1.5", children: t("pages.intent.onboarding.sectionGuide.title") }), _jsx("p", { className: "text-ink-3 text-[12px] leading-relaxed mb-2", children: t("pages.intent.onboarding.sectionGuide.description") }), _jsx("ul", { className: "space-y-1.5", children: SECTION_STEPS.map((s) => {
|
|
107
|
+
const key = STEP_TO_KEY[s];
|
|
108
|
+
return (_jsxs("li", { className: "text-ink-2 text-[13px] leading-relaxed", children: [_jsx("span", { className: "font-mono text-gov", children: "\u2022" }), " ", t(`pages.intent.wizard.stepLabels.${key}`)] }, s));
|
|
109
|
+
}) })] })] })), typeof step === "number" && (_jsxs("div", { className: "mb-5", children: [_jsx("h3", { className: "text-[15px] font-semibold text-ink mb-1.5", children: t(`pages.intent.wizard.stepLabels.${STEP_TO_KEY[step]}`) }), _jsx("p", { className: "text-ink-3 text-[12px] leading-relaxed mb-3", children: t(`pages.intent.wizard.guidance.${STEP_TO_KEY[step]}`) }), _jsx("textarea", { ref: textareaRef, value: currentSectionValue, onChange: (e) => handleSectionChange(e.target.value), placeholder: t(`pages.intent.wizard.guidance.${STEP_TO_KEY[step]}`), className: "w-full h-[180px] bg-surface border border-line rounded-[4px] p-3 text-[13px] text-ink leading-relaxed resize-y focus:outline-none focus:border-gov focus:ring-1 focus:ring-gov/30", spellCheck: false, "aria-label": t(`pages.intent.wizard.stepLabels.${STEP_TO_KEY[step]}`) }), isCurrentSectionEmpty && (_jsx("p", { className: "text-amber text-[12px] mt-1.5", role: "alert", children: t("pages.intent.wizard.emptyWarning") }))] })), step === "finish" && (_jsxs("div", { className: "mb-5", children: [_jsx("h3", { className: "text-[15px] font-semibold text-ink mb-3", children: t("pages.intent.wizard.finish") }), _jsx("div", { className: "space-y-3", children: SECTION_STEPS.map((s) => {
|
|
110
|
+
const key = STEP_TO_KEY[s];
|
|
111
|
+
const value = sections[key] ?? "";
|
|
112
|
+
return (_jsxs("div", { className: "border-l-2 border-gov/40 pl-3", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-0.5", children: t(`pages.intent.wizard.stepLabels.${key}`) }), _jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed whitespace-pre-wrap", children: value || t("pages.intent.wizard.emptyWarning") })] }, s));
|
|
113
|
+
}) })] })), _jsxs("div", { className: "flex items-center justify-between gap-2 pt-3 border-t border-line", children: [_jsxs("div", { children: [step === "intro" && (_jsx("button", { type: "button", onClick: onSkip, className: "border border-line bg-surface text-ink-2 rounded-[4px] px-4 py-2 text-[13px] hover:border-line-2 transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.intent.onboarding.skipOption") })), typeof step === "number" && (_jsx("button", { type: "button", onClick: handlePrev, className: "border border-line bg-surface text-ink-2 rounded-[4px] px-4 py-2 text-[13px] hover:border-line-2 transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.intent.wizard.prev") }))] }), _jsxs("div", { children: [step === "intro" && (_jsx("button", { type: "button", onClick: handleNext, className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.intent.onboarding.startFilling") })), typeof step === "number" && step < 5 && (_jsx("button", { type: "button", onClick: handleNext, disabled: isCurrentSectionEmpty, className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.intent.wizard.next") })), step === 5 && (_jsx("button", { type: "button", onClick: handleNext, disabled: isCurrentSectionEmpty, className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.intent.wizard.review") })), step === "finish" && (_jsx("button", { type: "button", onClick: () => onComplete(sections), className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.intent.wizard.complete") }))] })] }), showDiscardConfirm && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-ink/40 backdrop-blur-sm", role: "alertdialog", "aria-modal": "true", "aria-labelledby": "discard-title", onClick: () => setShowDiscardConfirm(false), children: _jsxs("div", { className: "bg-surface border border-line rounded-[8px] p-5 max-w-[400px] w-full mx-4", onClick: (e) => e.stopPropagation(), children: [_jsx("p", { id: "discard-title", className: "text-ink text-[14px] font-semibold mb-2", children: t("pages.intent.wizard.discardConfirmTitle") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-4", children: t("pages.intent.wizard.discardConfirmBody") }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { type: "button", onClick: () => setShowDiscardConfirm(false), className: "border border-line bg-surface text-ink-2 rounded-[4px] px-3 py-1.5 text-[12px] hover:border-line-2 transition-colors", children: t("pages.intent.wizard.discardConfirmCancel") }), _jsx("button", { type: "button", onClick: () => {
|
|
114
|
+
setShowDiscardConfirm(false);
|
|
115
|
+
onClose();
|
|
116
|
+
}, className: "bg-red-600 text-white rounded-[4px] px-3 py-1.5 text-[12px] hover:bg-red-700 transition-colors", children: t("pages.intent.wizard.discardConfirmConfirm") })] })] }) }))] }) }));
|
|
117
|
+
}
|
|
118
|
+
export function IntentEditor({ initialContent, onSaved, onCancel, lang }) {
|
|
119
|
+
const { t } = useTranslation();
|
|
120
|
+
// Parse initial content into 5 sections on mount. parseIntentDocSections
|
|
121
|
+
// returns undefined for missing sections; we normalize to empty string for
|
|
122
|
+
// textarea value binding (controlled input requires a string value).
|
|
123
|
+
const [sections, setSections] = useState(() => {
|
|
124
|
+
const parsed = parseIntentDocSections(initialContent);
|
|
125
|
+
return {
|
|
126
|
+
why: parsed.why ?? "",
|
|
127
|
+
desiredOutcome: parsed.desiredOutcome ?? "",
|
|
128
|
+
nonNegotiables: parsed.nonNegotiables ?? "",
|
|
129
|
+
stopEscalation: parsed.stopEscalation ?? "",
|
|
130
|
+
currentStrategicFocus: parsed.currentStrategicFocus ?? "",
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
const [saving, setSaving] = useState(false);
|
|
134
|
+
const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);
|
|
135
|
+
const firstTextareaRef = useRef(null);
|
|
136
|
+
// Focus the first textarea on mount
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
firstTextareaRef.current?.focus();
|
|
139
|
+
}, []);
|
|
140
|
+
// Detect changes by comparing current sections to initial parsed sections.
|
|
141
|
+
// We re-parse initialContent on each render (cheap, pure function) rather
|
|
142
|
+
// than storing a second state variable — avoids stale closure issues.
|
|
143
|
+
const initialSections = useMemo(() => parseIntentDocSections(initialContent), [initialContent]);
|
|
144
|
+
const hasChanges = useMemo(() => {
|
|
145
|
+
for (const step of SECTION_STEPS) {
|
|
146
|
+
const key = STEP_TO_KEY[step];
|
|
147
|
+
const current = sections[key] ?? "";
|
|
148
|
+
const initial = initialSections[key] ?? "";
|
|
149
|
+
if (current !== initial)
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}, [sections, initialSections]);
|
|
154
|
+
function handleSectionChange(key, value) {
|
|
155
|
+
setSections((prev) => ({ ...prev, [key]: value }));
|
|
156
|
+
}
|
|
157
|
+
async function handleSave() {
|
|
158
|
+
if (!hasChanges || saving)
|
|
159
|
+
return;
|
|
160
|
+
// Client-side validation: require at least one non-empty section.
|
|
161
|
+
// Empty individual sections are allowed (user may clear a section
|
|
162
|
+
// intentionally); the server emits warnings, not errors, for empty
|
|
163
|
+
// sections.
|
|
164
|
+
const allEmpty = SECTION_STEPS.every((s) => {
|
|
165
|
+
const key = STEP_TO_KEY[s];
|
|
166
|
+
return (sections[key] ?? "").trim().length === 0;
|
|
167
|
+
});
|
|
168
|
+
if (allEmpty) {
|
|
169
|
+
toast.error(t("pages.intent.editor.emptyContent"));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const assembled = assembleIntentDoc(sections);
|
|
173
|
+
setSaving(true);
|
|
174
|
+
const result = await saveIntentContent(assembled, lang);
|
|
175
|
+
setSaving(false);
|
|
176
|
+
if (!result.success) {
|
|
177
|
+
// Branch on structured reason field, NOT substring matching (PR-1083 N4).
|
|
178
|
+
if (result.reason === "oversized") {
|
|
179
|
+
toast.error(t("pages.intent.editor.oversized"));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
toast.error(t("pages.intent.editor.saveFailed"));
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
toast.success(t("pages.intent.editor.saveSuccess"));
|
|
187
|
+
onSaved();
|
|
188
|
+
}
|
|
189
|
+
function handleCancel() {
|
|
190
|
+
if (hasChanges) {
|
|
191
|
+
setShowDiscardConfirm(true);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
onCancel();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return (_jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsx("div", { className: "flex items-center justify-between mb-4", children: _jsxs("div", { children: [_jsx("h3", { id: "section-editor", className: "text-[15px] font-semibold text-ink mb-0.5", children: t("pages.intent.editor.title") }), _jsx("p", { className: "text-ink-3 text-[12px] leading-relaxed", children: t("pages.intent.editor.description") })] }) }), _jsx("div", { className: "space-y-4", children: SECTION_STEPS.map((step, idx) => {
|
|
198
|
+
const key = STEP_TO_KEY[step];
|
|
199
|
+
const value = sections[key] ?? "";
|
|
200
|
+
return (_jsxs("div", { children: [_jsx("label", { htmlFor: `intent-section-${key}`, className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-1", children: t(`pages.intent.wizard.stepLabels.${key}`) }), _jsx("p", { className: "text-ink-3 text-[12px] leading-relaxed mb-1.5", children: t(`pages.intent.wizard.guidance.${key}`) }), _jsx("textarea", { ref: idx === 0 ? firstTextareaRef : undefined, id: `intent-section-${key}`, value: value, onChange: (e) => handleSectionChange(key, e.target.value), className: "w-full h-[120px] bg-surface border border-line rounded-[4px] p-3 text-[13px] text-ink leading-relaxed resize-y focus:outline-none focus:border-gov focus:ring-1 focus:ring-gov/30", spellCheck: false, "aria-label": t(`pages.intent.wizard.stepLabels.${key}`) })] }, step));
|
|
201
|
+
}) }), _jsxs("div", { className: "flex items-center justify-end gap-2 mt-4", children: [_jsx("button", { type: "button", onClick: handleCancel, disabled: saving, className: "border border-line bg-surface text-ink-2 rounded-[4px] px-4 py-2 text-[13px] hover:border-line-2 transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.intent.editor.cancel") }), _jsx("button", { type: "button", onClick: handleSave, disabled: !hasChanges || saving, className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", children: saving ? t("pages.intent.editor.saving") : t("pages.intent.editor.save") })] }), showDiscardConfirm && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-ink/40 backdrop-blur-sm", role: "alertdialog", "aria-modal": "true", "aria-labelledby": "discard-title", onClick: () => setShowDiscardConfirm(false), children: _jsxs("div", { className: "bg-surface border border-line rounded-[8px] p-5 max-w-[400px] w-full mx-4", onClick: (e) => e.stopPropagation(), children: [_jsx("p", { id: "discard-title", className: "text-ink text-[14px] mb-4", children: t("pages.intent.editor.confirmDiscard") }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { type: "button", onClick: () => setShowDiscardConfirm(false), className: "border border-line bg-surface text-ink-2 rounded-[4px] px-3 py-1.5 text-[12px] hover:border-line-2 transition-colors", children: t("pages.intent.editor.no") }), _jsx("button", { type: "button", onClick: () => {
|
|
202
|
+
setShowDiscardConfirm(false);
|
|
203
|
+
onCancel();
|
|
204
|
+
}, className: "bg-red-600 text-white rounded-[4px] px-3 py-1.5 text-[12px] hover:bg-red-700 transition-colors", children: t("pages.intent.editor.yes") })] })] }) }))] }));
|
|
205
|
+
}
|
|
206
|
+
export function CreateIntentButton({ onCreated, showOnboarding = false, lang }) {
|
|
207
|
+
const { t } = useTranslation();
|
|
208
|
+
const [busy, setBusy] = useState(false);
|
|
209
|
+
const [showOnboardingModal, setShowOnboardingModal] = useState(showOnboarding && !isOnboardingDismissed());
|
|
210
|
+
// Create template + save assembled content from wizard.
|
|
211
|
+
// Called when user completes all 5 wizard steps.
|
|
212
|
+
async function createFromWizard(sections) {
|
|
213
|
+
setBusy(true);
|
|
214
|
+
// Step 1: create template (idempotent — ensures file exists)
|
|
215
|
+
const createResult = await createIntentTemplate(false, lang);
|
|
216
|
+
if (!createResult.success) {
|
|
217
|
+
setBusy(false);
|
|
218
|
+
toast.error(t("pages.intent.notFound.createFailed"));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// Step 2: save the assembled content (overwrites template defaults)
|
|
222
|
+
const assembled = assembleIntentDoc(sections);
|
|
223
|
+
const saveResult = await saveIntentContent(assembled, lang);
|
|
224
|
+
setBusy(false);
|
|
225
|
+
if (!saveResult.success) {
|
|
226
|
+
// Template was created but wizard content failed to save.
|
|
227
|
+
// The file exists (template), so refresh summary; show error so the
|
|
228
|
+
// user knows to edit manually. This is NOT a silent fallback (rc-9).
|
|
229
|
+
if (saveResult.reason === "oversized") {
|
|
230
|
+
toast.error(t("pages.intent.editor.oversized"));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
toast.error(t("pages.intent.editor.saveFailed"));
|
|
234
|
+
}
|
|
235
|
+
onCreated(true); // open editor so user can retry / inspect
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
toast.success(t("pages.intent.notFound.createSuccess"));
|
|
239
|
+
onCreated(false); // wizard already filled content — no need to open editor
|
|
240
|
+
}
|
|
241
|
+
// Create empty template. Returns true on success so the caller can decide
|
|
242
|
+
// whether to open the editor (legacy path) or just refresh (skip path).
|
|
243
|
+
// Does NOT call onCreated itself — avoids double-callback bug.
|
|
244
|
+
async function createEmptyTemplate() {
|
|
245
|
+
setBusy(true);
|
|
246
|
+
const result = await createIntentTemplate(false, lang);
|
|
247
|
+
setBusy(false);
|
|
248
|
+
if (!result.success) {
|
|
249
|
+
toast.error(t("pages.intent.notFound.createFailed"));
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
toast.success(t("pages.intent.notFound.createSuccess"));
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
function handleComplete(sections) {
|
|
256
|
+
setShowOnboardingModal(false);
|
|
257
|
+
setOnboardingDismissed();
|
|
258
|
+
void createFromWizard(sections);
|
|
259
|
+
}
|
|
260
|
+
function handleSkip() {
|
|
261
|
+
setShowOnboardingModal(false);
|
|
262
|
+
setOnboardingDismissed();
|
|
263
|
+
void createEmptyTemplate().then((ok) => {
|
|
264
|
+
if (ok)
|
|
265
|
+
onCreated(false);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function handleClose() {
|
|
269
|
+
setShowOnboardingModal(false);
|
|
270
|
+
}
|
|
271
|
+
function handleClick() {
|
|
272
|
+
if (showOnboarding && !isOnboardingDismissed()) {
|
|
273
|
+
setShowOnboardingModal(true);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Legacy direct-create path (onboarding already dismissed or not enabled):
|
|
277
|
+
// create template then open editor so user can fill content manually.
|
|
278
|
+
void createEmptyTemplate().then((ok) => {
|
|
279
|
+
if (ok)
|
|
280
|
+
onCreated(true);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: handleClick, disabled: busy, className: "bg-gov text-paper rounded-[4px] px-4 py-2 text-[13px] font-medium hover:bg-gov-dark transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", children: busy ? t("pages.intent.notFound.creating") : t("pages.intent.notFound.createButton") }), showOnboardingModal && (_jsx(OnboardingModal, { onComplete: handleComplete, onSkip: handleSkip, onClose: handleClose }))] }));
|
|
285
|
+
}
|
|
286
|
+
export function EditButton({ onClick, loading = false }) {
|
|
287
|
+
const { t } = useTranslation();
|
|
288
|
+
return (_jsx("button", { type: "button", onClick: onClick, disabled: loading, "aria-busy": loading, className: "border border-line bg-surface text-ink-2 rounded-[4px] px-3 py-1.5 text-[12px] hover:border-line-2 transition-colors focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-line", children: loading ? t("common.loading") : t("pages.intent.editor.edit") }));
|
|
289
|
+
}
|
|
@@ -5,7 +5,9 @@ import { toast } from "sonner";
|
|
|
5
5
|
import { PageShell } from "../../components/layout/page-shell.js";
|
|
6
6
|
import { PageLoading } from "../../components/layout/page-loading.js";
|
|
7
7
|
import { SectionTitle } from "../../components/layout/section-title.js";
|
|
8
|
-
import { fetchIntentSummary, fetchIntentDecisionSummary, patchFeatureFlag } from "../../api.js";
|
|
8
|
+
import { fetchIntentSummary, fetchIntentDecisionSummary, patchFeatureFlag, fetchIntentContent, fetchIntentVersions } from "../../api.js";
|
|
9
|
+
import { IntentEditor, CreateIntentButton, EditButton } from "./IntentOnboarding.js";
|
|
10
|
+
import { computeVersionDiff } from "@principles/core/runtime-v2/intent-browser";
|
|
9
11
|
// ── Sub-components ────────────────────────────────────────────────────────────
|
|
10
12
|
function FlagStatusBadge({ enabled }) {
|
|
11
13
|
const { t } = useTranslation();
|
|
@@ -54,9 +56,9 @@ function FlagToggleCard({ flagEnabled, onAfterEnable }) {
|
|
|
54
56
|
}
|
|
55
57
|
}, className: "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 focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", "aria-label": t("pages.intent.flagStatus.enable"), children: busy ? t("pages.intent.flagStatus.enabling") : t("pages.intent.flagStatus.enable") })] }));
|
|
56
58
|
}
|
|
57
|
-
function NotFoundBanner() {
|
|
59
|
+
function NotFoundBanner({ onCreated, lang }) {
|
|
58
60
|
const { t } = useTranslation();
|
|
59
|
-
return (_jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsx("h2", { className: "text-ink text-[15px] font-semibold mb-2", children: t("pages.intent.notFound.title") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3", children: t("pages.intent.notFound.description") }),
|
|
61
|
+
return (_jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsx("h2", { className: "text-ink text-[15px] font-semibold mb-2", children: t("pages.intent.notFound.title") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3", children: t("pages.intent.notFound.description") }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(CreateIntentButton, { onCreated: onCreated, showOnboarding: true, lang: lang }), _jsx("span", { className: "text-ink-4 text-[12px] font-mono", children: t("pages.intent.notFound.nextAction") })] })] }));
|
|
60
62
|
}
|
|
61
63
|
function OversizedBanner() {
|
|
62
64
|
const { t } = useTranslation();
|
|
@@ -77,6 +79,34 @@ function SectionContent({ label, content }) {
|
|
|
77
79
|
function MetaRow({ label, value }) {
|
|
78
80
|
return (_jsxs("div", { className: "flex items-baseline gap-3", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 w-32 shrink-0", children: label }), _jsx("span", { className: "text-ink-2 text-[12px] font-mono break-all", children: value })] }));
|
|
79
81
|
}
|
|
82
|
+
function VersionEntry({ version, previousContent, reasonLabel, defaultExpanded = false }) {
|
|
83
|
+
const { t } = useTranslation();
|
|
84
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
85
|
+
// Compute section-level diff between previous and this version.
|
|
86
|
+
// For the oldest version (no previous), diff against "" so all non-empty
|
|
87
|
+
// sections show as "changed" — gives the user a visual of what was filled.
|
|
88
|
+
const diff = computeVersionDiff(previousContent, version.contentSnapshot);
|
|
89
|
+
// Localize the timestamp for display (avoid showing raw UTC to end users).
|
|
90
|
+
const localizedTime = (() => {
|
|
91
|
+
try {
|
|
92
|
+
return new Date(version.createdAt).toLocaleString(undefined, {
|
|
93
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
94
|
+
hour: '2-digit', minute: '2-digit',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return version.createdAt.slice(0, 19).replace('T', ' ');
|
|
99
|
+
}
|
|
100
|
+
})();
|
|
101
|
+
return (_jsxs("div", { className: "border-b border-line last:border-b-0", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "w-full flex items-center gap-3 py-1.5 text-[12px] font-mono text-ink-2 hover:bg-surface-2/50 transition-colors text-left", "aria-expanded": expanded, "aria-label": expanded ? t("pages.intent.versionDiff.collapseHint") : t("pages.intent.versionDiff.expandHint"), title: version.contentHash, children: [_jsx("span", { className: "text-ink-3 shrink-0 w-4 inline-block", children: expanded ? "▾" : "▸" }), _jsx("span", { className: "text-ink-3 shrink-0", children: localizedTime }), _jsx("span", { className: "px-1.5 py-0.5 rounded-[2px] bg-surface-2 text-ink-3 shrink-0", children: reasonLabel }), _jsx("span", { className: "text-ink-4 text-[11px] ml-auto shrink-0 hidden sm:inline", children: expanded ? t("pages.intent.versionDiff.collapseHint") : t("pages.intent.versionDiff.expandHint") })] }), expanded && (_jsx("div", { className: "pb-2.5 pl-7 pr-2", children: _jsx("div", { className: "flex flex-wrap gap-1.5 mb-2", children: diff.map((d) => {
|
|
102
|
+
// d.section is one of the 5 section keys from computeVersionDiff's
|
|
103
|
+
// SECTION_KEYS — safe to construct the i18n key dynamically.
|
|
104
|
+
const labelKey = `pages.intent.wizard.stepLabels.${d.section}`;
|
|
105
|
+
return (_jsxs("span", { className: `inline-flex items-center gap-1 px-1.5 py-0.5 rounded-[2px] text-[11px] font-mono border ${d.changed
|
|
106
|
+
? "border-amber/40 text-amber bg-amber/5"
|
|
107
|
+
: "border-line text-ink-3 bg-surface"}`, children: [_jsx("span", { className: `w-1.5 h-1.5 rounded-full ${d.changed ? "bg-amber" : "bg-ink-4"}`, "aria-hidden": "true" }), t(labelKey), _jsx("span", { className: "text-ink-4 ml-0.5", children: d.changed ? t("pages.intent.versionDiff.changed") : t("pages.intent.versionDiff.unchanged") })] }, d.section));
|
|
108
|
+
}) }) }))] }));
|
|
109
|
+
}
|
|
80
110
|
// Static mapping from ownerAction enum value to its i18n label key.
|
|
81
111
|
// Avoids dynamic string construction so i18n extraction tools can find keys.
|
|
82
112
|
const DECISION_COUNT_LABEL_KEYS = [
|
|
@@ -116,10 +146,27 @@ export function IntentPage() {
|
|
|
116
146
|
const [data, setData] = useState(null);
|
|
117
147
|
const [pageState, setPageState] = useState("loading");
|
|
118
148
|
const [errorMsg, setErrorMsg] = useState(null);
|
|
149
|
+
// PRI-477: inline editor state
|
|
150
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
151
|
+
const [editorContent, setEditorContent] = useState("");
|
|
152
|
+
const [editorLoading, setEditorLoading] = useState(false);
|
|
153
|
+
// Bilingual: language selector state
|
|
154
|
+
const [lang, setLang] = useState('zh-CN');
|
|
155
|
+
// Version history
|
|
156
|
+
const [versions, setVersions] = useState([]);
|
|
157
|
+
// Map raw backend reason codes to localized labels for the version history panel.
|
|
158
|
+
// Unknown reasons fall back to the raw string (observability over silent masking).
|
|
159
|
+
const versionReasonLabel = useCallback((reason) => {
|
|
160
|
+
if (reason === 'init')
|
|
161
|
+
return t("pages.intent.versionHistory.reasonInit");
|
|
162
|
+
if (reason === 'save')
|
|
163
|
+
return t("pages.intent.versionHistory.reasonSave");
|
|
164
|
+
return reason;
|
|
165
|
+
}, [t]);
|
|
119
166
|
const loadData = useCallback(async () => {
|
|
120
167
|
setPageState("loading");
|
|
121
168
|
setErrorMsg(null);
|
|
122
|
-
const result = await fetchIntentSummary();
|
|
169
|
+
const result = await fetchIntentSummary(lang);
|
|
123
170
|
if (!result.success) {
|
|
124
171
|
// ERR-002: graceful degradation with reason
|
|
125
172
|
setErrorMsg(result.error ?? t("pages.intent.loadError"));
|
|
@@ -129,10 +176,68 @@ export function IntentPage() {
|
|
|
129
176
|
// result.data is already validated by validateIntentSummary in the API layer
|
|
130
177
|
setData(result.data);
|
|
131
178
|
setPageState("loaded");
|
|
132
|
-
|
|
179
|
+
// Load version history in parallel (best-effort, never blocks page)
|
|
180
|
+
fetchIntentVersions(lang).then(v => {
|
|
181
|
+
if (v.success && v.data?.versions) {
|
|
182
|
+
setVersions(v.data.versions);
|
|
183
|
+
}
|
|
184
|
+
}).catch(() => { });
|
|
185
|
+
}, [t, lang]);
|
|
133
186
|
useEffect(() => {
|
|
134
187
|
loadData();
|
|
135
188
|
}, [loadData]);
|
|
189
|
+
// P0 fix: when language changes, reset editor state and stale versions.
|
|
190
|
+
// Without this, switching language while editing would keep the editor open
|
|
191
|
+
// with the old language's content — and saving would write that content
|
|
192
|
+
// into the NEW language's file (data corruption).
|
|
193
|
+
// Also clears stale versions so the panel doesn't show the old language's
|
|
194
|
+
// history while the new language's fetch is in flight or has failed.
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
setIsEditing(false);
|
|
197
|
+
setEditorContent("");
|
|
198
|
+
setVersions([]);
|
|
199
|
+
}, [lang]);
|
|
200
|
+
// PRI-477: Called when user clicks "Edit" — fetch raw content and open editor
|
|
201
|
+
const handleStartEdit = useCallback(async () => {
|
|
202
|
+
setEditorLoading(true);
|
|
203
|
+
const result = await fetchIntentContent(lang);
|
|
204
|
+
setEditorLoading(false);
|
|
205
|
+
if (!result.success) {
|
|
206
|
+
toast.error(t("pages.intent.loadError"));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
setEditorContent(result.data.content);
|
|
210
|
+
setIsEditing(true);
|
|
211
|
+
}, [t, lang]);
|
|
212
|
+
// PRI-477: Called when template is created — refresh summary; optionally
|
|
213
|
+
// open the editor immediately so the user can start filling.
|
|
214
|
+
//
|
|
215
|
+
// PR-1083 review (CodeRabbit A5): `openEditor` lets CreateIntentButton
|
|
216
|
+
// distinguish "Start filling" (open editor) from "Skip" (just refresh the
|
|
217
|
+
// summary so NotFoundBanner re-renders to the sections view — otherwise the
|
|
218
|
+
// user would see a stale "file not found" banner even though INTENT.md now
|
|
219
|
+
// exists on disk).
|
|
220
|
+
const handleCreated = useCallback(async (openEditor) => {
|
|
221
|
+
await loadData();
|
|
222
|
+
if (!openEditor)
|
|
223
|
+
return;
|
|
224
|
+
setEditorLoading(true);
|
|
225
|
+
const result = await fetchIntentContent(lang);
|
|
226
|
+
setEditorLoading(false);
|
|
227
|
+
if (result.success) {
|
|
228
|
+
setEditorContent(result.data.content);
|
|
229
|
+
setIsEditing(true);
|
|
230
|
+
}
|
|
231
|
+
}, [loadData, lang]);
|
|
232
|
+
// PRI-477: Called after successful save — close editor and refresh summary
|
|
233
|
+
const handleSaved = useCallback(() => {
|
|
234
|
+
setIsEditing(false);
|
|
235
|
+
void loadData();
|
|
236
|
+
}, [loadData]);
|
|
237
|
+
// PRI-477: Called when user cancels editing
|
|
238
|
+
const handleCancelEdit = useCallback(() => {
|
|
239
|
+
setIsEditing(false);
|
|
240
|
+
}, []);
|
|
136
241
|
// ── Loading state ────────────────────────────────────────────────────────
|
|
137
242
|
if (pageState === "loading") {
|
|
138
243
|
return (_jsx(PageShell, { children: _jsx(PageLoading, { cardCount: 3, label: t("common.loading") }) }));
|
|
@@ -143,5 +248,11 @@ export function IntentPage() {
|
|
|
143
248
|
}
|
|
144
249
|
// ── Loaded state ─────────────────────────────────────────────────────────
|
|
145
250
|
const summary = data;
|
|
146
|
-
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.intent.eyebrow") }), _jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink", children: t("pages.intent.title") }), summary && (_jsx(FlagStatusBadge, { enabled: summary.flagEnabled }))] }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.intent.subtitle") }), summary && !summary.flagEnabled && (_jsx(FlagToggleCard, { flagEnabled: summary.flagEnabled, onAfterEnable: loadData })), summary && summary.flagEnabled && !summary.found && summary.reason === "not_found" && (_jsx(NotFoundBanner, {})), summary && summary.flagEnabled && summary.found && summary.reason === "oversized" && (_jsx(OversizedBanner, {})), summary && summary.flagEnabled && summary.ok && summary.sections && (_jsxs(_Fragment, { children: [_jsxs("section", { "aria-labelledby": "section-content", children: [_jsx(SectionTitle, { id: "section-content", children: t("pages.intent.title") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5 space-y-5", children: [_jsx(SectionContent, { label: t("pages.intent.sections.why"), content: summary.sections.why }), _jsx(SectionContent, { label: t("pages.intent.sections.desiredOutcome"), content: summary.sections.desiredOutcome }), _jsx(SectionContent, { label: t("pages.intent.sections.nonNegotiables"), content: summary.sections.nonNegotiables }), _jsx(SectionContent, { label: t("pages.intent.sections.stopEscalation"), content: summary.sections.stopEscalation }), _jsx(SectionContent, { label: t("pages.intent.sections.currentStrategicFocus"), content: summary.sections.currentStrategicFocus })] })] }), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-warnings", children: [_jsx(SectionTitle, { id: "section-warnings", children: t("pages.intent.warnings.title") }), summary.warnings.length === 0 ? (_jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed", children: t("pages.intent.warnings.empty") })) : (_jsx("ul", { className: "bg-panel border border-line rounded-[6px] p-4 space-y-2", children: summary.warnings.map((w, i) => (_jsx(WarningItem, { warning: w }, i))) }))] }), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-meta", children: [_jsx(SectionTitle, { id: "section-meta", children: t("pages.intent.meta.title") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-4 space-y-2", children: [summary.lastEditedAt && (_jsx(MetaRow, { label: t("pages.intent.meta.lastEditedAt"), value: summary.lastEditedAt })), summary.contentHash && (_jsx(MetaRow, { label: t("pages.intent.meta.contentHash"), value: summary.contentHash })), summary.path && (_jsx(MetaRow, { label: t("pages.intent.meta.path"), value: summary.path }))] })] }), summary.path && (_jsxs("section", { className: "mt-8", "aria-labelledby": "section-edit", children: [_jsx(SectionTitle, { id: "section-edit", children: t("pages.intent.editEntry.heading") }), _jsx(EditEntry, { filePath: summary.path })] }))] })), summary && summary.flagEnabled && !summary.ok && summary.reason && summary.reason !== "not_found" && summary.reason !== "oversized" && (_jsxs("div", { className: "bg-panel border border-red-200 rounded-[6px] p-5", children: [_jsx("h2", { className: "text-ink text-[15px] font-semibold mb-2", children: t("pages.intent.loadError") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3 font-mono", children: summary.reason }), summary.nextAction && (_jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed", children: summary.nextAction }))] })), summary && summary.flagEnabled && (_jsx(DecisionSummarySection, {}))]
|
|
251
|
+
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.intent.eyebrow") }), _jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink", children: t("pages.intent.title") }), summary && (_jsx(FlagStatusBadge, { enabled: summary.flagEnabled })), _jsxs("select", { value: lang, onChange: (e) => setLang(e.target.value), disabled: isEditing || editorLoading, className: "ml-auto border border-line bg-surface text-ink-2 rounded-[3px] px-2 py-1 text-[12px] font-mono focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed", "aria-label": t("pages.intent.langSelector.ariaLabel"), title: isEditing || editorLoading ? t("pages.intent.langSelector.disabledWhileEditing") : undefined, children: [_jsx("option", { value: "zh-CN", children: "\u4E2D\u6587" }), _jsx("option", { value: "en", children: "English" })] })] }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.intent.subtitle") }), summary && !summary.flagEnabled && (_jsx(FlagToggleCard, { flagEnabled: summary.flagEnabled, onAfterEnable: loadData })), summary && summary.flagEnabled && !summary.found && summary.reason === "not_found" && (_jsx(NotFoundBanner, { onCreated: handleCreated, lang: lang })), summary && summary.flagEnabled && summary.found && summary.reason === "oversized" && (_jsx(OversizedBanner, {})), summary && summary.flagEnabled && summary.ok && summary.sections && (_jsxs(_Fragment, { children: [isEditing && (_jsx("section", { "aria-labelledby": "section-editor", className: "mb-8", children: _jsx(IntentEditor, { initialContent: editorContent, onSaved: handleSaved, onCancel: handleCancelEdit, lang: lang }) })), !isEditing && (_jsxs(_Fragment, { children: [_jsxs("section", { "aria-labelledby": "section-content", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx(SectionTitle, { id: "section-content", children: t("pages.intent.title") }), _jsx(EditButton, { onClick: handleStartEdit, loading: editorLoading })] }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5 space-y-5", children: [_jsx(SectionContent, { label: t("pages.intent.sections.why"), content: summary.sections.why }), _jsx(SectionContent, { label: t("pages.intent.sections.desiredOutcome"), content: summary.sections.desiredOutcome }), _jsx(SectionContent, { label: t("pages.intent.sections.nonNegotiables"), content: summary.sections.nonNegotiables }), _jsx(SectionContent, { label: t("pages.intent.sections.stopEscalation"), content: summary.sections.stopEscalation }), _jsx(SectionContent, { label: t("pages.intent.sections.currentStrategicFocus"), content: summary.sections.currentStrategicFocus })] })] }), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-warnings", children: [_jsx(SectionTitle, { id: "section-warnings", children: t("pages.intent.warnings.title") }), summary.warnings.length === 0 ? (_jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed", children: t("pages.intent.warnings.empty") })) : (_jsx("ul", { className: "bg-panel border border-line rounded-[6px] p-4 space-y-2", children: summary.warnings.map((w, i) => (_jsx(WarningItem, { warning: w }, i))) }))] }), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-meta", children: [_jsx(SectionTitle, { id: "section-meta", children: t("pages.intent.meta.title") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-4 space-y-2", children: [summary.lastEditedAt && (_jsx(MetaRow, { label: t("pages.intent.meta.lastEditedAt"), value: summary.lastEditedAt })), summary.contentHash && (_jsx(MetaRow, { label: t("pages.intent.meta.contentHash"), value: summary.contentHash })), summary.path && (_jsx(MetaRow, { label: t("pages.intent.meta.path"), value: summary.path }))] })] }), summary.path && (_jsxs("section", { className: "mt-8", "aria-labelledby": "section-edit", children: [_jsx(SectionTitle, { id: "section-edit", children: t("pages.intent.editEntry.heading") }), _jsx(EditEntry, { filePath: summary.path })] }))] }))] })), summary && summary.flagEnabled && !summary.ok && summary.reason && summary.reason !== "not_found" && summary.reason !== "oversized" && (_jsxs("div", { className: "bg-panel border border-red-200 rounded-[6px] p-5", children: [_jsx("h2", { className: "text-ink text-[15px] font-semibold mb-2", children: t("pages.intent.loadError") }), _jsx("p", { className: "text-ink-3 text-[13px] leading-relaxed mb-3 font-mono", children: summary.reason }), summary.nextAction && (_jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed", children: summary.nextAction }))] })), summary && summary.flagEnabled && (_jsx(DecisionSummarySection, {})), versions.length > 0 && (_jsxs("details", { className: "border border-line rounded-[3px] mt-6", children: [_jsxs("summary", { className: "cursor-pointer px-4 py-2 text-[13px] font-medium text-ink-2 select-none", children: [t("pages.intent.versionHistory.title"), " (", versions.length, ")"] }), _jsx("div", { className: "px-4 py-2 border-t border-line", children: versions.map((v, idx) => {
|
|
252
|
+
// versions are sorted newest-first; previous = idx + 1.
|
|
253
|
+
// For the oldest version (no previous), diff against "" so
|
|
254
|
+
// all filled sections show as "changed".
|
|
255
|
+
const previousContent = versions[idx + 1]?.contentSnapshot ?? "";
|
|
256
|
+
return (_jsx(VersionEntry, { version: v, previousContent: previousContent, reasonLabel: versionReasonLabel(v.reason), defaultExpanded: idx === 0 }, v.id));
|
|
257
|
+
}) })] }))] }) }));
|
|
147
258
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Metadata — Control Center agent grouping & role descriptions
|
|
3
|
+
*
|
|
4
|
+
* Pure data module (no React, no I/O). Defines:
|
|
5
|
+
* - Which dependency cluster each of the 9 internal agents belongs to
|
|
6
|
+
* - Bilingual role / detail / impact / tech-detail text for progressive disclosure
|
|
7
|
+
* - Whether toggling the agent off requires inline confirmation (isCore)
|
|
8
|
+
*
|
|
9
|
+
* Content source: design-prototype/control-center.html (approved prototype).
|
|
10
|
+
* Agent names match INTERNAL_AGENT_NAMES from @principles/core (camelCase).
|
|
11
|
+
*
|
|
12
|
+
* ERR entries:
|
|
13
|
+
* - rc-1: pure static data, no untrusted input
|
|
14
|
+
* - rc-2: no `as` bypasses
|
|
15
|
+
* - rc-5: consumers use Object.hasOwn() to look up metadata by agent name
|
|
16
|
+
*/
|
|
17
|
+
import type { InternalAgentName } from '@principles/core/runtime-v2';
|
|
18
|
+
export type AgentGroup = 'core_trio' | 'code_chain' | 'quality_polish' | 'sidechain';
|
|
19
|
+
export type ImpactLevel = 'danger' | 'amber' | 'green';
|
|
20
|
+
export type GroupTag = 'must' | 'recommend' | 'optional' | 'independent';
|
|
21
|
+
export interface AgentMeta {
|
|
22
|
+
name: InternalAgentName;
|
|
23
|
+
group: AgentGroup;
|
|
24
|
+
/** 中文角色名(如"诊断师") */
|
|
25
|
+
displayNameZh: string;
|
|
26
|
+
displayNameEn: string;
|
|
27
|
+
/** 一句话角色(L1 折叠态显示) */
|
|
28
|
+
roleZh: string;
|
|
29
|
+
roleEn: string;
|
|
30
|
+
/** 展开后的详细说明(L2),段落用 \n\n 分隔 */
|
|
31
|
+
detailZh: string;
|
|
32
|
+
detailEn: string;
|
|
33
|
+
/** 关掉会怎样(L2 impact box) */
|
|
34
|
+
impactLevel: ImpactLevel;
|
|
35
|
+
impactZh: string;
|
|
36
|
+
impactEn: string;
|
|
37
|
+
/** 技术细节(L3 嵌套折叠)— 键为标签,值可含反引号包裹的 code 片段 */
|
|
38
|
+
techDetailZh: Record<string, string>;
|
|
39
|
+
techDetailEn: Record<string, string>;
|
|
40
|
+
/** 是否核心代理(关掉需 inline confirm)。true 对应原型 data-core="true" */
|
|
41
|
+
isCore: boolean;
|
|
42
|
+
/** MVP 状态说明(可选,仅 philosopher/rolloutReviewer 有) */
|
|
43
|
+
mvpNoteZh?: string;
|
|
44
|
+
mvpNoteEn?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface AgentGroupMeta {
|
|
47
|
+
id: AgentGroup;
|
|
48
|
+
labelZh: string;
|
|
49
|
+
labelEn: string;
|
|
50
|
+
tag: GroupTag;
|
|
51
|
+
tagLabelZh: string;
|
|
52
|
+
tagLabelEn: string;
|
|
53
|
+
hintZh: string;
|
|
54
|
+
hintEn: string;
|
|
55
|
+
}
|
|
56
|
+
export declare const GROUP_ORDER: AgentGroup[];
|
|
57
|
+
export declare const AGENT_GROUPS: AgentGroupMeta[];
|
|
58
|
+
export declare const AGENT_METADATA: Record<InternalAgentName, AgentMeta>;
|