@garygentry/feature-forge 0.2.13 → 0.3.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/README.md +6 -3
- package/adapters/GENERATION-REPORT.md +20 -0
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/references/epic-manifest-schema.json +5 -0
- package/adapters/claude/references/forge-config-schema.json +2 -2
- package/adapters/claude/references/shared-conventions.md +12 -0
- package/adapters/claude/references/stacks/_generic.md +21 -0
- package/adapters/claude/references/stacks/go.md +19 -0
- package/adapters/claude/references/stacks/python.md +21 -0
- package/adapters/claude/references/stacks/rust.md +19 -0
- package/adapters/claude/references/stacks/typescript.md +23 -0
- package/adapters/claude/scripts/epic-manifest.py +75 -5
- package/adapters/claude/scripts/forge-root.sh +102 -14
- package/adapters/claude/scripts/forge-session.py +68 -9
- package/adapters/claude/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-2-tech/SKILL.md +2 -0
- package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/claude/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-verify/SKILL.md +3 -3
- package/adapters/claude/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/references/epic-manifest-schema.json +5 -0
- package/adapters/codex/references/forge-config-schema.json +2 -2
- package/adapters/codex/references/shared-conventions.md +12 -0
- package/adapters/codex/references/stacks/_generic.md +21 -0
- package/adapters/codex/references/stacks/go.md +19 -0
- package/adapters/codex/references/stacks/python.md +21 -0
- package/adapters/codex/references/stacks/rust.md +19 -0
- package/adapters/codex/references/stacks/typescript.md +23 -0
- package/adapters/codex/scripts/epic-manifest.py +75 -5
- package/adapters/codex/scripts/forge-root.sh +102 -14
- package/adapters/codex/scripts/forge-session.py +68 -9
- package/adapters/codex/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-2-tech/SKILL.md +2 -0
- package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/codex/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-verify/SKILL.md +3 -3
- package/adapters/codex/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/references/epic-manifest-schema.json +5 -0
- package/adapters/copilot/references/forge-config-schema.json +2 -2
- package/adapters/copilot/references/shared-conventions.md +12 -0
- package/adapters/copilot/references/stacks/_generic.md +21 -0
- package/adapters/copilot/references/stacks/go.md +19 -0
- package/adapters/copilot/references/stacks/python.md +21 -0
- package/adapters/copilot/references/stacks/rust.md +19 -0
- package/adapters/copilot/references/stacks/typescript.md +23 -0
- package/adapters/copilot/scripts/epic-manifest.py +75 -5
- package/adapters/copilot/scripts/forge-root.sh +102 -14
- package/adapters/copilot/scripts/forge-session.py +68 -9
- package/adapters/copilot/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +2 -0
- package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-verify/forge-verify.md +3 -3
- package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/references/epic-manifest-schema.json +5 -0
- package/adapters/cursor/references/forge-config-schema.json +2 -2
- package/adapters/cursor/references/shared-conventions.md +12 -0
- package/adapters/cursor/references/stacks/_generic.md +21 -0
- package/adapters/cursor/references/stacks/go.md +19 -0
- package/adapters/cursor/references/stacks/python.md +21 -0
- package/adapters/cursor/references/stacks/rust.md +19 -0
- package/adapters/cursor/references/stacks/typescript.md +23 -0
- package/adapters/cursor/scripts/epic-manifest.py +75 -5
- package/adapters/cursor/scripts/forge-root.sh +102 -14
- package/adapters/cursor/scripts/forge-session.py +68 -9
- package/adapters/cursor/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +2 -0
- package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +2 -0
- package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +2 -0
- package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +5 -0
- package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +7 -9
- package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +3 -3
- package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/epic-manifest-schema.json +5 -0
- package/adapters/gemini/references/forge-config-schema.json +2 -2
- package/adapters/gemini/references/shared-conventions.md +12 -0
- package/adapters/gemini/references/stacks/_generic.md +21 -0
- package/adapters/gemini/references/stacks/go.md +19 -0
- package/adapters/gemini/references/stacks/python.md +21 -0
- package/adapters/gemini/references/stacks/rust.md +19 -0
- package/adapters/gemini/references/stacks/typescript.md +23 -0
- package/adapters/gemini/scripts/epic-manifest.py +75 -5
- package/adapters/gemini/scripts/forge-root.sh +102 -14
- package/adapters/gemini/scripts/forge-session.py +68 -9
- package/adapters/gemini/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +2 -0
- package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-verify/forge-verify.md +3 -3
- package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/pi/.feature-forge-bundle.json +6 -0
- package/adapters/pi/agents/forge-researcher.md +139 -0
- package/adapters/pi/agents/forge-spec-writer.md +116 -0
- package/adapters/pi/agents/forge-verifier.md +126 -0
- package/adapters/pi/extensions/ask-user-question/LICENSE +21 -0
- package/adapters/pi/extensions/ask-user-question/README.md +91 -0
- package/adapters/pi/extensions/ask-user-question/ask-user-question.ts +298 -0
- package/adapters/pi/extensions/ask-user-question/config.ts +78 -0
- package/adapters/pi/extensions/ask-user-question/events.ts +57 -0
- package/adapters/pi/extensions/ask-user-question/index.ts +61 -0
- package/adapters/pi/extensions/ask-user-question/locales/de.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/en.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/es.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/fr.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/pt-BR.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/pt.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/ru.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/uk.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/zh.json +29 -0
- package/adapters/pi/extensions/ask-user-question/reconcile.ts +49 -0
- package/adapters/pi/extensions/ask-user-question/rpc-fallback.ts +168 -0
- package/adapters/pi/extensions/ask-user-question/state/build-questionnaire.ts +302 -0
- package/adapters/pi/extensions/ask-user-question/state/i18n-bridge.ts +53 -0
- package/adapters/pi/extensions/ask-user-question/state/key-router.ts +277 -0
- package/adapters/pi/extensions/ask-user-question/state/questionnaire-session.ts +234 -0
- package/adapters/pi/extensions/ask-user-question/state/row-intent.ts +145 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/contract.ts +26 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/derivations.ts +42 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/focus.ts +19 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/projections.ts +101 -0
- package/adapters/pi/extensions/ask-user-question/state/state-reducer.ts +292 -0
- package/adapters/pi/extensions/ask-user-question/state/state.ts +55 -0
- package/adapters/pi/extensions/ask-user-question/tool/format-answer.ts +31 -0
- package/adapters/pi/extensions/ask-user-question/tool/response-envelope.ts +49 -0
- package/adapters/pi/extensions/ask-user-question/tool/types.ts +147 -0
- package/adapters/pi/extensions/ask-user-question/tool/validate-questionnaire.ts +58 -0
- package/adapters/pi/extensions/ask-user-question/vendor-config-shim.ts +65 -0
- package/adapters/pi/extensions/ask-user-question/view/component-binding.ts +47 -0
- package/adapters/pi/extensions/ask-user-question/view/components/inline-input.ts +98 -0
- package/adapters/pi/extensions/ask-user-question/view/components/multi-select-view.ts +193 -0
- package/adapters/pi/extensions/ask-user-question/view/components/option-list-view.ts +70 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/markdown-content-cache.ts +79 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-block-renderer.ts +111 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-box-renderer.ts +88 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-layout-decider.ts +202 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-pane.ts +228 -0
- package/adapters/pi/extensions/ask-user-question/view/components/submit-picker.ts +67 -0
- package/adapters/pi/extensions/ask-user-question/view/components/tab-bar.ts +59 -0
- package/adapters/pi/extensions/ask-user-question/view/components/wrapping-select.ts +293 -0
- package/adapters/pi/extensions/ask-user-question/view/dialog-builder.ts +224 -0
- package/adapters/pi/extensions/ask-user-question/view/props-adapter.ts +125 -0
- package/adapters/pi/extensions/ask-user-question/view/stateful-view.ts +26 -0
- package/adapters/pi/extensions/ask-user-question/view/tab-components.ts +18 -0
- package/adapters/pi/extensions/ask-user-question/view/tab-content-strategy.ts +252 -0
- package/adapters/pi/package.json +26 -0
- package/adapters/pi/references/epic-manifest-schema.json +125 -0
- package/adapters/pi/references/forge-config-schema.json +236 -0
- package/adapters/pi/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/references/portable-root.md +71 -0
- package/adapters/pi/references/process-overview.md +143 -0
- package/adapters/pi/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/references/shared-conventions.md +295 -0
- package/adapters/pi/references/skill-frontmatter.schema.json +17 -0
- package/adapters/pi/references/stack-resolution.md +54 -0
- package/adapters/pi/references/stacks/_generic.md +111 -0
- package/adapters/pi/references/stacks/go.md +157 -0
- package/adapters/pi/references/stacks/python.md +184 -0
- package/adapters/pi/references/stacks/rust.md +170 -0
- package/adapters/pi/references/stacks/typescript.md +134 -0
- package/adapters/pi/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/references/templates/specs-hygiene/AGENTS.md +32 -0
- package/adapters/pi/references/templates/specs-hygiene/CLAUDE.md +31 -0
- package/adapters/pi/references/vendor-construct-inventory.md +50 -0
- package/adapters/pi/scripts/epic-manifest.py +1694 -0
- package/adapters/pi/scripts/forge-bootstrap.py +1070 -0
- package/adapters/pi/scripts/forge-init.sh +58 -0
- package/adapters/pi/scripts/forge-root.sh +179 -0
- package/adapters/pi/scripts/forge-session.py +1888 -0
- package/adapters/pi/scripts/validate-traceability.py +150 -0
- package/adapters/pi/skills/forge/SKILL.md +243 -0
- package/adapters/pi/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge/references/process-overview.md +143 -0
- package/adapters/pi/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-0-epic/SKILL.md +308 -0
- package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +266 -0
- package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +75 -0
- package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-1-prd/SKILL.md +164 -0
- package/adapters/pi/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-1-prd/references/prd-template.md +106 -0
- package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-2-tech/SKILL.md +225 -0
- package/adapters/pi/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
- package/adapters/pi/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-3-specs/SKILL.md +178 -0
- package/adapters/pi/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-3-specs/references/spec-archetypes.md +106 -0
- package/adapters/pi/skills/forge-3-specs/references/spec-examples.md +71 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-4-backlog/SKILL.md +175 -0
- package/adapters/pi/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-5-loop/SKILL.md +314 -0
- package/adapters/pi/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +85 -0
- package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +341 -0
- package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-6-docs/SKILL.md +202 -0
- package/adapters/pi/skills/forge-6-docs/references/doc-conventions.md +126 -0
- package/adapters/pi/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-bootstrap/SKILL.md +250 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +35 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +36 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/pi/skills/forge-fix/SKILL.md +98 -0
- package/adapters/pi/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-guide/SKILL.md +192 -0
- package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/pi/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-init/SKILL.md +72 -0
- package/adapters/pi/skills/forge-verify/SKILL.md +273 -0
- package/adapters/pi/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists.md +477 -0
- package/dist/agent-targets.d.ts +1 -1
- package/dist/agent-targets.js +23 -3
- package/dist/detect.d.ts +1 -1
- package/dist/detect.js +2 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +2 -2
- package/dist/placements.js +5 -1
- package/dist/rauf.d.ts +4 -4
- package/dist/rauf.js +3 -3
- package/dist/types.d.ts +31 -6
- package/dist/types.js +6 -3
- package/package.json +14 -3
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/components/preview/preview-pane.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import { type Component, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
|
+
import type { QuestionData } from "../../../tool/types.js";
|
|
5
|
+
import type { StatefulView } from "../../stateful-view.js";
|
|
6
|
+
import type { OptionListView } from "../option-list-view.js";
|
|
7
|
+
import type { PreviewBlockRenderer } from "./preview-block-renderer.js";
|
|
8
|
+
import {
|
|
9
|
+
bodyWidths,
|
|
10
|
+
columnWidths,
|
|
11
|
+
decideLayout,
|
|
12
|
+
PREVIEW_PADDING_LEFT,
|
|
13
|
+
type PreviewLayoutMode,
|
|
14
|
+
STACKED_GAP_ROWS,
|
|
15
|
+
} from "./preview-layout-decider.js";
|
|
16
|
+
|
|
17
|
+
// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----
|
|
18
|
+
export {
|
|
19
|
+
MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,
|
|
20
|
+
MAX_PREVIEW_HEIGHT_STACKED,
|
|
21
|
+
NO_PREVIEW_TEXT,
|
|
22
|
+
NOTES_AFFORDANCE_OVERHEAD,
|
|
23
|
+
} from "./markdown-content-cache.js";
|
|
24
|
+
export { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from "./preview-block-renderer.js";
|
|
25
|
+
export {
|
|
26
|
+
BORDER_HORIZONTAL_OVERHEAD,
|
|
27
|
+
BORDER_INNER_PADDING_HORIZONTAL,
|
|
28
|
+
BORDER_VERTICAL_OVERHEAD,
|
|
29
|
+
BOX_MIN_CONTENT_WIDTH,
|
|
30
|
+
renderBorderedBox,
|
|
31
|
+
stripFenceMarkers,
|
|
32
|
+
} from "./preview-box-renderer.js";
|
|
33
|
+
export {
|
|
34
|
+
PREVIEW_COLUMN_GAP,
|
|
35
|
+
PREVIEW_MIN_WIDTH,
|
|
36
|
+
PREVIEW_PADDING_LEFT,
|
|
37
|
+
STACKED_GAP_ROWS,
|
|
38
|
+
} from "./preview-layout-decider.js";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Per-tick projection of PreviewPane state. Replaces the prior
|
|
42
|
+
* `setNotesVisible(boolean)` sliver-setter and the sibling reads of
|
|
43
|
+
* `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads
|
|
44
|
+
* `selectedIndex` and `focused` from its own props — both derived from
|
|
45
|
+
* canonical state via `selectPreviewPaneProps`. `OptionListView` and
|
|
46
|
+
* `PreviewPane` see the same source of truth without the cross-component
|
|
47
|
+
* live read.
|
|
48
|
+
*/
|
|
49
|
+
export interface PreviewPaneProps {
|
|
50
|
+
notesVisible: boolean;
|
|
51
|
+
selectedIndex: number;
|
|
52
|
+
focused: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* True while the "other" (custom-answer) row is focused and accepting input
|
|
55
|
+
* (`state.inputMode`, set by the reducer via `ROW_INTENT_META.other.activatesInputMode`).
|
|
56
|
+
* Surfaced by `selectPreviewPaneProps` from canonical state; drives the full-width
|
|
57
|
+
* early-returns below so the inline input isn't cramped into the narrow left column.
|
|
58
|
+
*/
|
|
59
|
+
inputMode: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PreviewPaneConfig {
|
|
63
|
+
question: QuestionData;
|
|
64
|
+
getTerminalWidth: () => number;
|
|
65
|
+
optionListView: OptionListView;
|
|
66
|
+
previewBlock: PreviewBlockRenderer;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`
|
|
71
|
+
* via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical
|
|
72
|
+
* state). Delegates option-side rendering to `OptionListView` (which still
|
|
73
|
+
* owns its render-time state — input buffer, confirmedIndex) and preview-side
|
|
74
|
+
* rendering to `PreviewBlockRenderer` (which owns the markdown cache and
|
|
75
|
+
* bordered-box composition).
|
|
76
|
+
*
|
|
77
|
+
* `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`
|
|
78
|
+
* combines them via `decideLayout` (mode threaded into both calls — never
|
|
79
|
+
* re-derived).
|
|
80
|
+
*/
|
|
81
|
+
export class PreviewPane implements StatefulView<PreviewPaneProps>, Component {
|
|
82
|
+
private readonly question: QuestionData;
|
|
83
|
+
private readonly getTerminalWidth: () => number;
|
|
84
|
+
private readonly optionListView: OptionListView;
|
|
85
|
+
private readonly previewBlock: PreviewBlockRenderer;
|
|
86
|
+
private props: PreviewPaneProps;
|
|
87
|
+
/**
|
|
88
|
+
* Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`
|
|
89
|
+
* before any render. Initialized to a throwing sentinel so missing injection is a hard fail
|
|
90
|
+
* rather than a silent fallback to a magic constant — render is illegal until injected.
|
|
91
|
+
*/
|
|
92
|
+
private globalLeftWidth: (paneWidth: number) => number = () => {
|
|
93
|
+
throw new Error("PreviewPane.setGlobalLeftWidth must be called before render()");
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
constructor(config: PreviewPaneConfig) {
|
|
97
|
+
this.question = config.question;
|
|
98
|
+
this.getTerminalWidth = config.getTerminalWidth;
|
|
99
|
+
this.optionListView = config.optionListView;
|
|
100
|
+
this.previewBlock = config.previewBlock;
|
|
101
|
+
this.props = { notesVisible: false, selectedIndex: 0, focused: false, inputMode: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setGlobalLeftWidth(getter: (paneWidth: number) => number): void {
|
|
105
|
+
this.globalLeftWidth = getter;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getAdaptiveLeft(paneWidth: number): number {
|
|
109
|
+
return this.globalLeftWidth(paneWidth);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setProps(props: PreviewPaneProps): void {
|
|
113
|
+
this.props = props;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
handleInput(_data: string): void {}
|
|
117
|
+
|
|
118
|
+
invalidate(): void {
|
|
119
|
+
this.previewBlock.invalidate();
|
|
120
|
+
this.optionListView.invalidate();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
render(width: number): string[] {
|
|
124
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width);
|
|
125
|
+
// Spec: hide the preview pane entirely when no option carries a `preview`.
|
|
126
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);
|
|
127
|
+
// `inputMode` (typing on the "other" custom-answer row): the preview is irrelevant —
|
|
128
|
+
// the row sits at index `options.length`, out of bounds for any option's preview — so
|
|
129
|
+
// render the option list at the full pane width instead of the cramped left column.
|
|
130
|
+
// Side-by-side + preview block resume verbatim on nav-away (inputMode clears).
|
|
131
|
+
if (this.props.inputMode) return this.optionListView.render(width);
|
|
132
|
+
|
|
133
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
134
|
+
if (mode === "side-by-side") return this.renderSideBySide(width, mode);
|
|
135
|
+
|
|
136
|
+
// Stacked: options + blank gap + preview block.
|
|
137
|
+
return [
|
|
138
|
+
...this.optionListView.render(width),
|
|
139
|
+
...Array(STACKED_GAP_ROWS).fill(""),
|
|
140
|
+
...this.previewBlock.renderBlock(
|
|
141
|
+
width,
|
|
142
|
+
this.props.selectedIndex,
|
|
143
|
+
mode,
|
|
144
|
+
this.props.focused,
|
|
145
|
+
this.props.notesVisible,
|
|
146
|
+
),
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
focusedItemRowRange(width: number): [number, number] {
|
|
151
|
+
if (this.question.multiSelect === true) return this.optionListView.focusedItemRowRange(width);
|
|
152
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.focusedItemRowRange(width);
|
|
153
|
+
// `inputMode`: compute the focused row range against the FULL pane width (not the
|
|
154
|
+
// side-by-side leftWidth), mirroring `render`'s full-width option list.
|
|
155
|
+
if (this.props.inputMode) return this.optionListView.focusedItemRowRange(width);
|
|
156
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
157
|
+
if (mode === "stacked") return this.optionListView.focusedItemRowRange(width);
|
|
158
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
159
|
+
const { leftWidth } = columnWidths(width, adaptiveLeft);
|
|
160
|
+
return this.optionListView.focusedItemRowRange(leftWidth);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
naturalHeight(width: number): number {
|
|
164
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width).length;
|
|
165
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;
|
|
166
|
+
// `inputMode`: height is the full-width option list only (no preview block) — preserves
|
|
167
|
+
// the `naturalHeight === render.length` parity invariant.
|
|
168
|
+
if (this.props.inputMode) return this.optionListView.render(width).length;
|
|
169
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
170
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
171
|
+
const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
|
|
172
|
+
const optionsHeight = this.optionListView.render(optionsWidth).length;
|
|
173
|
+
const previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);
|
|
174
|
+
if (mode === "side-by-side") return Math.max(optionsHeight, previewBlockHeight);
|
|
175
|
+
return optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
maxNaturalHeight(width: number): number {
|
|
179
|
+
if (this.question.multiSelect === true) return this.optionListView.render(width).length;
|
|
180
|
+
if (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;
|
|
181
|
+
// `inputMode`: like naturalHeight — full-width option list only, so the
|
|
182
|
+
// `maxNaturalHeight >= naturalHeight` parity invariant holds (both equal the list height).
|
|
183
|
+
if (this.props.inputMode) return this.optionListView.render(width).length;
|
|
184
|
+
const mode = decideLayout(this.getTerminalWidth(), width);
|
|
185
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
186
|
+
const { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);
|
|
187
|
+
const optionsHeight = this.optionListView.render(optionsWidth).length;
|
|
188
|
+
let maxPreviewBlock = 0;
|
|
189
|
+
for (let i = 0; i < this.question.options.length; i++) {
|
|
190
|
+
const h = this.previewBlock.blockHeight(previewWidth, i, mode);
|
|
191
|
+
if (h > maxPreviewBlock) maxPreviewBlock = h;
|
|
192
|
+
}
|
|
193
|
+
if (mode === "side-by-side") return Math.max(optionsHeight, maxPreviewBlock);
|
|
194
|
+
return optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {
|
|
198
|
+
const adaptiveLeft = this.getAdaptiveLeft(width);
|
|
199
|
+
const { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);
|
|
200
|
+
const leftLines = this.optionListView.render(leftWidth);
|
|
201
|
+
const rightLines = this.renderPaddedPreviewLines(rightWidth, mode);
|
|
202
|
+
const rows = Math.max(leftLines.length, rightLines.length);
|
|
203
|
+
const gapStr = " ".repeat(gap);
|
|
204
|
+
const out: string[] = [];
|
|
205
|
+
for (let i = 0; i < rows; i++) {
|
|
206
|
+
const leftRaw = leftLines[i] ?? "";
|
|
207
|
+
const rightRaw = rightLines[i] ?? "";
|
|
208
|
+
const leftClamped = truncateToWidth(leftRaw, leftWidth, "");
|
|
209
|
+
const leftPad = " ".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));
|
|
210
|
+
const joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;
|
|
211
|
+
out.push(truncateToWidth(joined, width, ""));
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {
|
|
217
|
+
const inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);
|
|
218
|
+
const contentLines = this.previewBlock.renderBlock(
|
|
219
|
+
inner,
|
|
220
|
+
this.props.selectedIndex,
|
|
221
|
+
mode,
|
|
222
|
+
this.props.focused,
|
|
223
|
+
this.props.notesVisible,
|
|
224
|
+
);
|
|
225
|
+
const pad = " ".repeat(PREVIEW_PADDING_LEFT);
|
|
226
|
+
return contentLines.map((l) => (l === "" ? "" : `${pad}${l}`));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/components/submit-picker.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
5
|
+
import { t } from "../../state/i18n-bridge.js";
|
|
6
|
+
import type { StatefulView } from "../stateful-view.js";
|
|
7
|
+
|
|
8
|
+
const ACTIVE_POINTER = "❯ ";
|
|
9
|
+
const INACTIVE_POINTER = " ";
|
|
10
|
+
const NUMBER_SEPARATOR = ". ";
|
|
11
|
+
|
|
12
|
+
export const SUBMIT_LABEL = "Submit answers";
|
|
13
|
+
export const CANCEL_LABEL = "Cancel";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Per-tick projection of SubmitPicker state. The picker is a fixed 2-row
|
|
17
|
+
* structure (Submit / Cancel) — labels are static, only the active marker
|
|
18
|
+
* varies per tick. `selectSubmitPickerProps` precomputes `active` per row.
|
|
19
|
+
*/
|
|
20
|
+
export interface SubmitPickerProps {
|
|
21
|
+
/** Per-row active flag. Length always 2: row 0 = Submit, row 1 = Cancel. */
|
|
22
|
+
rows: ReadonlyArray<{ active: boolean }>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Static 2-row picker rendered on the Submit Tab. Row 0 = "Submit answers", Row 1 = "Cancel".
|
|
27
|
+
*
|
|
28
|
+
* - Active pointer (❯) follows `props.rows[i].active` per tick.
|
|
29
|
+
* - Both rows render in normal style at all times — D1 (revised) allows partial submission,
|
|
30
|
+
* so Submit is never dimmed or visually marked as unselectable. The warning header in
|
|
31
|
+
* `buildSubmitContainer` is the sole signal of incompleteness.
|
|
32
|
+
* - `naturalHeight(width)` is state-INDEPENDENT and returns a constant 2, so the
|
|
33
|
+
* chrome-mirror layout in `buildSubmitContainer` can subtract a fixed 2 lines without
|
|
34
|
+
* re-rendering.
|
|
35
|
+
*/
|
|
36
|
+
export class SubmitPicker implements StatefulView<SubmitPickerProps> {
|
|
37
|
+
private props: SubmitPickerProps;
|
|
38
|
+
|
|
39
|
+
constructor(private readonly theme: Theme) {
|
|
40
|
+
this.props = { rows: [{ active: false }, { active: false }] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setProps(props: SubmitPickerProps): void {
|
|
44
|
+
this.props = props;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
handleInput(_data: string): void {}
|
|
48
|
+
|
|
49
|
+
invalidate(): void {}
|
|
50
|
+
|
|
51
|
+
naturalHeight(_width: number): number {
|
|
52
|
+
return 2;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
render(width: number): string[] {
|
|
56
|
+
const lines: string[] = [];
|
|
57
|
+
for (let i = 0; i < 2; i++) {
|
|
58
|
+
const text = i === 0 ? t("submit.label", SUBMIT_LABEL) : t("submit.cancel", CANCEL_LABEL);
|
|
59
|
+
const active = this.props.rows[i]?.active ?? false;
|
|
60
|
+
const pointer = active ? ACTIVE_POINTER : INACTIVE_POINTER;
|
|
61
|
+
const number = `${i + 1}${NUMBER_SEPARATOR}`;
|
|
62
|
+
const label = active ? this.theme.fg("accent", this.theme.bold(text)) : this.theme.fg("text", text);
|
|
63
|
+
lines.push(truncateToWidth(`${pointer}${number}${label}`, width, ""));
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/components/tab-bar.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
5
|
+
import type { StatefulView } from "../stateful-view.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Per-tick projection of TabBar state. The selector
|
|
9
|
+
* (`selectTabBarProps`) hoists every render-time derivation
|
|
10
|
+
* (`allAnswered`, `answered`, `isActive`, `submitActive`) into props so
|
|
11
|
+
* `render()` is pure styling. Replaces the prior `setConfig(TabBarConfig)`
|
|
12
|
+
* snowflake and the inline `+ 1` magic at `props-adapter.ts:127`.
|
|
13
|
+
*/
|
|
14
|
+
export interface TabBarProps {
|
|
15
|
+
/** One per author-defined question, in order. */
|
|
16
|
+
tabs: ReadonlyArray<{ label: string; answered: boolean; active: boolean }>;
|
|
17
|
+
/** Submit-tab state. `allAnswered` drives the success/dim color picker. */
|
|
18
|
+
submit: { active: boolean; allAnswered: boolean };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class TabBar implements StatefulView<TabBarProps> {
|
|
22
|
+
private props: TabBarProps;
|
|
23
|
+
|
|
24
|
+
constructor(private readonly theme: Theme) {
|
|
25
|
+
this.props = { tabs: [], submit: { active: false, allAnswered: false } };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setProps(props: TabBarProps): void {
|
|
29
|
+
this.props = props;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
handleInput(_data: string): void {}
|
|
33
|
+
|
|
34
|
+
invalidate(): void {}
|
|
35
|
+
|
|
36
|
+
render(width: number): string[] {
|
|
37
|
+
const pieces: string[] = [" ← "];
|
|
38
|
+
|
|
39
|
+
for (const tab of this.props.tabs) {
|
|
40
|
+
const box = tab.answered ? "■" : "□";
|
|
41
|
+
const rawSeg = ` ${box} ${tab.label} `;
|
|
42
|
+
const styled = tab.active
|
|
43
|
+
? this.theme.bg("selectedBg", this.theme.fg("text", rawSeg))
|
|
44
|
+
: this.theme.fg(tab.answered ? "success" : "muted", rawSeg);
|
|
45
|
+
pieces.push(styled);
|
|
46
|
+
pieces.push(" ");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const submitText = " ✓ Submit ";
|
|
50
|
+
const submitStyled = this.props.submit.active
|
|
51
|
+
? this.theme.bg("selectedBg", this.theme.fg("text", submitText))
|
|
52
|
+
: this.theme.fg(this.props.submit.allAnswered ? "success" : "dim", submitText);
|
|
53
|
+
pieces.push(submitStyled);
|
|
54
|
+
pieces.push(" →");
|
|
55
|
+
|
|
56
|
+
const tabLine = truncateToWidth(pieces.join(""), width, "");
|
|
57
|
+
return [tabLine, ""];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/components/wrapping-select.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Component } from "@earendil-works/pi-tui";
|
|
4
|
+
import { visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
5
|
+
import { renderInlineInputRow } from "./inline-input.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Row-intent discriminated union. `kind` is the single discriminator —
|
|
9
|
+
* pre-1.0.3 boolean flags have been removed (see `banned-flags.test.ts`).
|
|
10
|
+
* Modeled after `QuestionnaireAction` (`key-router.ts:13-32`) and `Effect`
|
|
11
|
+
* (`state-reducer.ts:26-32`) — pure literal-tagged variants, no shared base,
|
|
12
|
+
* exhaustive-`switch` enforcement via non-`void` returns.
|
|
13
|
+
*
|
|
14
|
+
* Variant semantics:
|
|
15
|
+
* - `option`: a regular author-defined option row.
|
|
16
|
+
* - `other`: the inline free-text input row appended to every question
|
|
17
|
+
* (label is "Type something."). Renders as inline `Input` when active.
|
|
18
|
+
* - `next`: the explicit commit-and-advance row appended to multi-select questions
|
|
19
|
+
* (label is "Next"). Renders without a number / checkbox.
|
|
20
|
+
*/
|
|
21
|
+
export type WrappingSelectItem =
|
|
22
|
+
| { kind: "option"; label: string; description?: string }
|
|
23
|
+
| { kind: "other"; label: string; description?: string }
|
|
24
|
+
| { kind: "next"; label: string; description?: string };
|
|
25
|
+
|
|
26
|
+
export interface WrappingSelectTheme {
|
|
27
|
+
selectedText: (text: string) => string;
|
|
28
|
+
description: (text: string) => string;
|
|
29
|
+
scrollInfo: (text: string) => string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Numbering controls.
|
|
34
|
+
*
|
|
35
|
+
* Use `numberStartOffset` + `totalItemsForNumbering` when a list is logically a slice of a
|
|
36
|
+
* larger numbered sequence — e.g. to start numbering at an offset and pad the column as
|
|
37
|
+
* if the list were part of a longer continuous numbered sequence.
|
|
38
|
+
*/
|
|
39
|
+
export interface WrappingSelectOptions {
|
|
40
|
+
/** Start numbering at this offset + 1 (default 0 → rows labeled 1, 2, 3 …). */
|
|
41
|
+
numberStartOffset?: number;
|
|
42
|
+
/** Override the total used to pad the number column (useful when items span multiple lists). */
|
|
43
|
+
totalItemsForNumbering?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class WrappingSelect implements Component {
|
|
47
|
+
private static readonly ACTIVE_POINTER = "❯ ";
|
|
48
|
+
private static readonly INACTIVE_POINTER = " ";
|
|
49
|
+
private static readonly NUMBER_SEPARATOR = ". ";
|
|
50
|
+
private static readonly CONFIRMED_MARK = " ✔";
|
|
51
|
+
private static readonly MIN_CONTENT_WIDTH = 1;
|
|
52
|
+
|
|
53
|
+
private readonly items: readonly WrappingSelectItem[];
|
|
54
|
+
private readonly maxVisible: number;
|
|
55
|
+
private readonly theme: WrappingSelectTheme;
|
|
56
|
+
private numberStartOffset: number;
|
|
57
|
+
private totalItemsForNumbering: number;
|
|
58
|
+
|
|
59
|
+
private selectedIndex = 0;
|
|
60
|
+
private focused = true;
|
|
61
|
+
private inputBuffer = "";
|
|
62
|
+
private inputCursorOffset: number | undefined = undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Index of the row that was previously confirmed for this list (e.g. the user's prior
|
|
65
|
+
* answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row
|
|
66
|
+
* styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When
|
|
67
|
+
* `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).
|
|
68
|
+
*/
|
|
69
|
+
private confirmedIndex: number | undefined = undefined;
|
|
70
|
+
/**
|
|
71
|
+
* When set together with `confirmedIndex`, replaces the row's static label at render time.
|
|
72
|
+
* Used for the `kind: "other"` sentinel — its label is "Type something." but if the user's
|
|
73
|
+
* prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).
|
|
74
|
+
*/
|
|
75
|
+
private confirmedLabelOverride: string | undefined = undefined;
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
items: readonly WrappingSelectItem[],
|
|
79
|
+
maxVisible: number,
|
|
80
|
+
theme: WrappingSelectTheme,
|
|
81
|
+
options: WrappingSelectOptions = {},
|
|
82
|
+
) {
|
|
83
|
+
this.items = items;
|
|
84
|
+
this.maxVisible = Math.max(1, maxVisible);
|
|
85
|
+
this.theme = theme;
|
|
86
|
+
this.numberStartOffset = options.numberStartOffset ?? 0;
|
|
87
|
+
this.totalItemsForNumbering = options.totalItemsForNumbering ?? items.length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Update the numbering offset + total padding width without rebuilding the component.
|
|
92
|
+
* Lets the host realign the number column when the underlying item set changes.
|
|
93
|
+
*/
|
|
94
|
+
setNumbering(numberStartOffset: number, totalItemsForNumbering: number): void {
|
|
95
|
+
this.numberStartOffset = numberStartOffset;
|
|
96
|
+
this.totalItemsForNumbering = Math.max(1, totalItemsForNumbering);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setSelectedIndex(index: number): void {
|
|
100
|
+
this.selectedIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setFocused(focused: boolean): void {
|
|
104
|
+
this.focused = focused;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Mark a previously-confirmed row. Pass `undefined` to clear. `labelOverride` replaces
|
|
109
|
+
* the row's static `item.label` at render time — used for the `kind: "other"` sentinel so
|
|
110
|
+
* the row reads `Hello ✔` instead of `Type something. ✔` when the prior answer was custom
|
|
111
|
+
* text.
|
|
112
|
+
*/
|
|
113
|
+
setConfirmedIndex(index: number | undefined, labelOverride?: string): void {
|
|
114
|
+
if (index === undefined) {
|
|
115
|
+
this.confirmedIndex = undefined;
|
|
116
|
+
this.confirmedLabelOverride = undefined;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.confirmedIndex = Math.max(0, Math.min(index, this.items.length - 1));
|
|
120
|
+
this.confirmedLabelOverride = labelOverride;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setInputBuffer(text: string): void {
|
|
124
|
+
this.inputBuffer = text;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Set the cursor offset for the inline input row. `undefined` → end-of-buffer fallback. */
|
|
128
|
+
setInputCursorOffset(offset: number | undefined): void {
|
|
129
|
+
this.inputCursorOffset = offset;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Intentionally empty — input is routed at the container level. */
|
|
133
|
+
handleInput(_data: string): void {}
|
|
134
|
+
|
|
135
|
+
invalidate(): void {}
|
|
136
|
+
|
|
137
|
+
render(width: number): string[] {
|
|
138
|
+
if (this.items.length === 0) return [];
|
|
139
|
+
|
|
140
|
+
const { startIndex, endIndex } = this.computeVisibleWindow();
|
|
141
|
+
const numberWidth = String(Math.max(1, this.totalItemsForNumbering)).length;
|
|
142
|
+
const lines: string[] = [];
|
|
143
|
+
|
|
144
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
145
|
+
const item = this.items[i];
|
|
146
|
+
if (!item) continue;
|
|
147
|
+
const isActive = i === this.selectedIndex && this.focused;
|
|
148
|
+
lines.push(...this.renderItem(item, i, isActive, width, numberWidth));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.hasItemsOutsideWindow(startIndex, endIndex)) {
|
|
152
|
+
lines.push(this.theme.scrollInfo(` (${this.selectedIndex + 1}/${this.items.length})`));
|
|
153
|
+
}
|
|
154
|
+
return lines;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns the [startRow, endRow) range of the focused (selected) item within
|
|
159
|
+
* the output of `render(width)`. Computed by iterating the visible window and
|
|
160
|
+
* summing per-item row counts — O(maxVisible) per call.
|
|
161
|
+
*/
|
|
162
|
+
focusedItemRowRange(width: number): [number, number] {
|
|
163
|
+
if (this.items.length === 0) return [0, 0];
|
|
164
|
+
const { startIndex, endIndex } = this.computeVisibleWindow();
|
|
165
|
+
const numberWidth = String(Math.max(1, this.totalItemsForNumbering)).length;
|
|
166
|
+
let row = 0;
|
|
167
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
168
|
+
const item = this.items[i];
|
|
169
|
+
if (!item) continue;
|
|
170
|
+
const isActive = i === this.selectedIndex && this.focused;
|
|
171
|
+
const itemRowCount = this.computeItemRowCount(item, i, isActive, width, numberWidth);
|
|
172
|
+
if (i === this.selectedIndex) {
|
|
173
|
+
return [row, row + itemRowCount];
|
|
174
|
+
}
|
|
175
|
+
row += itemRowCount;
|
|
176
|
+
}
|
|
177
|
+
return [0, 1];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Per-item row count. Delegates to `renderItem().length` so `renderItem` remains
|
|
182
|
+
* the single source of truth for per-item row math — eliminates the prior shadow-copy
|
|
183
|
+
* that risked silent miscounts when new `kind` values branch in `renderItem` but not here.
|
|
184
|
+
*/
|
|
185
|
+
private computeItemRowCount(
|
|
186
|
+
item: WrappingSelectItem,
|
|
187
|
+
index: number,
|
|
188
|
+
isActive: boolean,
|
|
189
|
+
width: number,
|
|
190
|
+
numberWidth: number,
|
|
191
|
+
): number {
|
|
192
|
+
return this.renderItem(item, index, isActive, width, numberWidth).length;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private computeVisibleWindow(): { startIndex: number; endIndex: number } {
|
|
196
|
+
const half = Math.floor(this.maxVisible / 2);
|
|
197
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - half, this.items.length - this.maxVisible));
|
|
198
|
+
const endIndex = Math.min(startIndex + this.maxVisible, this.items.length);
|
|
199
|
+
return { startIndex, endIndex };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private hasItemsOutsideWindow(startIndex: number, endIndex: number): boolean {
|
|
203
|
+
return startIndex > 0 || endIndex < this.items.length;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private renderItem(
|
|
207
|
+
item: WrappingSelectItem,
|
|
208
|
+
index: number,
|
|
209
|
+
isActive: boolean,
|
|
210
|
+
width: number,
|
|
211
|
+
numberWidth: number,
|
|
212
|
+
): string[] {
|
|
213
|
+
const rowPrefix = this.buildRowPrefix(index, isActive, numberWidth);
|
|
214
|
+
const continuationPrefix = " ".repeat(visibleWidth(rowPrefix));
|
|
215
|
+
const contentWidth = Math.max(WrappingSelect.MIN_CONTENT_WIDTH, width - visibleWidth(rowPrefix));
|
|
216
|
+
|
|
217
|
+
if (this.shouldRenderAsInlineInput(item, isActive)) {
|
|
218
|
+
return this.renderInlineInputRow(rowPrefix, continuationPrefix, contentWidth);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Confirmed row gets a trailing ` ✔` and accent+bold styling; pointer is independent
|
|
222
|
+
// (still ❯ when active). When `index === confirmedIndex` AND `isActive`, both `❯` and
|
|
223
|
+
// `✔` appear on the same row — load-bearing for the case where the prior answer was
|
|
224
|
+
// row 0 (cursor resets to 0 on tab-back, so the confirmed row IS the active row).
|
|
225
|
+
// Optional `confirmedLabelOverride` replaces the static label (used for `kind: "other"`
|
|
226
|
+
// + `kind: "custom"` answer); the inline-input branch above still wins for `kind: "other" + isActive`.
|
|
227
|
+
const isConfirmed = index === this.confirmedIndex;
|
|
228
|
+
const label = isConfirmed
|
|
229
|
+
? `${this.confirmedLabelOverride ?? item.label}${WrappingSelect.CONFIRMED_MARK}`
|
|
230
|
+
: item.label;
|
|
231
|
+
const applySelectedStyle = isActive || isConfirmed;
|
|
232
|
+
|
|
233
|
+
return [
|
|
234
|
+
...this.renderLabelBlock(label, rowPrefix, continuationPrefix, contentWidth, applySelectedStyle),
|
|
235
|
+
...this.renderDescriptionBlock(item.description, continuationPrefix, contentWidth),
|
|
236
|
+
];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private buildRowPrefix(index: number, isActive: boolean, numberWidth: number): string {
|
|
240
|
+
const pointer = isActive ? WrappingSelect.ACTIVE_POINTER : WrappingSelect.INACTIVE_POINTER;
|
|
241
|
+
const displayNumber = this.numberStartOffset + index + 1;
|
|
242
|
+
const paddedNumber = String(displayNumber).padStart(numberWidth, " ");
|
|
243
|
+
return `${pointer}${paddedNumber}${WrappingSelect.NUMBER_SEPARATOR}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private shouldRenderAsInlineInput(item: WrappingSelectItem, isActive: boolean): boolean {
|
|
247
|
+
return item.kind === "other" && isActive;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Render the inline input row across one or more lines, wrapping at `contentWidth`.
|
|
252
|
+
* Delegates to the shared `renderInlineInputRow` helper (`./inline-input.ts`) so the
|
|
253
|
+
* single-select wrap path and the multi-select single-line path share one cursor-
|
|
254
|
+
* building core. The `multiline: true` path is byte-identical to the pre-extraction
|
|
255
|
+
* output (cursor rationale lives in `inline-input.ts`).
|
|
256
|
+
*/
|
|
257
|
+
private renderInlineInputRow(rowPrefix: string, continuationPrefix: string, contentWidth: number): string[] {
|
|
258
|
+
return renderInlineInputRow({
|
|
259
|
+
buffer: this.inputBuffer,
|
|
260
|
+
cursorOffset: this.inputCursorOffset,
|
|
261
|
+
rowPrefix,
|
|
262
|
+
continuationPrefix,
|
|
263
|
+
contentWidth,
|
|
264
|
+
selectedText: this.theme.selectedText,
|
|
265
|
+
multiline: true,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private renderLabelBlock(
|
|
270
|
+
label: string,
|
|
271
|
+
rowPrefix: string,
|
|
272
|
+
continuationPrefix: string,
|
|
273
|
+
contentWidth: number,
|
|
274
|
+
applySelectedStyle: boolean,
|
|
275
|
+
): string[] {
|
|
276
|
+
const wrapped = wrapTextWithAnsi(label, contentWidth);
|
|
277
|
+
return wrapped.map((segment, index) => {
|
|
278
|
+
const prefix = index === 0 ? rowPrefix : continuationPrefix;
|
|
279
|
+
const line = `${prefix}${segment}`;
|
|
280
|
+
return applySelectedStyle ? this.theme.selectedText(line) : line;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private renderDescriptionBlock(
|
|
285
|
+
description: string | undefined,
|
|
286
|
+
continuationPrefix: string,
|
|
287
|
+
contentWidth: number,
|
|
288
|
+
): string[] {
|
|
289
|
+
if (!description) return [];
|
|
290
|
+
const wrapped = wrapTextWithAnsi(description, contentWidth);
|
|
291
|
+
return wrapped.map((segment) => `${continuationPrefix}${this.theme.description(segment)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|